Compare commits
244 Commits
Author | SHA1 | Date | |
---|---|---|---|
167e518b70 | |||
3203edf906 | |||
5735f54b30 | |||
64a5989b71 | |||
cc71c128cf | |||
7ee68a4ae8 | |||
|
f43de7bf71 | ||
|
c2fc8f61dd | ||
|
27322532e0 | ||
|
c6fe82c7e6 | ||
|
0dd0d5db67 | ||
54e2d88db3 | |||
|
0921835d27 | ||
81c36c6fb1 | |||
735423ca67 | |||
|
76183883da | ||
|
2463edd996 | ||
3a22950790 | |||
|
c6cfc42774 | ||
|
15bdc45892 | ||
|
449162856b | ||
|
8b5ef2e597 | ||
|
903fd14a61 | ||
|
9809df4b27 | ||
|
9a5e8be378 | ||
|
f144f29386 | ||
|
efe1ff69c9 | ||
|
f9ffa1cf10 | ||
3d66efe8f4 | |||
|
39b4ffb633 | ||
|
f485e7a639 | ||
|
cf3d38a2f5 | ||
|
13baeda0a0 | ||
|
d1504e7bf0 | ||
6c1ef600a8 | |||
2af5007698 | |||
|
c50b1b640d | ||
dcdb4610f8 | |||
a165321c39 | |||
|
fa600673a6 | ||
f8517f0ead | |||
|
0de305b397 | ||
|
15c015a9a5 | ||
eae1b8d6df | |||
|
270f5a9772 | ||
|
30c932f178 | ||
98310f427b | |||
5e70ad158e | |||
|
06a4331135 | ||
4044b7f4c2 | |||
016ce0e8bb | |||
|
272274a5c4 | ||
85d5781122 | |||
3da710891e | |||
d706a42b1b | |||
f1efd5a2f2 | |||
2d38a7bd63 | |||
844560ae1e | |||
5fa5345981 | |||
|
58ebf3842a | ||
|
6a0a47083f | ||
|
bedb2bf413 | ||
|
961b9dc5ff | ||
fc6b51cc24 | |||
5433ae43d6 | |||
afaf9d64c7 | |||
|
8a34935d0f | ||
72ca826be2 | |||
7e38a912d4 | |||
|
ff67c84f18 | ||
ccaf53ec67 | |||
|
2fa2e90d57 | ||
|
037cf2f1be | ||
aa04a9fbf4 | |||
8a6de531c8 | |||
4bdd91c18b | |||
|
b8dc207a0e | ||
|
1a69bee090 | ||
|
a0cf17c845 | ||
|
0fe48de228 | ||
|
0947fd9b73 | ||
731aea8c8d | |||
|
1166d7ffe5 | ||
6ed9aec51a | |||
fe49cd4665 | |||
|
df0c9f77c8 | ||
ff7abeb414 | |||
d3e9041760 | |||
d610a15fdb | |||
|
6211f61ecd | ||
2a380347c6 | |||
|
815b181a0c | ||
|
8aebff96a5 | ||
38b1a79633 | |||
0018492f8d | |||
1a114a9c44 | |||
|
cbd2c4a0ab | ||
807bce391c | |||
|
1b3f130e4e | ||
|
081581140e | ||
|
bec948357a | ||
bff5c61cf4 | |||
1afa01bffb | |||
|
a808f8f73e | ||
e2c537d381 | |||
|
35920fbfeb | ||
f1e406a678 | |||
518c3d3f6e | |||
|
8a6b5a0145 | ||
|
a8c2a61106 | ||
|
63032efdba | ||
|
3bf7a146af | ||
|
cdfffc65e8 | ||
|
650488e1c7 | ||
|
c22ddfcae9 | ||
|
ed4dc9a224 | ||
|
31180597a5 | ||
c23c70ce05 | |||
|
a58a4350c2 | ||
|
e7b7ff6781 | ||
497bbad8ed | |||
0a25ee3170 | |||
a83952f7b4 | |||
cac309f4b1 | |||
|
61e60e4e64 | ||
|
099cb89bbb | ||
|
5627ca5315 | ||
|
c04c0afba5 | ||
|
fc1c773298 | ||
|
3a7795c875 | ||
|
70da3d6dfd | ||
|
550218e9e0 | ||
|
3dcafa50e8 | ||
|
d529c283f2 | ||
|
bc6e0369a1 | ||
|
5df2b1c810 | ||
8bb725f234 | |||
c919c59b49 | |||
91e63a9763 | |||
|
4b9e398f67 | ||
|
088740408e | ||
|
6b5932c617 | ||
5d232cf4e8 | |||
9fbc60e93a | |||
42f299f553 | |||
|
a4b761f720 | ||
|
fca6610047 | ||
fd23150725 | |||
c700a78613 | |||
78c55b3b21 | |||
|
8b9c8a6156 | ||
|
736052740a | ||
|
968a46fbe7 | ||
|
e7f6f8e74d | ||
|
531950f272 | ||
|
2569265a1f | ||
|
06f8445a98 | ||
|
8d28ac1d59 | ||
|
11a14b2ef6 | ||
37799cd2c9 | |||
b3270c5921 | |||
59520435d2 | |||
149678b3f0 | |||
509d06e39e | |||
|
d24717c13a | ||
|
a6119b890f | ||
|
3bf98d1ddf | ||
|
6fa4c6e6a9 | ||
|
0b25327b28 | ||
|
7ed8fcbed7 | ||
|
8136167d8e | ||
|
a298e11da2 | ||
|
4f8eab5a7a | ||
|
398d1f1658 | ||
4ccd5a3cab | |||
|
5da5e324e4 | ||
|
34aa20ca65 | ||
|
7fc152ad67 | ||
3c018ddad3 | |||
|
4b77a87785 | ||
|
39f01066ad | ||
|
4901c00199 | ||
a67b02c389 | |||
|
20f9ac94c9 | ||
|
a13a01f99f | ||
|
4666c84860 | ||
|
29fc4e265b | ||
|
5a7b371005 | ||
|
92d21d559f | ||
|
117ff89562 | ||
|
d45752ddb5 | ||
|
e0b0ae4566 | ||
43022220a0 | |||
|
8405d9619d | ||
|
e011e5d409 | ||
|
225279dbd2 | ||
|
7de95e55f2 | ||
a24ae0a14e | |||
b394b3128c | |||
1facbfeb2b | |||
3ef9f59184 | |||
859561b6a7 | |||
3f91e6cbe7 | |||
|
1d77dd0fec | ||
|
b4b2fc9e72 | ||
|
fddb10af77 | ||
|
edf154afa3 | ||
|
224670fa15 | ||
|
9b58348297 | ||
|
1221cd89d4 | ||
|
dedca1e149 | ||
|
089d62cfc2 | ||
91bb4ecb46 | |||
f12b72cc04 | |||
80b45be543 | |||
49e3199c06 | |||
a0cee6634e | |||
c9dda72725 | |||
9e5304a270 | |||
3ae2f14a16 | |||
32c575125f | |||
6f327f751b | |||
24c0254c01 | |||
c004aa9e57 | |||
ffd989b13b | |||
772de0b017 | |||
3d8f41b309 | |||
237c6b5297 | |||
ef9e431afd | |||
9d6acd9c20 | |||
141cc633b9 | |||
da98777b04 | |||
6f3154ec19 | |||
5856236b12 | |||
4339ea2d73 | |||
cf6db6ef40 | |||
9cee828d0d | |||
3f64881096 | |||
77991dbbdd | |||
f176602179 | |||
b123e8b921 | |||
accbe3c216 | |||
cc576f4840 | |||
ab14155ebd |
@ -1,9 +1,9 @@
|
||||
name: CI
|
||||
name: local-test分支打包构建docker并发布运行
|
||||
run-name: ${{ gitea.actor }} is testing out Gitea Actions
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- develop
|
||||
- local-test
|
||||
|
||||
jobs:
|
||||
Docker-Build:
|
||||
@ -14,18 +14,19 @@ jobs:
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: 设置go环境
|
||||
uses: https://gitea.joylink.club/actions/local-setup-go@v0.1.1
|
||||
uses: https://gitea.joylink.club/actions/local-setup-go@v0.1.2
|
||||
with:
|
||||
go-version: '1.22.1'
|
||||
- name: 构建go build
|
||||
run: |
|
||||
go env -w GO111MODULE=on
|
||||
go env -w GOPROXY=https://goproxy.cn,direct
|
||||
go env -w CGO_ENABLED=0
|
||||
go build -o rts-sim-testing-service
|
||||
- name: 设置 Docker
|
||||
uses: https://gitea.joylink.club/actions/local-setup-docker-cli-action@v0.1.1
|
||||
- name: 设置 Docker Buildx
|
||||
uses: https://gitea.joylink.club/actions/local-setup-buildx-action@v0.1.1
|
||||
with:
|
||||
version: 'v0.13.1'
|
||||
uses: https://gitea.joylink.club/actions/local-setup-buildx-action@v0.1.3
|
||||
- name: docker登录gitea.joylink.club
|
||||
uses: https://gitea.joylink.club/docker/login-action@v3
|
||||
with:
|
||||
@ -39,16 +40,18 @@ jobs:
|
||||
file: ./Dockerfile_Gitea
|
||||
push: true
|
||||
tags: |
|
||||
gitea.joylink.club/joylink/rts-sim-testing-service:0.1.1
|
||||
gitea.joylink.club/joylink/rts-sim-testing-service:latest
|
||||
# - name: SSH Connect and Publish
|
||||
# uses: http://120.46.212.6:3000/appleboy/ssh-action@v1.0.3
|
||||
# with:
|
||||
# host: ${{ secrets.LOCAL_SSH_HOST }}
|
||||
# port: ${{ secrets.LOCAL_SSH_PORT }}
|
||||
# username: ${{ secrets.LOCAL_SSH_USER }}
|
||||
# password: ${{ secrets.LOCAL_SSH_PASSWORD }}
|
||||
# script: |
|
||||
# whoami
|
||||
# sudo docker images
|
||||
- run: echo "This job's status is ${{ job.status }}."
|
||||
gitea.joylink.club/joylink/rts-sim-testing-service:local-test
|
||||
- name: 发布到服务器
|
||||
uses: https://gitea.joylink.club/appleboy/ssh-action@v1.0.3
|
||||
with:
|
||||
host: ${{ secrets.LOCAL_233_SSH_HOST }}
|
||||
port: ${{ secrets.LOCAL_233_SSH_PORT }}
|
||||
username: ${{ secrets.LOCAL_233_SSH_USER }}
|
||||
password: ${{ secrets.LOCAL_233_SSH_PASSWORD }}
|
||||
script: |
|
||||
docker rm -f rts-sim-testing-service || echo "rts-sim-testing-service not exist"
|
||||
docker pull gitea.joylink.club/joylink/rts-sim-testing-service:local-test
|
||||
docker run --name rts-sim-testing-service --restart=always --network net --ip 192.168.53.191 -d -p 9091:9091 -p 4000:4000/udp -v /usr/local/joylink/logs/bjrtsts:/logs/bjrtsts gitea.joylink.club/joylink/rts-sim-testing-service:local-test rts-sim-testing-service -config test_local
|
||||
- name: 清理tag为none的镜像
|
||||
run: |
|
||||
docker rmi $(docker images --filter "dangling=true" -q)
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -27,3 +27,4 @@ output/
|
||||
# Go workspace file
|
||||
/go.work.sum
|
||||
/pom.xml
|
||||
/bj-rtsts-server
|
||||
|
6
.gitmodules
vendored
6
.gitmodules
vendored
@ -1,9 +1,9 @@
|
||||
[submodule "rts-sim-testing-message"]
|
||||
path = rts-sim-testing-message
|
||||
url = http://120.46.212.6:3000/joylink/rts-sim-testing-message.git
|
||||
url = https://gitea.joylink.club/joylink/rts-sim-testing-message.git
|
||||
[submodule "rts-sim-module"]
|
||||
path = rts-sim-module
|
||||
url = http://120.46.212.6:3000/joylink/rts-sim-module.git
|
||||
url = https://gitea.joylink.club/joylink/rts-sim-module.git
|
||||
[submodule "jl-iot-module"]
|
||||
path = jl-iot-module
|
||||
url = http://120.46.212.6:3000/joylink/jl-iot.git
|
||||
url = https://gitea.joylink.club/joylink/jl-iot.git
|
||||
|
@ -1,4 +1,4 @@
|
||||
FROM alpine
|
||||
FROM docker.m.daocloud.io/library/alpine:3.20
|
||||
COPY ./rts-sim-testing-service /usr/local/bin/rts-sim-testing-service
|
||||
WORKDIR /bj-rtsts
|
||||
COPY ./config/*.yml ./
|
||||
|
@ -14,9 +14,17 @@
|
||||
- viper 配置管理
|
||||
- swagger 文档生成(基于[swaggo](https://github.com/swaggo/swag))(需要安装 go install github.com/swaggo/swag/cmd/swag@latest,在项目根目录(即 bj-rtsts-server-go)执行 swag init,然后启动项目即可,启动后访问 http://{ip}:{port}/swagger/index.html)
|
||||
|
||||
# 动力学
|
||||
- dotnet.exe publish -r win-x64 -p:PublishSingleFile=true
|
||||
|
||||
# 开发说明
|
||||
|
||||
- 在 api 目录下添加路由及请求数据解析返回
|
||||
- 在 service 目录下添加具体的业务逻辑(数据库查询)
|
||||
- 设置 go 代理 go env -w GOPROXY=https://goproxy.cn,direct
|
||||
- 运行 go mod download / go mod tidy
|
||||
|
||||
# windows 编译linux环境
|
||||
- go env -w GOOS=linux
|
||||
- go build -o appname
|
||||
- go env -w GOOS=windows
|
||||
|
@ -190,7 +190,7 @@ func deleteProjectRunConfig(c *gin.Context) {
|
||||
// @Failure 500 {object} dto.ErrorDto
|
||||
// @Router /api/v1/runconfig/description [get]
|
||||
func getRunCofigDescription(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, parseRunCofigStruct(&config.ThridPartyConfig{}))
|
||||
c.JSON(http.StatusOK, parseRunCofigStruct(&config.ThirdPartyConfig{}))
|
||||
}
|
||||
|
||||
// 解析环境配置结构
|
||||
|
@ -22,10 +22,12 @@ import (
|
||||
)
|
||||
|
||||
func InitSimulationRouter(api *gin.RouterGroup, authMiddleware *jwt.GinJWTMiddleware) {
|
||||
|
||||
authed := api.Group("/v1/simulation").Use(authMiddleware.MiddlewareFunc(), middleware.PermissMiddleware)
|
||||
authed.POST("/createByProject", createByProjectId)
|
||||
authed.POST("/destroy/:id", destroy)
|
||||
authed.GET("/list", findAllSimulations)
|
||||
|
||||
authed.POST("/check/data", checkSimMapData)
|
||||
authed.POST("/train/add", addTrain)
|
||||
authed.POST("/train/config", configTrain)
|
||||
@ -34,6 +36,8 @@ func InitSimulationRouter(api *gin.RouterGroup, authMiddleware *jwt.GinJWTMiddle
|
||||
authed.POST("/train/update", updateTrain)
|
||||
authed.POST("/train/control", controlTrain)
|
||||
authed.POST("/train/conn", trainConnThird)
|
||||
authed.DELETE("/train/unconn/:trainId/:id", trainUnConnThird)
|
||||
|
||||
authed.GET("/train/conn/type/:id", trainConnTypeList)
|
||||
|
||||
authed.POST("/switch/operation", turnoutOperation)
|
||||
@ -47,12 +51,14 @@ func InitSimulationRouter(api *gin.RouterGroup, authMiddleware *jwt.GinJWTMiddle
|
||||
authed.POST("/psl/operation", pslBtnOperation) //1
|
||||
authed.POST("/psd/operation", psdOperation)
|
||||
authed.PUT("/balise/position/modify", balisePositionModify)
|
||||
authed.PUT("/balise/position/reset", transponderPositionReset)
|
||||
authed.PUT("/balise/position/reset", balisePositionReset)
|
||||
authed.PUT("/balise/telegram/modify", baliseTelegramModify)
|
||||
authed.PUT("/balise/telegram/reset", baliseTelegramReset)
|
||||
authed.PUT("/balise/telegram/stop", baliseTelegramStop)
|
||||
authed.PUT("/balise/telegram/send", baliseTelegramSend)
|
||||
authed.PUT("/balise/reset", baliseReset)
|
||||
authed.PUT("/ckm/operation", ckmOperation)
|
||||
authed.PUT("/xcj/operation", xcjOperation)
|
||||
//authed.POST("/bypass/operation", bypassBtnOrKeyOperation)
|
||||
|
||||
// 初始化地图信息
|
||||
@ -92,6 +98,7 @@ func createByProjectId(c *gin.Context) {
|
||||
if len(mapInfos) == 0 {
|
||||
panic(sys_error.New("测试启动失败,项目未关联发布图"))
|
||||
}
|
||||
|
||||
var mapIds []int32
|
||||
for _, mapInfo := range mapInfos {
|
||||
if mapInfo.Type == data_proto.PictureType_value[data_proto.PictureType_TrainData.String()] {
|
||||
@ -235,15 +242,28 @@ func addTrain(c *gin.Context) {
|
||||
if req.TrainId < 0 || req.TrainId >= 255 {
|
||||
panic(sys_error.New("列车编号不能小于0,大于255"))
|
||||
}
|
||||
if req.TrainSpeed < 0 || req.TrainSpeed > 90 {
|
||||
panic(sys_error.New("列车编号不能小于0,大于255"))
|
||||
}
|
||||
rsp := &state_proto.TrainState{
|
||||
Id: fmt.Sprintf("%v", req.TrainId),
|
||||
HeadDeviceId: req.Id,
|
||||
HeadOffset: req.HeadOffset,
|
||||
DevicePort: req.DevicePort,
|
||||
RunDirection: req.RunDirection,
|
||||
Id: fmt.Sprintf("%v", req.TrainId),
|
||||
HeadDeviceId: req.Id,
|
||||
HeadOffset: req.HeadOffset,
|
||||
TrainLoad: req.TrainLoad,
|
||||
TrainMaxSpeed: req.TrainMaxSpeed,
|
||||
TrainMaxAcc: req.TrainMaxAcc,
|
||||
TrainMaxBrake: req.TrainMaxBrake,
|
||||
TrainEmergencyBrake: req.TrainEmergencyBrake,
|
||||
ProjectCode: simulation.ProjectCode,
|
||||
//HeadOffset: 93211,
|
||||
DevicePort: req.DevicePort,
|
||||
TrainRunUp: req.RunDirection,
|
||||
//RunDirection: req.RunDirection,
|
||||
TrainLength: req.TrainLength,
|
||||
WheelDiameter: req.WheelDiameter,
|
||||
Speed: req.TrainSpeed,
|
||||
Show: true,
|
||||
TrainCoachNum: req.TrainCoachNum,
|
||||
ConnState: &state_proto.TrainConnState{TrainControlMapId: req.TrainControlMapId, Conn: false, ConnType: state_proto.TrainConnState_NONE},
|
||||
}
|
||||
var err *sys_error.BusinessError = memory.AddTrainStateNew(simulation,
|
||||
@ -309,6 +329,30 @@ func trainConnThird(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, "ok")
|
||||
}
|
||||
|
||||
// ATS测试仿真-取消列车连接三方
|
||||
//
|
||||
// @Summary ATS测试仿真-取消列车连接三方
|
||||
//
|
||||
// @Security JwtAuth
|
||||
//
|
||||
// @Description ATS测试仿真-取消列车连接三方
|
||||
// @Tags ATS测试仿真Api
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param Authorization header string true "JWT Token"
|
||||
// @Param trainId path string true "列车id"
|
||||
// @Param id path string true "仿真id"
|
||||
// @Success 200 {object} dto.AddTrainRspDto
|
||||
// @Failure 500 {object} dto.ErrorDto
|
||||
// @Router /api/v1/simulation/train/conn [delete]
|
||||
func trainUnConnThird(c *gin.Context) {
|
||||
trainId := c.Param("trainId")
|
||||
simId := c.Param("id")
|
||||
simulation := checkDeviceDataAndReturn(simId)
|
||||
memory.TrainUnConn(simulation, trainId)
|
||||
c.JSON(http.StatusOK, "ok")
|
||||
}
|
||||
|
||||
// ATS测试仿真-列车连接三方类型列表
|
||||
//
|
||||
// @Summary ATS测试仿真-列车连接三方类型列表
|
||||
@ -373,14 +417,13 @@ func controlTrain(c *gin.Context) {
|
||||
// @Failure 500 {object} dto.ErrorDto
|
||||
// @Router /api/v1/simulation/train/remove/all [post]
|
||||
func removeAllTrain(c *gin.Context) {
|
||||
|
||||
rt := &dto.RemoveAllTrainRspDto{}
|
||||
if err := c.ShouldBind(&rt); err != nil {
|
||||
panic(sys_error.New("移除所有列车失败,请求参数异常", err))
|
||||
}
|
||||
slog.Debug("ATS测试仿真-移除所有列车,请求:", rt)
|
||||
simulation := checkDeviceDataAndReturn(rt.SimulationId)
|
||||
memory.RemoveAllTrain(simulation)
|
||||
memory.RemoveAllTrain(simulation, false)
|
||||
//TODO 后续调用列车删除操作
|
||||
c.JSON(http.StatusOK, "ok")
|
||||
}
|
||||
@ -408,7 +451,7 @@ func removeTrain(c *gin.Context) {
|
||||
}
|
||||
slog.Debug("ATS测试仿真-移除列车,请求:", rt)
|
||||
simulation := checkDeviceDataAndReturn(rt.SimulationId)
|
||||
memory.RemoveTrainState(simulation, rt.TrainId)
|
||||
memory.RemoveTrainState(simulation, rt.TrainId, false)
|
||||
//TODO 后续调用列车删除操作
|
||||
c.JSON(http.StatusOK, "ok")
|
||||
}
|
||||
@ -613,7 +656,10 @@ func pslBtnOperation(c *gin.Context) {
|
||||
}
|
||||
simulation := checkDeviceDataAndReturn(req.SimulationId)
|
||||
slog.Info("传入状态参数", req)
|
||||
memory.ChangePSLButtonState(simulation, req.MapId, req.PslId, req.ButtonCode, req.Down)
|
||||
var err *sys_error.BusinessError = memory.ChangePSLButtonState(simulation, req.MapId, req.PslId, req.ButtonCode, req.Down)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
c.JSON(http.StatusOK, "ok")
|
||||
}
|
||||
|
||||
@ -714,7 +760,7 @@ func relayOperation(c *gin.Context) {
|
||||
//
|
||||
// @Success 200 {object} string
|
||||
// @Failure 500 {object} dto.ErrorDto
|
||||
// @Router /api/v1/simulation/balise/position/modify [put]
|
||||
// @Router /api/v1/simulation/balisecodec/position/modify [put]
|
||||
func balisePositionModify(c *gin.Context) {
|
||||
req := &dto.BaliseMoveReqDto{}
|
||||
if err := c.ShouldBind(&req); err != nil {
|
||||
@ -743,8 +789,8 @@ func balisePositionModify(c *gin.Context) {
|
||||
//
|
||||
// @Success 200 {object} string
|
||||
// @Failure 500 {object} dto.ErrorDto
|
||||
// @Router /api/v1/simulation/balise/position/reset [put]
|
||||
func transponderPositionReset(c *gin.Context) {
|
||||
// @Router /api/v1/simulation/balisecodec/position/reset [put]
|
||||
func balisePositionReset(c *gin.Context) {
|
||||
req := &dto.BaliseReqDto{}
|
||||
if err := c.ShouldBind(&req); err != nil {
|
||||
panic(sys_error.New("应答器复位操作失败,请求参数异常", err))
|
||||
@ -772,7 +818,7 @@ func transponderPositionReset(c *gin.Context) {
|
||||
//
|
||||
// @Success 200 {object} string
|
||||
// @Failure 500 {object} dto.ErrorDto
|
||||
// @Router /api/v1/simulation/balise/telegram/modify [put]
|
||||
// @Router /api/v1/simulation/balisecodec/telegram/modify [put]
|
||||
func baliseTelegramModify(c *gin.Context) {
|
||||
req := &dto.BaliseModifyTelegramReqDto{}
|
||||
if err := c.ShouldBind(&req); err != nil {
|
||||
@ -801,7 +847,7 @@ func baliseTelegramModify(c *gin.Context) {
|
||||
//
|
||||
// @Success 200 {object} string
|
||||
// @Failure 500 {object} dto.ErrorDto
|
||||
// @Router /api/v1/simulation/balise/telegram/reset [put]
|
||||
// @Router /api/v1/simulation/balisecodec/telegram/reset [put]
|
||||
func baliseTelegramReset(c *gin.Context) {
|
||||
req := &dto.BaliseReqDto{}
|
||||
if err := c.ShouldBind(&req); err != nil {
|
||||
@ -809,7 +855,7 @@ func baliseTelegramReset(c *gin.Context) {
|
||||
}
|
||||
simulation := checkDeviceDataAndReturn(req.SimulationId)
|
||||
slog.Info("传入状态参数", req)
|
||||
var err *sys_error.BusinessError = memory.BaliseTelegramReset(simulation, req)
|
||||
var err = memory.BaliseTelegramReset(simulation, req)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@ -830,7 +876,7 @@ func baliseTelegramReset(c *gin.Context) {
|
||||
//
|
||||
// @Success 200 {object} string
|
||||
// @Failure 500 {object} dto.ErrorDto
|
||||
// @Router /api/v1/simulation/balise/telegram/stop [put]
|
||||
// @Router /api/v1/simulation/balisecodec/telegram/stop [put]
|
||||
func baliseTelegramStop(c *gin.Context) {
|
||||
req := &dto.BaliseReqDto{}
|
||||
if err := c.ShouldBind(&req); err != nil {
|
||||
@ -859,7 +905,7 @@ func baliseTelegramStop(c *gin.Context) {
|
||||
//
|
||||
// @Success 200 {object} string
|
||||
// @Failure 500 {object} dto.ErrorDto
|
||||
// @Router /api/v1/simulation/balise/telegram/send [put]
|
||||
// @Router /api/v1/simulation/balisecodec/telegram/send [put]
|
||||
func baliseTelegramSend(c *gin.Context) {
|
||||
req := &dto.BaliseReqDto{}
|
||||
if err := c.ShouldBind(&req); err != nil {
|
||||
@ -888,7 +934,7 @@ func baliseTelegramSend(c *gin.Context) {
|
||||
//
|
||||
// @Success 200 {object} string
|
||||
// @Failure 500 {object} dto.ErrorDto
|
||||
// @Router /api/v1/simulation/balise/reset [put]
|
||||
// @Router /api/v1/simulation/balisecodec/reset [put]
|
||||
func baliseReset(c *gin.Context) {
|
||||
//req := &dto.BaliseReqDto{}
|
||||
//if err := c.ShouldBind(&req); err != nil {
|
||||
@ -896,13 +942,69 @@ func baliseReset(c *gin.Context) {
|
||||
//}
|
||||
simulationId := c.Query("simulationId")
|
||||
simulation := checkDeviceDataAndReturn(simulationId)
|
||||
err := memory.BaliseReset(simulation)
|
||||
err := memory.AllBaliseTelegramReset(simulation)
|
||||
if err != nil {
|
||||
panic(sys_error.New(fmt.Sprintf("应答器状态重置操作失败,%s", err.Error()), err))
|
||||
}
|
||||
c.JSON(http.StatusOK, "ok")
|
||||
}
|
||||
|
||||
// 车库门操作
|
||||
//
|
||||
// @Summary 车库门操作
|
||||
//
|
||||
// @Security JwtAuth
|
||||
//
|
||||
// @Description 车库门操作
|
||||
// @Tags ATS测试仿真Api
|
||||
// @Accept json
|
||||
// @Param Authorization header string true "JWT Token"
|
||||
// @Param CkmOperationReq body request_proto.CkmOperationReq true "车库门操作"
|
||||
//
|
||||
// @Success 200 {object} string
|
||||
// @Failure 500 {object} dto.ErrorDto
|
||||
// @Router /api/v1/simulation/ckm/operation [put]
|
||||
func ckmOperation(c *gin.Context) {
|
||||
req := &request_proto.CkmOperationReq{}
|
||||
if err := c.ShouldBind(&req); err != nil {
|
||||
panic(dto.ErrorDto{Code: dto.ArgumentParseError, Message: err.Error()})
|
||||
}
|
||||
simulation := checkDeviceDataAndReturn(req.SimulationId)
|
||||
var err *sys_error.BusinessError = memory.CkmOperation(simulation, req)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
c.JSON(http.StatusOK, "ok")
|
||||
}
|
||||
|
||||
// 洗车机操作
|
||||
//
|
||||
// @Summary 洗车机操作
|
||||
//
|
||||
// @Security JwtAuth
|
||||
//
|
||||
// @Description 洗车机操作
|
||||
// @Tags ATS测试仿真Api
|
||||
// @Accept json
|
||||
// @Param Authorization header string true "JWT Token"
|
||||
// @Param XcjOperationReq body request_proto.XcjOperationReq true "洗车机操作"
|
||||
//
|
||||
// @Success 200 {object} string
|
||||
// @Failure 500 {object} dto.ErrorDto
|
||||
// @Router /api/v1/simulation/xcj/operation [put]
|
||||
func xcjOperation(c *gin.Context) {
|
||||
req := &request_proto.XcjOperationReq{}
|
||||
if err := c.ShouldBind(&req); err != nil {
|
||||
panic(dto.ErrorDto{Code: dto.ArgumentParseError, Message: err.Error()})
|
||||
}
|
||||
simulation := checkDeviceDataAndReturn(req.SimulationId)
|
||||
var err *sys_error.BusinessError = memory.XcjOperation(simulation, req)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
c.JSON(http.StatusOK, "ok")
|
||||
}
|
||||
|
||||
// 获取仿真设备数据并返回
|
||||
func checkDeviceDataAndReturn(simId string) *memory.VerifySimulation {
|
||||
deviceMemory := ts.FindSimulation(simId)
|
||||
|
@ -21,6 +21,7 @@ func InitUserRouter(api *gin.RouterGroup, authMiddleware *jwt.GinJWTMiddleware)
|
||||
authed := api.Group("/v1/user").Use(authMiddleware.MiddlewareFunc(), middleware.PermissMiddleware)
|
||||
authed.GET("/paging", pageQueryUser)
|
||||
authed.GET("/current", findUserInfo)
|
||||
|
||||
}
|
||||
|
||||
// 用户注册
|
||||
|
309
bin/acc_conn-example.go
Normal file
309
bin/acc_conn-example.go
Normal file
@ -0,0 +1,309 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"github.com/spf13/viper"
|
||||
"joylink.club/bj-rtsts-server/bin/config"
|
||||
"joylink.club/bj-rtsts-server/const/balise_const"
|
||||
"joylink.club/bj-rtsts-server/third_party/message"
|
||||
"joylink.club/bj-rtsts-server/third_party/tcp"
|
||||
"joylink.club/bj-rtsts-server/third_party/udp"
|
||||
"log/slog"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
const config_name = "example"
|
||||
|
||||
var exampleConfig config.ExampleConfig
|
||||
|
||||
func initConfig() {
|
||||
cnf := viper.New()
|
||||
cnf.SetConfigName(config_name)
|
||||
cnf.SetConfigType("yml")
|
||||
//cnf.AddConfigPath("./bin/config/")
|
||||
//cnf.AddConfigPath("./")
|
||||
cnf.AddConfigPath(".")
|
||||
err := cnf.ReadInConfig()
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("读取配置文件错误: %w", err))
|
||||
}
|
||||
err = cnf.Unmarshal(&exampleConfig)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("解析配置文件错误: %w", err))
|
||||
}
|
||||
slog.Info("成功加载配置", "config", exampleConfig)
|
||||
}
|
||||
|
||||
var btmCli udp.UdpClient
|
||||
|
||||
func initBtmTest() {
|
||||
ser := udp.NewServer(fmt.Sprintf("%v:%d", exampleConfig.Btm.LocalIp, exampleConfig.Btm.LocalPort), handleBtmVobcFrames)
|
||||
ser.Listen()
|
||||
btmCli = udp.NewClient(fmt.Sprintf("%v:%v", exampleConfig.Btm.RemoteIp, exampleConfig.Btm.RemotePort))
|
||||
}
|
||||
|
||||
var accClient udp.UdpClient
|
||||
|
||||
func initAccTest() {
|
||||
accClient = udp.NewClient(fmt.Sprintf("%v:%v", exampleConfig.Acc.RemoteIp, exampleConfig.Acc.RemotePort))
|
||||
go testAcc(accClient, 0.54)
|
||||
}
|
||||
|
||||
var speedClient udp.UdpClient
|
||||
|
||||
func initSpeedTest() {
|
||||
speedClient = udp.NewClient(fmt.Sprintf("%v:%v", exampleConfig.Speed.RemoteIp, exampleConfig.Speed.RemotePort))
|
||||
go testSpeed(speedClient, 30)
|
||||
}
|
||||
|
||||
var trainpcClient *tcp.TcpClient
|
||||
|
||||
func initTrainPc() {
|
||||
addr := fmt.Sprintf("%v:%v", exampleConfig.Trainpc.RemoteIp, exampleConfig.Trainpc.RemotePort)
|
||||
|
||||
client2, err := tcp.StartTcpClient(addr, trainPcDataHandle, trainPcConnErr)
|
||||
if err != nil {
|
||||
fmt.Println("仿真列车pc连接失败", err)
|
||||
return
|
||||
}
|
||||
trainpcClient = client2
|
||||
time.Sleep(time.Millisecond * 1500)
|
||||
createOrRemoveTrain()
|
||||
circleSendTrainActive()
|
||||
circleSendTrainMockData()
|
||||
//go circleSendTrainSpeedPlace()
|
||||
//sendBtm()
|
||||
}
|
||||
|
||||
func createOrRemoveTrain() {
|
||||
msgs := make([]*message.TrainPcSimBaseMessage, 0)
|
||||
//msgs = append(msgs, &message.TrainPcSimBaseMessage{Data: []byte{0x00}, Type: message.RECIVE_TRAIN_CREATE_REMOVE})
|
||||
msgs = append(msgs, &message.TrainPcSimBaseMessage{Data: []byte{0x01}, Type: message.RECIVE_TRAIN_CREATE_REMOVE})
|
||||
for _, msg := range msgs {
|
||||
data := msg.Encode()
|
||||
fmt.Println(fmt.Sprintf("发送列车模拟数据:%v,命令码:%v", hex.EncodeToString(data), msg.Data[0]))
|
||||
trainpcClient.Send(data)
|
||||
time.Sleep(time.Millisecond * 1000)
|
||||
}
|
||||
}
|
||||
func circleSendTrainActive() {
|
||||
msgs := make([]*message.TrainPcSimBaseMessage, 0)
|
||||
msgs = append(msgs, &message.TrainPcSimBaseMessage{Data: []byte{}, Type: message.SENDER_TRAIN_TC_ACTIVE})
|
||||
//msgs = append(msgs, &message.TrainPcSimBaseMessage{Data: []byte{}, Type: message.SENDER_TRAIN_HAND_KEY_FORWARD})
|
||||
|
||||
for _, msg := range msgs {
|
||||
|
||||
actData := msg.Encode()
|
||||
fmt.Println(fmt.Sprintf("发送列车驾驶室激活:%v", hex.EncodeToString(actData)))
|
||||
trainpcClient.Send(actData)
|
||||
time.Sleep(time.Millisecond * 1000)
|
||||
}
|
||||
}
|
||||
|
||||
func circleSendTrainMockData() {
|
||||
msgs := make([]*message.TrainPcSimBaseMessage, 0)
|
||||
msgs = append(msgs, &message.TrainPcSimBaseMessage{Type: message.SENDER_TRAIN_OUTR_INFO, Data: []byte{byte(0), 1}})
|
||||
//msgs = append(msgs, &message.TrainPcSimBaseMessage{Type: message.SENDER_TRAIN_OUTR_INFO, Data: []byte{byte(16), 1}})
|
||||
msgs = append(msgs, &message.TrainPcSimBaseMessage{Type: message.SENDER_TRAIN_OUTR_INFO, Data: []byte{byte(3), 0}})
|
||||
//msgs = append(msgs, &message.TrainPcSimBaseMessage{Type: message.SENDER_TRAIN_OUTR_INFO, Data: []byte{byte(3), 0}})
|
||||
msgs = append(msgs, &message.TrainPcSimBaseMessage{Type: message.SENDER_TRAIN_OUTR_INFO, Data: []byte{byte(16), 1}})
|
||||
msgs = append(msgs, &message.TrainPcSimBaseMessage{Type: message.SENDER_TRAIN_OUTR_INFO, Data: []byte{byte(45), 1}})
|
||||
msgs = append(msgs, &message.TrainPcSimBaseMessage{Type: message.SENDER_TRAIN_OUTR_INFO, Data: []byte{byte(46), 1}})
|
||||
/* for i := 3; i <= 50; i++ {
|
||||
if i != 2 {
|
||||
msgs = append(msgs, &message.TrainPcSimBaseMessage{Type: message.SENDER_TRAIN_OUTR_INFO, Data: []byte{byte(i), 0}})
|
||||
}
|
||||
}*/
|
||||
//msgs = append(msgs, &message.TrainPcSimBaseMessage{Type: message.SENDER_TRAIN_OUTR_INFO, Data: []byte{14, 0}})
|
||||
//msgs = append(msgs, &message.TrainPcSimBaseMessage{Type: message.SENDER_TRAIN_OUTR_INFO, Data: []byte{1, 1}})
|
||||
/*msgs = append(msgs, &message.TrainPcSimBaseMessage{Type: message.SENDER_TRAIN_OUTR_INFO, Data: []byte{1, 1}})
|
||||
msgs = append(msgs, &message.TrainPcSimBaseMessage{Type: message.SENDER_TRAIN_OUTR_INFO, Data: []byte{2, 0}})
|
||||
msgs = append(msgs, &message.TrainPcSimBaseMessage{Type: message.SENDER_TRAIN_OUTR_INFO, Data: []byte{12, 1}})
|
||||
msgs = append(msgs, &message.TrainPcSimBaseMessage{Type: message.SENDER_TRAIN_OUTR_INFO, Data: []byte{16, 1}})
|
||||
msgs = append(msgs, &message.TrainPcSimBaseMessage{Type: message.SENDER_TRAIN_OUTR_INFO, Data: []byte{20, 1}})
|
||||
msgs = append(msgs, &message.TrainPcSimBaseMessage{Type: message.SENDER_TRAIN_OUTR_INFO, Data: []byte{3, 1}})
|
||||
msgs = append(msgs, &message.TrainPcSimBaseMessage{Type: message.SENDER_TRAIN_OUTR_INFO, Data: []byte{4, 1}})
|
||||
msgs = append(msgs, &message.TrainPcSimBaseMessage{Type: message.SENDER_TRAIN_OUTR_INFO, Data: []byte{6, 1}})
|
||||
msgs = append(msgs, &message.TrainPcSimBaseMessage{Type: message.SENDER_TRAIN_OUTR_INFO, Data: []byte{7, 1}})
|
||||
msgs = append(msgs, &message.TrainPcSimBaseMessage{Type: message.SENDER_TRAIN_OUTR_INFO, Data: []byte{30, 1}})
|
||||
msgs = append(msgs, &message.TrainPcSimBaseMessage{Type: message.SENDER_TRAIN_OUTR_INFO, Data: []byte{22, 0}})
|
||||
msgs = append(msgs, &message.TrainPcSimBaseMessage{Type: message.SENDER_TRAIN_OUTR_INFO, Data: []byte{23, 0}})*/
|
||||
//msgs = append(msgs, &message.TrainPcSimBaseMessage{Type: message.SENDER_TRAIN_OUTR_INFO, Data: []byte{3, 0}})
|
||||
|
||||
for _, msg := range msgs {
|
||||
data := msg.Encode()
|
||||
fmt.Println(fmt.Sprintf("发送列车模拟数据:%v,命令码:%v", hex.EncodeToString(data), msg.Data[0]))
|
||||
trainpcClient.Send(data)
|
||||
time.Sleep(time.Millisecond * 1000)
|
||||
}
|
||||
}
|
||||
|
||||
func trainPcDataHandle(n int, data []byte) {
|
||||
hexData := hex.EncodeToString(data[:n])
|
||||
slog.Info(fmt.Sprintf("列车pc仿真接口长度:%v,实际长度:%v,接受数据:%v", len(data), n, hexData))
|
||||
|
||||
}
|
||||
func trainPcConnErr(err error) {
|
||||
|
||||
}
|
||||
func main() {
|
||||
|
||||
initConfig()
|
||||
initBtmTest()
|
||||
//initTrainPc()
|
||||
//initAccTest()
|
||||
//initSpeedTest()
|
||||
|
||||
for {
|
||||
}
|
||||
}
|
||||
|
||||
var testUserBtmMsg = "90007F8181B60B10183280003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
|
||||
var freeBtmMsg = strings.Repeat("00", balise_const.UserTelegramByteLen)
|
||||
|
||||
func sendPacket(lifeNum uint32, autoId byte) {
|
||||
userMsg, _ := hex.DecodeString(testUserBtmMsg)
|
||||
|
||||
msg := &message.BtmVobcMessage{FontTtl: 2, BtmStatus: 0x00, DecodeTime: 10, BackTtl: 2, BtmMsg: userMsg, ResponseTime: 10,
|
||||
VobcLifeNum: lifeNum, BaseBtmVobc: message.BaseBtmVobc{AutoIdFrame: autoId} /*MsgSerial: message.GetAutoMessageId()*/}
|
||||
sendData := msg.Encode()
|
||||
//fmt.Println("发送btm vobc len:", len(sendData), "报文:", hex.EncodeToString(sendData), "报文序列号:", msg.MsgSerial)
|
||||
time.Sleep(time.Millisecond * 100)
|
||||
err := btmCli.Send(sendData)
|
||||
if err != nil {
|
||||
fmt.Println(fmt.Sprintf("发送失败%v", err.Error()))
|
||||
}
|
||||
|
||||
/*freeMsg, _ := hex.DecodeString(freeBtmMsg)
|
||||
|
||||
msg2 := &message.BtmVobcMsgFree{BtmStatus: 0x00, WorkTemperature: 10, Fun1: uint16(0), Fun2: uint16(0), Fun3: uint16(0), Fun4: uint16(0),
|
||||
FreeMsg: freeMsg, RespTime: 20, VobcLifeNum: lifeNum, BaseBtmVobc: message.BaseBtmVobc{AutoIdFrame: autoId}, MsgSerial: message.GetAutoMessageId()}
|
||||
sendData2 := msg2.Encode()
|
||||
fmt.Println("发送btm vobc 空报文11111:", hex.EncodeToString(sendData2), "len:", len(sendData2), "报文序列号:", msg2.MsgSerial, "atoId=", autoId, "报文序列号:", msg2.MsgSerial)
|
||||
btmCli.Send(sendData2)*/
|
||||
}
|
||||
|
||||
func sendPacketFree(lifeNum uint32, autoId byte, msgs byte) {
|
||||
freeMsg, _ := hex.DecodeString(freeBtmMsg)
|
||||
newMsg := msgs + 1
|
||||
if newMsg > 255 {
|
||||
newMsg = 1
|
||||
}
|
||||
msg2 := &message.BtmVobcMsgFree{BtmStatus: 0x00, WorkTemperature: 10, Fun1: uint16(0), Fun2: uint16(0), Fun3: uint16(0), Fun4: uint16(0),
|
||||
//FreeMsg: freeMsg, RespTime: 20, VobcLifeNum: lifeNum, BaseBtmVobc: message.BaseBtmVobc{AutoIdFrame: autoId}, MsgSerial: newMsg}
|
||||
FreeMsg: freeMsg, RespTime: 20, VobcLifeNum: lifeNum, BaseBtmVobc: message.BaseBtmVobc{AutoIdFrame: autoId} /* MsgSerial: message.GetAutoMessageId()*/}
|
||||
sendData2 := msg2.Encode()
|
||||
//fmt.Println("发送btm vobc 空报文:", hex.EncodeToString(sendData2), "len:", len(sendData2), "报文序列号:", msg2.MsgSerial, "atoId=", autoId, "报文序列号:", msg2.MsgSerial)
|
||||
time.Sleep(time.Millisecond * 100)
|
||||
err := btmCli.Send(sendData2)
|
||||
if err != nil {
|
||||
fmt.Println(fmt.Sprintf("发送失败%v", err.Error()))
|
||||
}
|
||||
}
|
||||
|
||||
func RequestFramePackets(req *message.BtmVobcReq, vobcLife uint32) {
|
||||
//fmt.Println(fmt.Sprintf("接受 请求帧 frameStatus:%v,messageType:%v,lifeNum:%v,序列号:%v", req.FrameStatus, req.MessageType, req.VobcLifeNum, req.MessageSerial))
|
||||
if req.FrameStatus == message.REQ_FRAME_STATUS_BOOT && req.MessageType == message.REQ_PACKETS_TYPE_BOOT {
|
||||
//fmt.Println("000000000000000000000000000")
|
||||
sendPacketFree(vobcLife, req.AutoIdFrame, req.MessageSerial)
|
||||
} else if req.FrameStatus == message.REQ_FRAME_STATUS_OK {
|
||||
//帧正确,删除之前发送的数据
|
||||
//fmt.Println("11111111111111111111")
|
||||
|
||||
} else if req.FrameStatus == message.REQ_FRAME_STATUS_ERROR {
|
||||
//帧不正确 重新发送2次,如果2次后仍然不正确,则删除之前发送的数据
|
||||
///**/fmt.Println("22222222222222222")
|
||||
}
|
||||
}
|
||||
|
||||
var btmReceiveTime = time.Now().UnixMilli()
|
||||
var vobcNumLife uint32 = 0
|
||||
|
||||
func handleBtmVobcFrames(cfs []byte) {
|
||||
|
||||
fmt.Println(fmt.Sprintf("收到源数据:%v ,数据长度:%v", hex.EncodeToString(cfs), len(cfs)))
|
||||
frameType, dataText, err := message.BtmVobcDecode(cfs)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if frameType == message.COMMAND_TYPE {
|
||||
idCommand := &message.BtmVobcIdCommand{}
|
||||
idCommand.Decode(dataText)
|
||||
if vobcNumLife <= 0 {
|
||||
vobcNumLife = idCommand.VobcLifeNum
|
||||
}
|
||||
sendPacketFree(vobcNumLife, idCommand.AutoIdFrame, idCommand.AutoIdFrame)
|
||||
//sendPacket(vobcNumLife, idCommand.AutoIdFrame)
|
||||
} else if frameType == message.REQUEST_TYPE {
|
||||
if vobcNumLife <= 0 {
|
||||
return
|
||||
} else {
|
||||
//fmt.Println(fmt.Sprintf("准备发送vobcLife:%v", vobcNumLife))
|
||||
}
|
||||
req := &message.BtmVobcReq{}
|
||||
req.Decode(dataText)
|
||||
//fmt.Println(fmt.Sprintf("接受 请求帧 frameStatus:%v,messageType:%v,lifeNum:%v,序列号:%v", req.FrameStatus, req.MessageType, req.VobcLifeNum, req.MessageSerial))
|
||||
if time.Now().UnixMilli()-btmReceiveTime > 20*1000 {
|
||||
idCommand := &message.BtmVobcIdCommand{}
|
||||
idCommand.Decode(dataText)
|
||||
sendPacket(vobcNumLife, idCommand.AutoIdFrame)
|
||||
} else {
|
||||
|
||||
RequestFramePackets(req, vobcNumLife)
|
||||
}
|
||||
} else {
|
||||
slog.Error(fmt.Sprintf("btm vobc 解析未知命令帧类型:0x%v,原始数据:%v,长度:%v", strconv.FormatInt(int64(frameType), 16), hex.EncodeToString(cfs), len(cfs)))
|
||||
return
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func testSpeed(client udp.UdpClient, speed float32) {
|
||||
|
||||
//client = udp.NewClient(fmt.Sprintf("%v:%v", exampleConfig.Speed.RemoteIp, exampleConfig.Speed.RemotePort))
|
||||
ac := message.ElectricMachinery{Speed: speed, WheelDiameter: 800, IsBack: false}
|
||||
var index int64
|
||||
for {
|
||||
data := ac.Encode()
|
||||
//fmt.Println(hex.EncodeToString(data))
|
||||
err := client.Send(data)
|
||||
index++
|
||||
if index%10 == 0 {
|
||||
slog.Info(fmt.Sprintf("发送speed数据,时间戳:%v,发送次数:%v 数据:%v", time.Now().Unix(), index, hex.EncodeToString(data)))
|
||||
ac = message.ElectricMachinery{Speed: 0, WheelDiameter: 800, IsBack: false}
|
||||
client.Send(ac.Encode())
|
||||
break
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
slog.Error("发送数据失败", "err", err)
|
||||
}
|
||||
time.Sleep(time.Millisecond * 200)
|
||||
}
|
||||
}
|
||||
|
||||
// 加速度计
|
||||
func testAcc(client udp.UdpClient, acc float32) {
|
||||
|
||||
//client = udp.NewClient(fmt.Sprintf("%v:%v", exampleConfig.Acc.RemoteIp, exampleConfig.Acc.RemotePort))
|
||||
ac := message.Accelerometer{Acc: acc}
|
||||
var index int64
|
||||
for {
|
||||
data := ac.Encode()
|
||||
//fmt.Println(hex.EncodeToString(data))
|
||||
err := client.Send(data)
|
||||
index++
|
||||
if index%10 == 0 {
|
||||
slog.Info(fmt.Sprintf("发送acc数据,时间戳:%v,发送次数:%v 数据:%v", time.Now().Unix(), index, hex.EncodeToString(data)))
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
slog.Error("发送数据失败", "err", err)
|
||||
}
|
||||
time.Sleep(time.Millisecond * 11)
|
||||
}
|
||||
|
||||
}
|
15
bin/config/example.yml
Normal file
15
bin/config/example.yml
Normal file
@ -0,0 +1,15 @@
|
||||
acc:
|
||||
remoteIp: "192.168.1.201"
|
||||
remotePort: 4196
|
||||
speed:
|
||||
remoteIp: "192.168.1.200"
|
||||
remotePort: 4196
|
||||
btm:
|
||||
remoteIp: "192.168.1.199"
|
||||
remotePort: 4196
|
||||
localIp: "192.168.1.150"
|
||||
localPort: 4646
|
||||
trainpc:
|
||||
remoteIp: "192.168.3.211"
|
||||
remotePort: 10007
|
||||
|
27
bin/config/example_config.go
Normal file
27
bin/config/example_config.go
Normal file
@ -0,0 +1,27 @@
|
||||
package config
|
||||
|
||||
type ExampleConfig struct {
|
||||
Acc acc
|
||||
Speed speed
|
||||
Btm btm
|
||||
Trainpc trainpc
|
||||
}
|
||||
type acc struct {
|
||||
RemoteIp string
|
||||
RemotePort int
|
||||
}
|
||||
type speed struct {
|
||||
RemoteIp string
|
||||
RemotePort int
|
||||
}
|
||||
type btm struct {
|
||||
RemoteIp string
|
||||
RemotePort int
|
||||
LocalIp string
|
||||
LocalPort int
|
||||
}
|
||||
|
||||
type trainpc struct {
|
||||
RemoteIp string
|
||||
RemotePort int
|
||||
}
|
@ -15,7 +15,7 @@ logging:
|
||||
# 格式化
|
||||
# format: json
|
||||
# 日志文件路径
|
||||
path: /logs/bjrtsts
|
||||
path: /home/joylink/logs/bjrtsts
|
||||
# 日志文件名
|
||||
fileName: bjrtsts.log
|
||||
# 单个日志文件大小,单位mb,超过会自动滚动
|
||||
|
37
config/bj_local_pxf_2.yml
Normal file
37
config/bj_local_pxf_2.yml
Normal file
@ -0,0 +1,37 @@
|
||||
# 服务配置
|
||||
server:
|
||||
# 服务端口
|
||||
port: 9092
|
||||
|
||||
# 数据源
|
||||
datasource:
|
||||
# 数据库访问url
|
||||
dsn: root:Joylink@0503@tcp(172.29.5.167:3306)/bj-rtsts?charset=utf8mb4&parseTime=true&loc=UTC
|
||||
|
||||
# 日志配置
|
||||
logging:
|
||||
# 日志级别
|
||||
level: info
|
||||
# 格式化
|
||||
# format: json
|
||||
# 日志文件路径
|
||||
path: /home/joylink/logs/bjrtsts2
|
||||
# 日志文件名
|
||||
fileName: bjrtsts.log
|
||||
# 单个日志文件大小,单位mb,超过会自动滚动
|
||||
fileMaxSize: 50
|
||||
# 日志文件最大备份数量
|
||||
fileMaxBackups: 100
|
||||
# 日志文件最大保留时间,单位 天
|
||||
maxAge: 30
|
||||
# 是否压缩日志
|
||||
compress: false
|
||||
# 控制台是否输出
|
||||
stdout: false
|
||||
|
||||
# 消息配置
|
||||
messaging:
|
||||
mqtt:
|
||||
address: tcp://172.29.5.168:1883
|
||||
username: rtsts_service
|
||||
password: joylink@0503
|
128
config/config.go
128
config/config.go
@ -3,10 +3,9 @@ package config
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
|
||||
"github.com/spf13/viper"
|
||||
"joylink.club/iot/dto"
|
||||
"log/slog"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -60,18 +59,21 @@ type mqtt struct {
|
||||
// }
|
||||
|
||||
// 第三方配置结构
|
||||
type ThridPartyConfig struct {
|
||||
Id int32 `json:"id"`
|
||||
Dynamics DynamicsConfig `json:"dynamics" description:"动力学配置"`
|
||||
Vobc VobcConfig `json:"vobc" description:"半实物配置"`
|
||||
Interlocks []InterlockConfig `json:"interlock" description:"联锁配置"`
|
||||
RsspAxleCfgs []RsspAxleConfig `json:"rsspAxleCfgs" description:"所有联锁集中站计轴RSSP-I配置"`
|
||||
ElectricMachinery ElectricMachineryConfig `json:"electricMachinery" description:"电机配置"`
|
||||
BtmCanet BtmCanetConfig `json:"btmCanet" description:"BTM关联的网关设备CANET配置"`
|
||||
CidcModbus []CidcModbusConfig `json:"cidcModbus" description:"联锁驱采Modbus接口配置"`
|
||||
Radar RadarConfig `json:"radar" description:"车载雷达相关配置"`
|
||||
Acc AccConfig `json:"acc" description:"车载加速计"`
|
||||
PcSimConfig VehiclePCSimConfig `json:"pcSimConfig" description:"车载pc仿真平台通信"`
|
||||
type ThirdPartyConfig struct {
|
||||
Id int32 `json:"id"`
|
||||
Dynamics DynamicsConfig `json:"dynamics" description:"动力学配置"`
|
||||
Vobc VobcConfig `json:"vobc" description:"半实物配置"`
|
||||
Interlocks []InterlockConfig `json:"interlock" description:"联锁配置"`
|
||||
RsspAxleConfig RsspAxleConfig `json:"rsspAxleCfgs" description:"计轴通信配置"`
|
||||
//ElectricMachinery ElectricMachineryConfig `json:"electricMachinery" description:"电机配置"`
|
||||
ElectricMachinerys []ElectricMachineryConfig `json:"electricMachinerys" description:"电机配置"`
|
||||
BtmCanet BtmCanetConfig `json:"btmCanet" description:"11号线工装配置"` //废字段再利用
|
||||
CidcModbus []CidcModbusConfig `json:"cidcModbus" description:"联锁驱采Modbus接口配置"`
|
||||
Radar RadarConfig `json:"radar" description:"车载雷达相关配置"`
|
||||
Acc AccConfig `json:"acc" description:"车载加速计"`
|
||||
//PcSimConfig VehiclePCSimConfig `json:"pcSimConfig" description:"车载pc仿真平台通信"`
|
||||
PcSimConfigs []VehiclePCSimConfig `json:"pcSimConfigs" description:"车载pc仿真平台通信"`
|
||||
BtmVobc BtmVobcConfig `json:"btmVobc" description:"Btm Vobc 11号线相关配置"`
|
||||
}
|
||||
type RadarConfig struct {
|
||||
Open bool `json:"open" description:"是否开启"`
|
||||
@ -118,16 +120,28 @@ type VobcConfig struct {
|
||||
Open bool `json:"open" description:"是否开启"`
|
||||
}
|
||||
type InterlockConfig struct {
|
||||
Open bool `json:"open" description:"是否开启"`
|
||||
Ip string `json:"ip" description:"IP配置"`
|
||||
LocalPort int `json:"localPort" description:"本机监听接收端口"`
|
||||
RemotePort int `json:"remotePort" description:"远端接收采集信息端口"`
|
||||
Open bool `json:"open" description:"是否开启"`
|
||||
Code string `json:"code" description:"所属集中站"`
|
||||
Line string `json:"line" description:"联锁通信协议所属线路"`
|
||||
Period int `json:"period" description:"采集任务执行周期(ms)"`
|
||||
}
|
||||
type ElectricMachineryConfig struct {
|
||||
Ip string `json:"ip" description:"IP配置"`
|
||||
RemotePort int `json:"remotePort" description:"远端接收信息端口"`
|
||||
Open bool `json:"open" description:"是否开启"`
|
||||
EndPointA bool `json:"endPointA" description:"一号电机"`
|
||||
}
|
||||
|
||||
// BtmVobcConfig 11 号线 BTM vobc 网关设备配置
|
||||
type BtmVobcConfig struct {
|
||||
LocalUdpIp string `json:"LocalUdpIp" description:"本机监听接收UDP ip 配置"`
|
||||
LocalUdpPort int `json:"localUdpPort" description:"本机监听接收UDP端口"`
|
||||
RemoteIp string `json:"remoteIp" description:"btm串口设备IP配置"`
|
||||
RemoteUdpPort int `json:"remoteUdpPort" description:"btm串口设备UDP端口"`
|
||||
Open bool `json:"open" description:"是否开启"`
|
||||
}
|
||||
|
||||
// BtmCanetConfig BTM CANET网关设备配置
|
||||
@ -138,47 +152,61 @@ type BtmCanetConfig struct {
|
||||
Open bool `json:"open" description:"是否开启"`
|
||||
}
|
||||
|
||||
// RsspAxleConfig 计轴区段与联锁安全通信配置
|
||||
type RsspAxleConfig struct {
|
||||
Open bool `json:"open" description:"是否开启"`
|
||||
City string `json:"city" description:"所属城市"`
|
||||
LineId string `json:"lineId" description:"所属线路"`
|
||||
CentralizedStation string `json:"centralizedStation" description:"所属集中站"`
|
||||
RsspCfg RsspConfig `json:"rsspCfg" description:"安全通道配置"`
|
||||
}
|
||||
|
||||
// RsspConfig CI系统与计轴设备的安全通信协议配置参数
|
||||
// 计轴设备(管理一个集中站的所有计轴器)配置
|
||||
type RsspConfig struct {
|
||||
SrcAddr uint16 `json:"srcAddr" description:"16位源地址,本地地址"` //16位源地址,本地地址
|
||||
DstAddr uint16 `json:"dstAddr" description:"16位目的地址,远程地址"` //16位目的地址,远程地址
|
||||
DataVer1 uint32 `json:"dataVer1" description:"通道1数据版本"` //通道1数据版本
|
||||
DataVer2 uint32 `json:"dataVer2" description:"通道2数据版本"` //通道2数据版本
|
||||
SID1 uint32 `json:"sID1" description:"通道1源标识"` //通道1源标识
|
||||
SID2 uint32 `json:"sID2" description:"通道2源标识"` //通道2源标识
|
||||
SINIT1 uint32 `json:"sINIT1" description:"通道1序列初始"` //通道1序列初始
|
||||
SINIT2 uint32 `json:"sINIT2" description:"通道2序列初始"` //通道2序列初始
|
||||
SendingPeriod uint32 `json:"sendingPeriod" description:"发送周期值"` //接收方每个安全通信会话对应的发送周期值,单位ms
|
||||
SsrRsspTimeout uint32 `json:"ssrRsspTimeout" description:"等待SSR回应的定时器超时值"` //等待SSR回应的定时器超时值,为RsspTimer时间,1=SendingPeriod
|
||||
Mtv uint32 `json:"mtv" description:"最大时序偏差"` //每个安全通信会话可容忍的最大时序偏差,即当前接收的RSD的序列号与上一次RSD的序列号最大允许差值
|
||||
Udl uint32 `json:"udl" description:"RSD应用数据长度配置值"` //每个安全通信会话RSD应用数据长度发送和接收的配置值(支持固定长度和可变长度);0-可变长度,大于0即固定长度
|
||||
DeviceA bool `json:"deviceA" description:"true-A机;false-B机"` //true-A机;false-B机
|
||||
PicType byte `json:"picType" description:"协议交互类别"` //协议交互类别,message.PicType
|
||||
RemoteIp string `json:"remoteIp" description:"远程服务器ip"` //远程服务器ip
|
||||
RemoteUdpPort int `json:"remoteUdpPort" description:"远程服务器端口"` //远程服务器端口
|
||||
LocalUdpPort int `json:"localUdpPort" description:"本地服务器端口"` //本地服务器端口
|
||||
}
|
||||
|
||||
type VehiclePCSimConfig struct {
|
||||
type VehiclePCSimConfig2 struct {
|
||||
TrainEnds bool `json:"trainEnds" description:"列车端点A"`
|
||||
Open bool `json:"open" description:"是否开启"`
|
||||
PcSimIp string `json:"pcSimIp" description:"pc仿真平台通信ip"`
|
||||
PcSimPort uint32 `json:"pcSimPort" description:"pc仿真平台通信端口"`
|
||||
LocalTestingPort uint32 `json:"localTestingPort" description:"本地测试端口"`
|
||||
}
|
||||
|
||||
// CheckAddress 检测目标源地址目的地址是否在配置中
|
||||
func (c *RsspConfig) CheckAddress(srcAddr uint16, dstAddr uint16) bool {
|
||||
return true
|
||||
type VehiclePCSimConfig struct {
|
||||
Open bool `json:"open" description:"是否开启"`
|
||||
//TrainEnds bool `json:"trainEnds" description:"列车端点A?"`
|
||||
ConfigName string `json:"configName" description:"连接名称"`
|
||||
OpenA bool `json:"openA" description:"是否开启A端"`
|
||||
APcSimIp string `json:"apcSimIp" description:"pc仿真平台通信ip A端"`
|
||||
APcSimPort uint32 `json:"apcSimPort" description:"pc仿真平台通信端口A端"`
|
||||
OpenB bool `json:"openB" description:"是否开启B端"`
|
||||
BPcSimIp string `json:"bpcSimIp" description:"pc仿真平台通信ip B端"`
|
||||
BPcSimPort uint32 `json:"bpcSimPort" description:"pc仿真平台通信端口B端"`
|
||||
//LocalTestingPort uint32 `json:"localTestingPort" description:"本地测试端口"`
|
||||
}
|
||||
|
||||
// RsspAxleConfig Rssp计轴通信配置
|
||||
type RsspAxleConfig struct {
|
||||
Open bool `json:"open" description:"开启"`
|
||||
StationCode string `json:"stationCode" description:"集中站编号"`
|
||||
NetAConfig RsspNetConfig `json:"netAConfig" description:"A网配置"`
|
||||
}
|
||||
|
||||
// RsspNetConfig 计轴通信配置
|
||||
type RsspNetConfig struct {
|
||||
RemoteIp string `json:"remoteIp" description:"远端IP"`
|
||||
RemotePort int `json:"remotePort" description:"远端端口"`
|
||||
LocalPort int `json:"localPort" description:"本地端口"`
|
||||
|
||||
RemoteAddr string `json:"sourceAddr" description:"联锁地址(16进制,2字节)"`
|
||||
LocalAddr string `json:"targetAddr" description:"计轴地址(16进制,2字节)"`
|
||||
|
||||
RemoteSid1 string `json:"remoteSid1" description:"联锁SID_1(16进制,4字节)"`
|
||||
RemoteSid2 string `json:"remoteSid2" description:"联锁SID_2(16进制,4字节)"`
|
||||
LocalSid1 string `json:"localSid1" description:"计轴SID_1(16进制,4字节)"`
|
||||
LocalSid2 string `json:"localSid2" description:"计轴SID_2(16进制,4字节)"`
|
||||
|
||||
RemoteSinit1 string `json:"remoteSinit1" description:"联锁SINIT_1(16进制,4字节)"`
|
||||
RemoteSinit2 string `json:"remoteSinit2" description:"联锁SINIT_2(16进制,4字节)"`
|
||||
LocalSinit1 string `json:"localSinit1" description:"计轴SINIT_1(16进制,4字节)"`
|
||||
LocalSinit2 string `json:"localSinit2" description:"计轴SINIT_2(16进制,4字节)"`
|
||||
|
||||
RemoteDataVer1 string `json:"remoteDataVer1" description:"联锁DATAVER_1(16进制,4字节)"`
|
||||
RemoteDataVer2 string `json:"remoteDataVer2" description:"联锁DATAVER_2(16进制,4字节)"`
|
||||
LocalDataVer1 string `json:"localDataVer1" description:"计轴DATAVER_1(16进制,4字节)"`
|
||||
LocalDataVer2 string `json:"localDataVer2" description:"计轴DATAVER_2(16进制,4字节)"`
|
||||
|
||||
MaxDeviation int `json:"maxDeviation" description:"可容忍的最大时序偏差"`
|
||||
WaitSSRTimeout int `json:"waitSSRTimeout" description:"等待SSR回应的定时器超时值(ms)"`
|
||||
Period int `json:"period" description:"RSD发送周期(ms)"`
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -32,6 +32,6 @@ logging:
|
||||
# 消息配置
|
||||
messaging:
|
||||
mqtt:
|
||||
address: tcp://192.168.0.203:1883
|
||||
address: tcp://192.168.1.211:1883
|
||||
username: rtsts_service
|
||||
password: joylink@0503
|
14
docs/docs.go
14
docs/docs.go
@ -3263,7 +3263,7 @@ const docTemplate = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/v1/simulation/balise/position/modify": {
|
||||
"/api/v1/simulation/balisecodec/position/modify": {
|
||||
"put": {
|
||||
"security": [
|
||||
{
|
||||
@ -3312,7 +3312,7 @@ const docTemplate = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/v1/simulation/balise/position/reset": {
|
||||
"/api/v1/simulation/balisecodec/position/reset": {
|
||||
"put": {
|
||||
"security": [
|
||||
{
|
||||
@ -3361,7 +3361,7 @@ const docTemplate = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/v1/simulation/balise/reset": {
|
||||
"/api/v1/simulation/balisecodec/reset": {
|
||||
"put": {
|
||||
"security": [
|
||||
{
|
||||
@ -3408,7 +3408,7 @@ const docTemplate = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/v1/simulation/balise/telegram/modify": {
|
||||
"/api/v1/simulation/balisecodec/telegram/modify": {
|
||||
"put": {
|
||||
"security": [
|
||||
{
|
||||
@ -3457,7 +3457,7 @@ const docTemplate = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/v1/simulation/balise/telegram/reset": {
|
||||
"/api/v1/simulation/balisecodec/telegram/reset": {
|
||||
"put": {
|
||||
"security": [
|
||||
{
|
||||
@ -3506,7 +3506,7 @@ const docTemplate = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/v1/simulation/balise/telegram/send": {
|
||||
"/api/v1/simulation/balisecodec/telegram/send": {
|
||||
"put": {
|
||||
"security": [
|
||||
{
|
||||
@ -3555,7 +3555,7 @@ const docTemplate = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/v1/simulation/balise/telegram/stop": {
|
||||
"/api/v1/simulation/balisecodec/telegram/stop": {
|
||||
"put": {
|
||||
"security": [
|
||||
{
|
||||
|
@ -1,6 +1,6 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.32.0
|
||||
// protoc-gen-go v1.33.0
|
||||
// protoc v4.23.1
|
||||
// source: common_data.proto
|
||||
|
||||
@ -228,7 +228,7 @@ type TrainEndsState struct {
|
||||
SpeedSensorEnableB bool `protobuf:"varint,2,opt,name=speedSensorEnableB,proto3" json:"speedSensorEnableB,omitempty"`
|
||||
// 雷达是否有效
|
||||
RadarEnable bool `protobuf:"varint,3,opt,name=radarEnable,proto3" json:"radarEnable,omitempty"`
|
||||
// 雷达测速差值(米/秒)
|
||||
// 雷达测速差值(千米/小时)
|
||||
RadarCheckSpeedDiff float32 `protobuf:"fixed32,4,opt,name=radarCheckSpeedDiff,proto3" json:"radarCheckSpeedDiff,omitempty"`
|
||||
// 雷达检测时间(秒)
|
||||
RadarCheckTime int32 `protobuf:"varint,5,opt,name=radarCheckTime,proto3" json:"radarCheckTime,omitempty"`
|
||||
@ -238,10 +238,14 @@ type TrainEndsState struct {
|
||||
AccCheckSpeedDiff float32 `protobuf:"fixed32,7,opt,name=accCheckSpeedDiff,proto3" json:"accCheckSpeedDiff,omitempty"`
|
||||
// 加速度持续时间
|
||||
AccCheckTime int32 `protobuf:"varint,8,opt,name=accCheckTime,proto3" json:"accCheckTime,omitempty"`
|
||||
// 速传速度输出(米/秒)
|
||||
// 速传速度输出(千米/小时)
|
||||
AccOutSpeed int32 `protobuf:"varint,9,opt,name=accOutSpeed,proto3" json:"accOutSpeed,omitempty"`
|
||||
// 雷达速度输出(米/秒)
|
||||
// 雷达速度输出(千米/小时)
|
||||
RadarOutSpeed int32 `protobuf:"varint,10,opt,name=radarOutSpeed,proto3" json:"radarOutSpeed,omitempty"`
|
||||
// 记录雷达设置检测时间的时间点,用于计算周期内的数字
|
||||
RadarCheckTimeOverAt int64 `protobuf:"varint,11,opt,name=radarCheckTimeOverAt,proto3" json:"radarCheckTimeOverAt,omitempty"`
|
||||
// 记录加速度计设置检测时间的时间点,用于计算周期内的数字
|
||||
AccCheckTimeOverAt int64 `protobuf:"varint,12,opt,name=accCheckTimeOverAt,proto3" json:"accCheckTimeOverAt,omitempty"`
|
||||
}
|
||||
|
||||
func (x *TrainEndsState) Reset() {
|
||||
@ -346,6 +350,20 @@ func (x *TrainEndsState) GetRadarOutSpeed() int32 {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *TrainEndsState) GetRadarCheckTimeOverAt() int64 {
|
||||
if x != nil {
|
||||
return x.RadarCheckTimeOverAt
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *TrainEndsState) GetAccCheckTimeOverAt() int64 {
|
||||
if x != nil {
|
||||
return x.AccCheckTimeOverAt
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
var File_common_data_proto protoreflect.FileDescriptor
|
||||
|
||||
var file_common_data_proto_rawDesc = []byte{
|
||||
@ -388,8 +406,8 @@ var file_common_data_proto_rawDesc = []byte{
|
||||
0x01, 0x28, 0x05, 0x52, 0x07, 0x69, 0x64, 0x6c, 0x69, 0x6e, 0x67, 0x44, 0x12, 0x1a, 0x0a, 0x08,
|
||||
0x73, 0x74, 0x6f, 0x70, 0x53, 0x69, 0x67, 0x6e, 0x18, 0x11, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08,
|
||||
0x73, 0x74, 0x6f, 0x70, 0x53, 0x69, 0x67, 0x6e, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x6c, 0x69, 0x64,
|
||||
0x65, 0x18, 0x12, 0x20, 0x01, 0x28, 0x02, 0x52, 0x05, 0x73, 0x6c, 0x69, 0x64, 0x65, 0x22, 0xa4,
|
||||
0x03, 0x0a, 0x0e, 0x54, 0x72, 0x61, 0x69, 0x6e, 0x45, 0x6e, 0x64, 0x73, 0x53, 0x74, 0x61, 0x74,
|
||||
0x65, 0x18, 0x12, 0x20, 0x01, 0x28, 0x02, 0x52, 0x05, 0x73, 0x6c, 0x69, 0x64, 0x65, 0x22, 0x88,
|
||||
0x04, 0x0a, 0x0e, 0x54, 0x72, 0x61, 0x69, 0x6e, 0x45, 0x6e, 0x64, 0x73, 0x53, 0x74, 0x61, 0x74,
|
||||
0x65, 0x12, 0x2e, 0x0a, 0x12, 0x73, 0x70, 0x65, 0x65, 0x64, 0x53, 0x65, 0x6e, 0x73, 0x6f, 0x72,
|
||||
0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x41, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x12, 0x73,
|
||||
0x70, 0x65, 0x65, 0x64, 0x53, 0x65, 0x6e, 0x73, 0x6f, 0x72, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65,
|
||||
@ -415,10 +433,17 @@ var file_common_data_proto_rawDesc = []byte{
|
||||
0x28, 0x05, 0x52, 0x0b, 0x61, 0x63, 0x63, 0x4f, 0x75, 0x74, 0x53, 0x70, 0x65, 0x65, 0x64, 0x12,
|
||||
0x24, 0x0a, 0x0d, 0x72, 0x61, 0x64, 0x61, 0x72, 0x4f, 0x75, 0x74, 0x53, 0x70, 0x65, 0x65, 0x64,
|
||||
0x18, 0x0a, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0d, 0x72, 0x61, 0x64, 0x61, 0x72, 0x4f, 0x75, 0x74,
|
||||
0x53, 0x70, 0x65, 0x65, 0x64, 0x42, 0x2f, 0x5a, 0x2d, 0x6a, 0x6f, 0x79, 0x6c, 0x69, 0x6e, 0x6b,
|
||||
0x2e, 0x63, 0x6c, 0x75, 0x62, 0x2f, 0x62, 0x6a, 0x2d, 0x72, 0x74, 0x73, 0x74, 0x73, 0x2d, 0x73,
|
||||
0x65, 0x72, 0x76, 0x65, 0x72, 0x2f, 0x64, 0x74, 0x6f, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e,
|
||||
0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
0x53, 0x70, 0x65, 0x65, 0x64, 0x12, 0x32, 0x0a, 0x14, 0x72, 0x61, 0x64, 0x61, 0x72, 0x43, 0x68,
|
||||
0x65, 0x63, 0x6b, 0x54, 0x69, 0x6d, 0x65, 0x4f, 0x76, 0x65, 0x72, 0x41, 0x74, 0x18, 0x0b, 0x20,
|
||||
0x01, 0x28, 0x03, 0x52, 0x14, 0x72, 0x61, 0x64, 0x61, 0x72, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x54,
|
||||
0x69, 0x6d, 0x65, 0x4f, 0x76, 0x65, 0x72, 0x41, 0x74, 0x12, 0x2e, 0x0a, 0x12, 0x61, 0x63, 0x63,
|
||||
0x43, 0x68, 0x65, 0x63, 0x6b, 0x54, 0x69, 0x6d, 0x65, 0x4f, 0x76, 0x65, 0x72, 0x41, 0x74, 0x18,
|
||||
0x0c, 0x20, 0x01, 0x28, 0x03, 0x52, 0x12, 0x61, 0x63, 0x63, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x54,
|
||||
0x69, 0x6d, 0x65, 0x4f, 0x76, 0x65, 0x72, 0x41, 0x74, 0x42, 0x2f, 0x5a, 0x2d, 0x6a, 0x6f, 0x79,
|
||||
0x6c, 0x69, 0x6e, 0x6b, 0x2e, 0x63, 0x6c, 0x75, 0x62, 0x2f, 0x62, 0x6a, 0x2d, 0x72, 0x74, 0x73,
|
||||
0x74, 0x73, 0x2d, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2f, 0x64, 0x74, 0x6f, 0x2f, 0x63, 0x6f,
|
||||
0x6d, 0x6d, 0x6f, 0x6e, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74,
|
||||
0x6f, 0x33,
|
||||
}
|
||||
|
||||
var (
|
||||
|
@ -1,6 +1,6 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.32.0
|
||||
// protoc-gen-go v1.33.0
|
||||
// protoc v4.23.1
|
||||
// source: ibpGraphics.proto
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.32.0
|
||||
// protoc-gen-go v1.33.0
|
||||
// protoc v4.23.1
|
||||
// source: picture.proto
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.32.0
|
||||
// protoc-gen-go v1.33.0
|
||||
// protoc v4.23.1
|
||||
// source: pslGraphics.proto
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.32.0
|
||||
// protoc-gen-go v1.33.0
|
||||
// protoc v4.23.1
|
||||
// source: relayCabinetLayoutGraphics.proto
|
||||
|
||||
@ -90,8 +90,9 @@ func (Relay_ModelType) EnumDescriptor() ([]byte, []int) {
|
||||
type CjDataItem_PostionType int32
|
||||
|
||||
const (
|
||||
CjDataItem_Q CjDataItem_PostionType = 0
|
||||
CjDataItem_H CjDataItem_PostionType = 1
|
||||
CjDataItem_Q CjDataItem_PostionType = 0
|
||||
CjDataItem_H CjDataItem_PostionType = 1
|
||||
CjDataItem_NONE CjDataItem_PostionType = 2
|
||||
)
|
||||
|
||||
// Enum value maps for CjDataItem_PostionType.
|
||||
@ -99,10 +100,12 @@ var (
|
||||
CjDataItem_PostionType_name = map[int32]string{
|
||||
0: "Q",
|
||||
1: "H",
|
||||
2: "NONE",
|
||||
}
|
||||
CjDataItem_PostionType_value = map[string]int32{
|
||||
"Q": 0,
|
||||
"H": 1,
|
||||
"Q": 0,
|
||||
"H": 1,
|
||||
"NONE": 2,
|
||||
}
|
||||
)
|
||||
|
||||
@ -309,7 +312,9 @@ type Relay struct {
|
||||
Common *CommonInfo `protobuf:"bytes,1,opt,name=common,proto3" json:"common,omitempty"`
|
||||
Code string `protobuf:"bytes,2,opt,name=code,proto3" json:"code,omitempty"` //编号
|
||||
// string model = 3;//型号
|
||||
NewModel Relay_ModelType `protobuf:"varint,4,opt,name=newModel,proto3,enum=relayCabinetGraphicData.Relay_ModelType" json:"newModel,omitempty"` //型号
|
||||
NewModel Relay_ModelType `protobuf:"varint,4,opt,name=newModel,proto3,enum=relayCabinetGraphicData.Relay_ModelType" json:"newModel,omitempty"` //型号
|
||||
ShowCode string `protobuf:"bytes,5,opt,name=showCode,proto3" json:"showCode,omitempty"` //展示的编号
|
||||
DefaultInitialPosition CjDataItem_PostionType `protobuf:"varint,6,opt,name=defaultInitialPosition,proto3,enum=relayCabinetGraphicData.CjDataItem_PostionType" json:"defaultInitialPosition,omitempty"` //默认初始位置
|
||||
}
|
||||
|
||||
func (x *Relay) Reset() {
|
||||
@ -365,6 +370,20 @@ func (x *Relay) GetNewModel() Relay_ModelType {
|
||||
return Relay_Unknown
|
||||
}
|
||||
|
||||
func (x *Relay) GetShowCode() string {
|
||||
if x != nil {
|
||||
return x.ShowCode
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *Relay) GetDefaultInitialPosition() CjDataItem_PostionType {
|
||||
if x != nil {
|
||||
return x.DefaultInitialPosition
|
||||
}
|
||||
return CjDataItem_Q
|
||||
}
|
||||
|
||||
// 断相保护器
|
||||
type PhaseFailureProtector struct {
|
||||
state protoimpl.MessageState
|
||||
@ -549,9 +568,9 @@ type Combinationtype struct {
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Code string `protobuf:"bytes,1,opt,name=code,proto3" json:"code,omitempty"`
|
||||
OldrefRelays []string `protobuf:"bytes,2,rep,name=oldrefRelays,proto3" json:"oldrefRelays,omitempty"` //设备关联的继电器
|
||||
RefRelays []uint32 `protobuf:"varint,3,rep,packed,name=refRelays,proto3" json:"refRelays,omitempty"` //设备关联的继电器
|
||||
Code string `protobuf:"bytes,1,opt,name=code,proto3" json:"code,omitempty"`
|
||||
// repeated string oldrefRelays = 2;//设备关联的继电器
|
||||
RefRelays []uint32 `protobuf:"varint,3,rep,packed,name=refRelays,proto3" json:"refRelays,omitempty"` //设备关联的继电器
|
||||
}
|
||||
|
||||
func (x *Combinationtype) Reset() {
|
||||
@ -593,13 +612,6 @@ func (x *Combinationtype) GetCode() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *Combinationtype) GetOldrefRelays() []string {
|
||||
if x != nil {
|
||||
return x.OldrefRelays
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *Combinationtype) GetRefRelays() []uint32 {
|
||||
if x != nil {
|
||||
return x.RefRelays
|
||||
@ -833,9 +845,9 @@ type CjDataItem struct {
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
OldrelayId string `protobuf:"bytes,1,opt,name=oldrelayId,proto3" json:"oldrelayId,omitempty"` //采集对应的继电器Id
|
||||
Position CjDataItem_PostionType `protobuf:"varint,2,opt,name=position,proto3,enum=relayCabinetGraphicData.CjDataItem_PostionType" json:"position,omitempty"` //继电器的位置,QH对应着吸合
|
||||
RelayId uint32 `protobuf:"varint,3,opt,name=relayId,proto3" json:"relayId,omitempty"` //采集对应的继电器Id
|
||||
// string oldrelayId = 1;//采集对应的继电器Id
|
||||
Position CjDataItem_PostionType `protobuf:"varint,2,opt,name=position,proto3,enum=relayCabinetGraphicData.CjDataItem_PostionType" json:"position,omitempty"` //继电器的位置,QH对应着吸合
|
||||
RelayId uint32 `protobuf:"varint,3,opt,name=relayId,proto3" json:"relayId,omitempty"` //采集对应的继电器Id
|
||||
}
|
||||
|
||||
func (x *CjDataItem) Reset() {
|
||||
@ -870,13 +882,6 @@ func (*CjDataItem) Descriptor() ([]byte, []int) {
|
||||
return file_relayCabinetLayoutGraphics_proto_rawDescGZIP(), []int{11}
|
||||
}
|
||||
|
||||
func (x *CjDataItem) GetOldrelayId() string {
|
||||
if x != nil {
|
||||
return x.OldrelayId
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *CjDataItem) GetPosition() CjDataItem_PostionType {
|
||||
if x != nil {
|
||||
return x.Position
|
||||
@ -1007,8 +1012,8 @@ type QdData struct {
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
OldrefRelays []string `protobuf:"bytes,1,rep,name=oldrefRelays,proto3" json:"oldrefRelays,omitempty"` //驱动的继电器Id
|
||||
RefRelays []uint32 `protobuf:"varint,2,rep,packed,name=refRelays,proto3" json:"refRelays,omitempty"` //驱动的继电器Id
|
||||
// repeated string oldrefRelays=1;//驱动的继电器Id
|
||||
RefRelays []uint32 `protobuf:"varint,2,rep,packed,name=refRelays,proto3" json:"refRelays,omitempty"` //驱动的继电器Id
|
||||
}
|
||||
|
||||
func (x *QdData) Reset() {
|
||||
@ -1043,13 +1048,6 @@ func (*QdData) Descriptor() ([]byte, []int) {
|
||||
return file_relayCabinetLayoutGraphics_proto_rawDescGZIP(), []int{14}
|
||||
}
|
||||
|
||||
func (x *QdData) GetOldrefRelays() []string {
|
||||
if x != nil {
|
||||
return x.OldrefRelays
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *QdData) GetRefRelays() []uint32 {
|
||||
if x != nil {
|
||||
return x.RefRelays
|
||||
@ -1114,7 +1112,7 @@ var file_relayCabinetLayoutGraphics_proto_rawDesc = []byte{
|
||||
0x0b, 0x32, 0x17, 0x2e, 0x67, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x44, 0x61, 0x74, 0x61, 0x2e,
|
||||
0x43, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x06, 0x63, 0x6f, 0x6d, 0x6d,
|
||||
0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09,
|
||||
0x52, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x22, 0xac, 0x02, 0x0a, 0x05, 0x52, 0x65, 0x6c, 0x61, 0x79,
|
||||
0x52, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x22, 0xb1, 0x03, 0x0a, 0x05, 0x52, 0x65, 0x6c, 0x61, 0x79,
|
||||
0x12, 0x2f, 0x0a, 0x06, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b,
|
||||
0x32, 0x17, 0x2e, 0x67, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x44, 0x61, 0x74, 0x61, 0x2e, 0x43,
|
||||
0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x06, 0x63, 0x6f, 0x6d, 0x6d, 0x6f,
|
||||
@ -1123,102 +1121,105 @@ var file_relayCabinetLayoutGraphics_proto_rawDesc = []byte{
|
||||
0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x28, 0x2e, 0x72, 0x65, 0x6c, 0x61, 0x79, 0x43,
|
||||
0x61, 0x62, 0x69, 0x6e, 0x65, 0x74, 0x47, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x44, 0x61, 0x74,
|
||||
0x61, 0x2e, 0x52, 0x65, 0x6c, 0x61, 0x79, 0x2e, 0x4d, 0x6f, 0x64, 0x65, 0x6c, 0x54, 0x79, 0x70,
|
||||
0x65, 0x52, 0x08, 0x6e, 0x65, 0x77, 0x4d, 0x6f, 0x64, 0x65, 0x6c, 0x22, 0x97, 0x01, 0x0a, 0x09,
|
||||
0x4d, 0x6f, 0x64, 0x65, 0x6c, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x6e, 0x6b,
|
||||
0x6e, 0x6f, 0x77, 0x6e, 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x4a, 0x50, 0x58, 0x43, 0x5f, 0x31,
|
||||
0x30, 0x30, 0x30, 0x10, 0x01, 0x12, 0x0d, 0x0a, 0x09, 0x4a, 0x50, 0x58, 0x43, 0x5f, 0x31, 0x37,
|
||||
0x30, 0x30, 0x10, 0x02, 0x12, 0x0d, 0x0a, 0x09, 0x4a, 0x57, 0x4a, 0x58, 0x43, 0x5f, 0x34, 0x38,
|
||||
0x30, 0x10, 0x03, 0x12, 0x11, 0x0a, 0x0d, 0x4a, 0x57, 0x4a, 0x58, 0x43, 0x5f, 0x48, 0x31, 0x32,
|
||||
0x35, 0x5f, 0x38, 0x30, 0x10, 0x04, 0x12, 0x0d, 0x0a, 0x09, 0x4a, 0x57, 0x58, 0x43, 0x5f, 0x31,
|
||||
0x37, 0x30, 0x30, 0x10, 0x05, 0x12, 0x0d, 0x0a, 0x09, 0x4a, 0x57, 0x58, 0x43, 0x5f, 0x48, 0x33,
|
||||
0x34, 0x30, 0x10, 0x06, 0x12, 0x11, 0x0a, 0x0d, 0x4a, 0x59, 0x4a, 0x58, 0x43, 0x5f, 0x31, 0x36,
|
||||
0x30, 0x5f, 0x32, 0x36, 0x30, 0x10, 0x07, 0x12, 0x0c, 0x0a, 0x08, 0x4a, 0x5a, 0x58, 0x43, 0x5f,
|
||||
0x48, 0x31, 0x38, 0x10, 0x08, 0x22, 0x5c, 0x0a, 0x15, 0x50, 0x68, 0x61, 0x73, 0x65, 0x46, 0x61,
|
||||
0x69, 0x6c, 0x75, 0x72, 0x65, 0x50, 0x72, 0x6f, 0x74, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x12, 0x2f,
|
||||
0x0a, 0x06, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17,
|
||||
0x2e, 0x67, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x44, 0x61, 0x74, 0x61, 0x2e, 0x43, 0x6f, 0x6d,
|
||||
0x6d, 0x6f, 0x6e, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x06, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x12,
|
||||
0x12, 0x0a, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x63,
|
||||
0x6f, 0x64, 0x65, 0x22, 0x57, 0x0a, 0x10, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x46, 0x61, 0x75,
|
||||
0x6c, 0x74, 0x41, 0x6c, 0x61, 0x72, 0x6d, 0x12, 0x2f, 0x0a, 0x06, 0x63, 0x6f, 0x6d, 0x6d, 0x6f,
|
||||
0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x72, 0x61, 0x70, 0x68, 0x69,
|
||||
0x63, 0x44, 0x61, 0x74, 0x61, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x49, 0x6e, 0x66, 0x6f,
|
||||
0x52, 0x06, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x63, 0x6f, 0x64, 0x65,
|
||||
0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x22, 0xc1, 0x01, 0x0a,
|
||||
0x11, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x52, 0x65, 0x6c, 0x61, 0x74, 0x65, 0x52, 0x65, 0x6c,
|
||||
0x61, 0x79, 0x12, 0x12, 0x0a, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09,
|
||||
0x52, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x12, 0x54, 0x0a, 0x10, 0x63, 0x6f, 0x6d, 0x62, 0x69, 0x6e,
|
||||
0x61, 0x74, 0x69, 0x6f, 0x6e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b,
|
||||
0x32, 0x28, 0x2e, 0x72, 0x65, 0x6c, 0x61, 0x79, 0x43, 0x61, 0x62, 0x69, 0x6e, 0x65, 0x74, 0x47,
|
||||
0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x44, 0x61, 0x74, 0x61, 0x2e, 0x43, 0x6f, 0x6d, 0x62, 0x69,
|
||||
0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x74, 0x79, 0x70, 0x65, 0x52, 0x10, 0x63, 0x6f, 0x6d, 0x62,
|
||||
0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x12, 0x42, 0x0a, 0x0a,
|
||||
0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x54, 0x79, 0x70, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0e,
|
||||
0x32, 0x22, 0x2e, 0x67, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x44, 0x61, 0x74, 0x61, 0x2e, 0x52,
|
||||
0x65, 0x6c, 0x61, 0x74, 0x65, 0x64, 0x52, 0x65, 0x66, 0x2e, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65,
|
||||
0x54, 0x79, 0x70, 0x65, 0x52, 0x0a, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x54, 0x79, 0x70, 0x65,
|
||||
0x22, 0x67, 0x0a, 0x0f, 0x43, 0x6f, 0x6d, 0x62, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x74,
|
||||
0x79, 0x70, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28,
|
||||
0x09, 0x52, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x12, 0x22, 0x0a, 0x0c, 0x6f, 0x6c, 0x64, 0x72, 0x65,
|
||||
0x66, 0x52, 0x65, 0x6c, 0x61, 0x79, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0c, 0x6f,
|
||||
0x6c, 0x64, 0x72, 0x65, 0x66, 0x52, 0x65, 0x6c, 0x61, 0x79, 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x72,
|
||||
0x65, 0x66, 0x52, 0x65, 0x6c, 0x61, 0x79, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0d, 0x52, 0x09,
|
||||
0x72, 0x65, 0x66, 0x52, 0x65, 0x6c, 0x61, 0x79, 0x73, 0x22, 0x7c, 0x0a, 0x0c, 0x55, 0x6e, 0x69,
|
||||
0x71, 0x75, 0x65, 0x49, 0x64, 0x54, 0x79, 0x70, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x63, 0x69, 0x74,
|
||||
0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x63, 0x69, 0x74, 0x79, 0x12, 0x16, 0x0a,
|
||||
0x06, 0x6c, 0x69, 0x6e, 0x65, 0x49, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6c,
|
||||
0x69, 0x6e, 0x65, 0x49, 0x64, 0x12, 0x40, 0x0a, 0x1b, 0x62, 0x65, 0x6c, 0x6f, 0x6e, 0x67, 0x73,
|
||||
0x43, 0x6f, 0x6e, 0x63, 0x65, 0x6e, 0x74, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61,
|
||||
0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x1b, 0x62, 0x65, 0x6c, 0x6f,
|
||||
0x6e, 0x67, 0x73, 0x43, 0x6f, 0x6e, 0x63, 0x65, 0x6e, 0x74, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e,
|
||||
0x53, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x5c, 0x0a, 0x04, 0x43, 0x69, 0x43, 0x6a, 0x12,
|
||||
0x18, 0x0a, 0x07, 0x64, 0x73, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05,
|
||||
0x52, 0x07, 0x64, 0x73, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x3a, 0x0a, 0x06, 0x63, 0x6a, 0x4c,
|
||||
0x69, 0x73, 0x74, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x72, 0x65, 0x6c, 0x61,
|
||||
0x79, 0x43, 0x61, 0x62, 0x69, 0x6e, 0x65, 0x74, 0x47, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x44,
|
||||
0x61, 0x74, 0x61, 0x2e, 0x43, 0x6a, 0x44, 0x61, 0x74, 0x61, 0x53, 0x65, 0x74, 0x52, 0x06, 0x63,
|
||||
0x6a, 0x4c, 0x69, 0x73, 0x74, 0x22, 0x5a, 0x0a, 0x09, 0x43, 0x6a, 0x44, 0x61, 0x74, 0x61, 0x53,
|
||||
0x65, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
|
||||
0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x39, 0x0a, 0x07, 0x62, 0x69, 0x74, 0x4c, 0x69, 0x73,
|
||||
0x74, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x72, 0x65, 0x6c, 0x61, 0x79, 0x43,
|
||||
0x65, 0x52, 0x08, 0x6e, 0x65, 0x77, 0x4d, 0x6f, 0x64, 0x65, 0x6c, 0x12, 0x1a, 0x0a, 0x08, 0x73,
|
||||
0x68, 0x6f, 0x77, 0x43, 0x6f, 0x64, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x73,
|
||||
0x68, 0x6f, 0x77, 0x43, 0x6f, 0x64, 0x65, 0x12, 0x67, 0x0a, 0x16, 0x64, 0x65, 0x66, 0x61, 0x75,
|
||||
0x6c, 0x74, 0x49, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f,
|
||||
0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x2f, 0x2e, 0x72, 0x65, 0x6c, 0x61, 0x79, 0x43,
|
||||
0x61, 0x62, 0x69, 0x6e, 0x65, 0x74, 0x47, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x44, 0x61, 0x74,
|
||||
0x61, 0x2e, 0x43, 0x6a, 0x44, 0x61, 0x74, 0x61, 0x52, 0x07, 0x62, 0x69, 0x74, 0x4c, 0x69, 0x73,
|
||||
0x74, 0x22, 0x4b, 0x0a, 0x06, 0x43, 0x6a, 0x44, 0x61, 0x74, 0x61, 0x12, 0x41, 0x0a, 0x09, 0x72,
|
||||
0x65, 0x66, 0x52, 0x65, 0x6c, 0x61, 0x79, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x23,
|
||||
0x2e, 0x72, 0x65, 0x6c, 0x61, 0x79, 0x43, 0x61, 0x62, 0x69, 0x6e, 0x65, 0x74, 0x47, 0x72, 0x61,
|
||||
0x70, 0x68, 0x69, 0x63, 0x44, 0x61, 0x74, 0x61, 0x2e, 0x43, 0x6a, 0x44, 0x61, 0x74, 0x61, 0x49,
|
||||
0x74, 0x65, 0x6d, 0x52, 0x09, 0x72, 0x65, 0x66, 0x52, 0x65, 0x6c, 0x61, 0x79, 0x73, 0x22, 0xb0,
|
||||
0x01, 0x0a, 0x0a, 0x43, 0x6a, 0x44, 0x61, 0x74, 0x61, 0x49, 0x74, 0x65, 0x6d, 0x12, 0x1e, 0x0a,
|
||||
0x0a, 0x6f, 0x6c, 0x64, 0x72, 0x65, 0x6c, 0x61, 0x79, 0x49, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28,
|
||||
0x09, 0x52, 0x0a, 0x6f, 0x6c, 0x64, 0x72, 0x65, 0x6c, 0x61, 0x79, 0x49, 0x64, 0x12, 0x4b, 0x0a,
|
||||
0x08, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32,
|
||||
0x2f, 0x2e, 0x72, 0x65, 0x6c, 0x61, 0x79, 0x43, 0x61, 0x62, 0x69, 0x6e, 0x65, 0x74, 0x47, 0x72,
|
||||
0x61, 0x2e, 0x43, 0x6a, 0x44, 0x61, 0x74, 0x61, 0x49, 0x74, 0x65, 0x6d, 0x2e, 0x50, 0x6f, 0x73,
|
||||
0x74, 0x69, 0x6f, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x52, 0x16, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c,
|
||||
0x74, 0x49, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e,
|
||||
0x22, 0x97, 0x01, 0x0a, 0x09, 0x4d, 0x6f, 0x64, 0x65, 0x6c, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0b,
|
||||
0x0a, 0x07, 0x55, 0x6e, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x4a,
|
||||
0x50, 0x58, 0x43, 0x5f, 0x31, 0x30, 0x30, 0x30, 0x10, 0x01, 0x12, 0x0d, 0x0a, 0x09, 0x4a, 0x50,
|
||||
0x58, 0x43, 0x5f, 0x31, 0x37, 0x30, 0x30, 0x10, 0x02, 0x12, 0x0d, 0x0a, 0x09, 0x4a, 0x57, 0x4a,
|
||||
0x58, 0x43, 0x5f, 0x34, 0x38, 0x30, 0x10, 0x03, 0x12, 0x11, 0x0a, 0x0d, 0x4a, 0x57, 0x4a, 0x58,
|
||||
0x43, 0x5f, 0x48, 0x31, 0x32, 0x35, 0x5f, 0x38, 0x30, 0x10, 0x04, 0x12, 0x0d, 0x0a, 0x09, 0x4a,
|
||||
0x57, 0x58, 0x43, 0x5f, 0x31, 0x37, 0x30, 0x30, 0x10, 0x05, 0x12, 0x0d, 0x0a, 0x09, 0x4a, 0x57,
|
||||
0x58, 0x43, 0x5f, 0x48, 0x33, 0x34, 0x30, 0x10, 0x06, 0x12, 0x11, 0x0a, 0x0d, 0x4a, 0x59, 0x4a,
|
||||
0x58, 0x43, 0x5f, 0x31, 0x36, 0x30, 0x5f, 0x32, 0x36, 0x30, 0x10, 0x07, 0x12, 0x0c, 0x0a, 0x08,
|
||||
0x4a, 0x5a, 0x58, 0x43, 0x5f, 0x48, 0x31, 0x38, 0x10, 0x08, 0x22, 0x5c, 0x0a, 0x15, 0x50, 0x68,
|
||||
0x61, 0x73, 0x65, 0x46, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x50, 0x72, 0x6f, 0x74, 0x65, 0x63,
|
||||
0x74, 0x6f, 0x72, 0x12, 0x2f, 0x0a, 0x06, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x18, 0x01, 0x20,
|
||||
0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x44, 0x61, 0x74,
|
||||
0x61, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x06, 0x63, 0x6f,
|
||||
0x6d, 0x6d, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x02, 0x20, 0x01,
|
||||
0x28, 0x09, 0x52, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x22, 0x57, 0x0a, 0x10, 0x53, 0x69, 0x67, 0x6e,
|
||||
0x61, 0x6c, 0x46, 0x61, 0x75, 0x6c, 0x74, 0x41, 0x6c, 0x61, 0x72, 0x6d, 0x12, 0x2f, 0x0a, 0x06,
|
||||
0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67,
|
||||
0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x44, 0x61, 0x74, 0x61, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x6f,
|
||||
0x6e, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x06, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x12, 0x12, 0x0a,
|
||||
0x04, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x63, 0x6f, 0x64,
|
||||
0x65, 0x22, 0xc1, 0x01, 0x0a, 0x11, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x52, 0x65, 0x6c, 0x61,
|
||||
0x74, 0x65, 0x52, 0x65, 0x6c, 0x61, 0x79, 0x12, 0x12, 0x0a, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x18,
|
||||
0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x12, 0x54, 0x0a, 0x10, 0x63,
|
||||
0x6f, 0x6d, 0x62, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x18,
|
||||
0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x72, 0x65, 0x6c, 0x61, 0x79, 0x43, 0x61, 0x62,
|
||||
0x69, 0x6e, 0x65, 0x74, 0x47, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x44, 0x61, 0x74, 0x61, 0x2e,
|
||||
0x43, 0x6f, 0x6d, 0x62, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x74, 0x79, 0x70, 0x65, 0x52,
|
||||
0x10, 0x63, 0x6f, 0x6d, 0x62, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x74, 0x79, 0x70, 0x65,
|
||||
0x73, 0x12, 0x42, 0x0a, 0x0a, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x54, 0x79, 0x70, 0x65, 0x18,
|
||||
0x06, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x22, 0x2e, 0x67, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x44,
|
||||
0x61, 0x74, 0x61, 0x2e, 0x52, 0x65, 0x6c, 0x61, 0x74, 0x65, 0x64, 0x52, 0x65, 0x66, 0x2e, 0x44,
|
||||
0x65, 0x76, 0x69, 0x63, 0x65, 0x54, 0x79, 0x70, 0x65, 0x52, 0x0a, 0x64, 0x65, 0x76, 0x69, 0x63,
|
||||
0x65, 0x54, 0x79, 0x70, 0x65, 0x22, 0x43, 0x0a, 0x0f, 0x43, 0x6f, 0x6d, 0x62, 0x69, 0x6e, 0x61,
|
||||
0x74, 0x69, 0x6f, 0x6e, 0x74, 0x79, 0x70, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x63, 0x6f, 0x64, 0x65,
|
||||
0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x12, 0x1c, 0x0a, 0x09,
|
||||
0x72, 0x65, 0x66, 0x52, 0x65, 0x6c, 0x61, 0x79, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0d, 0x52,
|
||||
0x09, 0x72, 0x65, 0x66, 0x52, 0x65, 0x6c, 0x61, 0x79, 0x73, 0x22, 0x7c, 0x0a, 0x0c, 0x55, 0x6e,
|
||||
0x69, 0x71, 0x75, 0x65, 0x49, 0x64, 0x54, 0x79, 0x70, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x63, 0x69,
|
||||
0x74, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x63, 0x69, 0x74, 0x79, 0x12, 0x16,
|
||||
0x0a, 0x06, 0x6c, 0x69, 0x6e, 0x65, 0x49, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06,
|
||||
0x6c, 0x69, 0x6e, 0x65, 0x49, 0x64, 0x12, 0x40, 0x0a, 0x1b, 0x62, 0x65, 0x6c, 0x6f, 0x6e, 0x67,
|
||||
0x73, 0x43, 0x6f, 0x6e, 0x63, 0x65, 0x6e, 0x74, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74,
|
||||
0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x1b, 0x62, 0x65, 0x6c,
|
||||
0x6f, 0x6e, 0x67, 0x73, 0x43, 0x6f, 0x6e, 0x63, 0x65, 0x6e, 0x74, 0x72, 0x61, 0x74, 0x69, 0x6f,
|
||||
0x6e, 0x53, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x5c, 0x0a, 0x04, 0x43, 0x69, 0x43, 0x6a,
|
||||
0x12, 0x18, 0x0a, 0x07, 0x64, 0x73, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28,
|
||||
0x05, 0x52, 0x07, 0x64, 0x73, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x3a, 0x0a, 0x06, 0x63, 0x6a,
|
||||
0x4c, 0x69, 0x73, 0x74, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x72, 0x65, 0x6c,
|
||||
0x61, 0x79, 0x43, 0x61, 0x62, 0x69, 0x6e, 0x65, 0x74, 0x47, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63,
|
||||
0x44, 0x61, 0x74, 0x61, 0x2e, 0x43, 0x6a, 0x44, 0x61, 0x74, 0x61, 0x53, 0x65, 0x74, 0x52, 0x06,
|
||||
0x63, 0x6a, 0x4c, 0x69, 0x73, 0x74, 0x22, 0x5a, 0x0a, 0x09, 0x43, 0x6a, 0x44, 0x61, 0x74, 0x61,
|
||||
0x53, 0x65, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28,
|
||||
0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x39, 0x0a, 0x07, 0x62, 0x69, 0x74, 0x4c, 0x69,
|
||||
0x73, 0x74, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x72, 0x65, 0x6c, 0x61, 0x79,
|
||||
0x43, 0x61, 0x62, 0x69, 0x6e, 0x65, 0x74, 0x47, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x44, 0x61,
|
||||
0x74, 0x61, 0x2e, 0x43, 0x6a, 0x44, 0x61, 0x74, 0x61, 0x52, 0x07, 0x62, 0x69, 0x74, 0x4c, 0x69,
|
||||
0x73, 0x74, 0x22, 0x4b, 0x0a, 0x06, 0x43, 0x6a, 0x44, 0x61, 0x74, 0x61, 0x12, 0x41, 0x0a, 0x09,
|
||||
0x72, 0x65, 0x66, 0x52, 0x65, 0x6c, 0x61, 0x79, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32,
|
||||
0x23, 0x2e, 0x72, 0x65, 0x6c, 0x61, 0x79, 0x43, 0x61, 0x62, 0x69, 0x6e, 0x65, 0x74, 0x47, 0x72,
|
||||
0x61, 0x70, 0x68, 0x69, 0x63, 0x44, 0x61, 0x74, 0x61, 0x2e, 0x43, 0x6a, 0x44, 0x61, 0x74, 0x61,
|
||||
0x49, 0x74, 0x65, 0x6d, 0x2e, 0x50, 0x6f, 0x73, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x79, 0x70, 0x65,
|
||||
0x52, 0x08, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65,
|
||||
0x6c, 0x61, 0x79, 0x49, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65, 0x6c,
|
||||
0x61, 0x79, 0x49, 0x64, 0x22, 0x1b, 0x0a, 0x0b, 0x50, 0x6f, 0x73, 0x74, 0x69, 0x6f, 0x6e, 0x54,
|
||||
0x79, 0x70, 0x65, 0x12, 0x05, 0x0a, 0x01, 0x51, 0x10, 0x00, 0x12, 0x05, 0x0a, 0x01, 0x48, 0x10,
|
||||
0x01, 0x22, 0x5c, 0x0a, 0x04, 0x43, 0x69, 0x51, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x64, 0x73, 0x43,
|
||||
0x6f, 0x75, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x07, 0x64, 0x73, 0x43, 0x6f,
|
||||
0x75, 0x6e, 0x74, 0x12, 0x3a, 0x0a, 0x06, 0x71, 0x64, 0x4c, 0x69, 0x73, 0x74, 0x18, 0x02, 0x20,
|
||||
0x03, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x72, 0x65, 0x6c, 0x61, 0x79, 0x43, 0x61, 0x62, 0x69, 0x6e,
|
||||
0x65, 0x74, 0x47, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x44, 0x61, 0x74, 0x61, 0x2e, 0x51, 0x64,
|
||||
0x44, 0x61, 0x74, 0x61, 0x53, 0x65, 0x74, 0x52, 0x06, 0x71, 0x64, 0x4c, 0x69, 0x73, 0x74, 0x22,
|
||||
0x5a, 0x0a, 0x09, 0x51, 0x64, 0x44, 0x61, 0x74, 0x61, 0x53, 0x65, 0x74, 0x12, 0x12, 0x0a, 0x04,
|
||||
0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65,
|
||||
0x12, 0x39, 0x0a, 0x07, 0x62, 0x69, 0x74, 0x4c, 0x69, 0x73, 0x74, 0x18, 0x02, 0x20, 0x03, 0x28,
|
||||
0x0b, 0x32, 0x1f, 0x2e, 0x72, 0x65, 0x6c, 0x61, 0x79, 0x43, 0x61, 0x62, 0x69, 0x6e, 0x65, 0x74,
|
||||
0x47, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x44, 0x61, 0x74, 0x61, 0x2e, 0x51, 0x64, 0x44, 0x61,
|
||||
0x74, 0x61, 0x52, 0x07, 0x62, 0x69, 0x74, 0x4c, 0x69, 0x73, 0x74, 0x22, 0x4a, 0x0a, 0x06, 0x51,
|
||||
0x64, 0x44, 0x61, 0x74, 0x61, 0x12, 0x22, 0x0a, 0x0c, 0x6f, 0x6c, 0x64, 0x72, 0x65, 0x66, 0x52,
|
||||
0x65, 0x6c, 0x61, 0x79, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0c, 0x6f, 0x6c, 0x64,
|
||||
0x72, 0x65, 0x66, 0x52, 0x65, 0x6c, 0x61, 0x79, 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x72, 0x65, 0x66,
|
||||
0x52, 0x65, 0x6c, 0x61, 0x79, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0d, 0x52, 0x09, 0x72, 0x65,
|
||||
0x66, 0x52, 0x65, 0x6c, 0x61, 0x79, 0x73, 0x42, 0x2d, 0x5a, 0x2b, 0x6a, 0x6f, 0x79, 0x6c, 0x69,
|
||||
0x6e, 0x6b, 0x2e, 0x63, 0x6c, 0x75, 0x62, 0x2f, 0x62, 0x6a, 0x2d, 0x72, 0x74, 0x73, 0x74, 0x73,
|
||||
0x2d, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2f, 0x64, 0x74, 0x6f, 0x2f, 0x64, 0x61, 0x74, 0x61,
|
||||
0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
0x49, 0x74, 0x65, 0x6d, 0x52, 0x09, 0x72, 0x65, 0x66, 0x52, 0x65, 0x6c, 0x61, 0x79, 0x73, 0x22,
|
||||
0x9a, 0x01, 0x0a, 0x0a, 0x43, 0x6a, 0x44, 0x61, 0x74, 0x61, 0x49, 0x74, 0x65, 0x6d, 0x12, 0x4b,
|
||||
0x0a, 0x08, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e,
|
||||
0x32, 0x2f, 0x2e, 0x72, 0x65, 0x6c, 0x61, 0x79, 0x43, 0x61, 0x62, 0x69, 0x6e, 0x65, 0x74, 0x47,
|
||||
0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x44, 0x61, 0x74, 0x61, 0x2e, 0x43, 0x6a, 0x44, 0x61, 0x74,
|
||||
0x61, 0x49, 0x74, 0x65, 0x6d, 0x2e, 0x50, 0x6f, 0x73, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x79, 0x70,
|
||||
0x65, 0x52, 0x08, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x72,
|
||||
0x65, 0x6c, 0x61, 0x79, 0x49, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x72, 0x65,
|
||||
0x6c, 0x61, 0x79, 0x49, 0x64, 0x22, 0x25, 0x0a, 0x0b, 0x50, 0x6f, 0x73, 0x74, 0x69, 0x6f, 0x6e,
|
||||
0x54, 0x79, 0x70, 0x65, 0x12, 0x05, 0x0a, 0x01, 0x51, 0x10, 0x00, 0x12, 0x05, 0x0a, 0x01, 0x48,
|
||||
0x10, 0x01, 0x12, 0x08, 0x0a, 0x04, 0x4e, 0x4f, 0x4e, 0x45, 0x10, 0x02, 0x22, 0x5c, 0x0a, 0x04,
|
||||
0x43, 0x69, 0x51, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x64, 0x73, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x18,
|
||||
0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x07, 0x64, 0x73, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x3a,
|
||||
0x0a, 0x06, 0x71, 0x64, 0x4c, 0x69, 0x73, 0x74, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x22,
|
||||
0x2e, 0x72, 0x65, 0x6c, 0x61, 0x79, 0x43, 0x61, 0x62, 0x69, 0x6e, 0x65, 0x74, 0x47, 0x72, 0x61,
|
||||
0x70, 0x68, 0x69, 0x63, 0x44, 0x61, 0x74, 0x61, 0x2e, 0x51, 0x64, 0x44, 0x61, 0x74, 0x61, 0x53,
|
||||
0x65, 0x74, 0x52, 0x06, 0x71, 0x64, 0x4c, 0x69, 0x73, 0x74, 0x22, 0x5a, 0x0a, 0x09, 0x51, 0x64,
|
||||
0x44, 0x61, 0x74, 0x61, 0x53, 0x65, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18,
|
||||
0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x39, 0x0a, 0x07, 0x62,
|
||||
0x69, 0x74, 0x4c, 0x69, 0x73, 0x74, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x72,
|
||||
0x65, 0x6c, 0x61, 0x79, 0x43, 0x61, 0x62, 0x69, 0x6e, 0x65, 0x74, 0x47, 0x72, 0x61, 0x70, 0x68,
|
||||
0x69, 0x63, 0x44, 0x61, 0x74, 0x61, 0x2e, 0x51, 0x64, 0x44, 0x61, 0x74, 0x61, 0x52, 0x07, 0x62,
|
||||
0x69, 0x74, 0x4c, 0x69, 0x73, 0x74, 0x22, 0x26, 0x0a, 0x06, 0x51, 0x64, 0x44, 0x61, 0x74, 0x61,
|
||||
0x12, 0x1c, 0x0a, 0x09, 0x72, 0x65, 0x66, 0x52, 0x65, 0x6c, 0x61, 0x79, 0x73, 0x18, 0x02, 0x20,
|
||||
0x03, 0x28, 0x0d, 0x52, 0x09, 0x72, 0x65, 0x66, 0x52, 0x65, 0x6c, 0x61, 0x79, 0x73, 0x42, 0x2d,
|
||||
0x5a, 0x2b, 0x6a, 0x6f, 0x79, 0x6c, 0x69, 0x6e, 0x6b, 0x2e, 0x63, 0x6c, 0x75, 0x62, 0x2f, 0x62,
|
||||
0x6a, 0x2d, 0x72, 0x74, 0x73, 0x74, 0x73, 0x2d, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2f, 0x64,
|
||||
0x74, 0x6f, 0x2f, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70,
|
||||
0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
}
|
||||
|
||||
var (
|
||||
@ -1270,21 +1271,22 @@ var file_relayCabinetLayoutGraphics_proto_depIdxs = []int32{
|
||||
18, // 9: relayCabinetGraphicData.RelayCabinet.common:type_name -> graphicData.CommonInfo
|
||||
18, // 10: relayCabinetGraphicData.Relay.common:type_name -> graphicData.CommonInfo
|
||||
0, // 11: relayCabinetGraphicData.Relay.newModel:type_name -> relayCabinetGraphicData.Relay.ModelType
|
||||
18, // 12: relayCabinetGraphicData.PhaseFailureProtector.common:type_name -> graphicData.CommonInfo
|
||||
18, // 13: relayCabinetGraphicData.SignalFaultAlarm.common:type_name -> graphicData.CommonInfo
|
||||
8, // 14: relayCabinetGraphicData.DeviceRelateRelay.combinationtypes:type_name -> relayCabinetGraphicData.Combinationtype
|
||||
19, // 15: relayCabinetGraphicData.DeviceRelateRelay.deviceType:type_name -> graphicData.RelatedRef.DeviceType
|
||||
11, // 16: relayCabinetGraphicData.CiCj.cjList:type_name -> relayCabinetGraphicData.CjDataSet
|
||||
12, // 17: relayCabinetGraphicData.CjDataSet.bitList:type_name -> relayCabinetGraphicData.CjData
|
||||
13, // 18: relayCabinetGraphicData.CjData.refRelays:type_name -> relayCabinetGraphicData.CjDataItem
|
||||
1, // 19: relayCabinetGraphicData.CjDataItem.position:type_name -> relayCabinetGraphicData.CjDataItem.PostionType
|
||||
15, // 20: relayCabinetGraphicData.CiQd.qdList:type_name -> relayCabinetGraphicData.QdDataSet
|
||||
16, // 21: relayCabinetGraphicData.QdDataSet.bitList:type_name -> relayCabinetGraphicData.QdData
|
||||
22, // [22:22] is the sub-list for method output_type
|
||||
22, // [22:22] is the sub-list for method input_type
|
||||
22, // [22:22] is the sub-list for extension type_name
|
||||
22, // [22:22] is the sub-list for extension extendee
|
||||
0, // [0:22] is the sub-list for field type_name
|
||||
1, // 12: relayCabinetGraphicData.Relay.defaultInitialPosition:type_name -> relayCabinetGraphicData.CjDataItem.PostionType
|
||||
18, // 13: relayCabinetGraphicData.PhaseFailureProtector.common:type_name -> graphicData.CommonInfo
|
||||
18, // 14: relayCabinetGraphicData.SignalFaultAlarm.common:type_name -> graphicData.CommonInfo
|
||||
8, // 15: relayCabinetGraphicData.DeviceRelateRelay.combinationtypes:type_name -> relayCabinetGraphicData.Combinationtype
|
||||
19, // 16: relayCabinetGraphicData.DeviceRelateRelay.deviceType:type_name -> graphicData.RelatedRef.DeviceType
|
||||
11, // 17: relayCabinetGraphicData.CiCj.cjList:type_name -> relayCabinetGraphicData.CjDataSet
|
||||
12, // 18: relayCabinetGraphicData.CjDataSet.bitList:type_name -> relayCabinetGraphicData.CjData
|
||||
13, // 19: relayCabinetGraphicData.CjData.refRelays:type_name -> relayCabinetGraphicData.CjDataItem
|
||||
1, // 20: relayCabinetGraphicData.CjDataItem.position:type_name -> relayCabinetGraphicData.CjDataItem.PostionType
|
||||
15, // 21: relayCabinetGraphicData.CiQd.qdList:type_name -> relayCabinetGraphicData.QdDataSet
|
||||
16, // 22: relayCabinetGraphicData.QdDataSet.bitList:type_name -> relayCabinetGraphicData.QdData
|
||||
23, // [23:23] is the sub-list for method output_type
|
||||
23, // [23:23] is the sub-list for method input_type
|
||||
23, // [23:23] is the sub-list for extension type_name
|
||||
23, // [23:23] is the sub-list for extension extendee
|
||||
0, // [0:23] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_relayCabinetLayoutGraphics_proto_init() }
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,6 +1,6 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.32.0
|
||||
// protoc-gen-go v1.33.0
|
||||
// protoc v4.23.1
|
||||
// source: tccGraphics.proto
|
||||
|
||||
@ -20,52 +20,104 @@ const (
|
||||
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||
)
|
||||
|
||||
type TccKeyType int32
|
||||
type TccElementColor int32
|
||||
|
||||
const (
|
||||
TccKeyType_driverControllerActivationClint TccKeyType = 0 //司控器激活端
|
||||
TccKeyType_frontAndRearDirectionalControl TccKeyType = 1 //前后方向控制
|
||||
TccElementColor_green TccElementColor = 0
|
||||
TccElementColor_red TccElementColor = 1
|
||||
TccElementColor_blue TccElementColor = 2
|
||||
)
|
||||
|
||||
// Enum value maps for TccKeyType.
|
||||
// Enum value maps for TccElementColor.
|
||||
var (
|
||||
TccKeyType_name = map[int32]string{
|
||||
0: "driverControllerActivationClint",
|
||||
1: "frontAndRearDirectionalControl",
|
||||
TccElementColor_name = map[int32]string{
|
||||
0: "green",
|
||||
1: "red",
|
||||
2: "blue",
|
||||
}
|
||||
TccKeyType_value = map[string]int32{
|
||||
"driverControllerActivationClint": 0,
|
||||
"frontAndRearDirectionalControl": 1,
|
||||
TccElementColor_value = map[string]int32{
|
||||
"green": 0,
|
||||
"red": 1,
|
||||
"blue": 2,
|
||||
}
|
||||
)
|
||||
|
||||
func (x TccKeyType) Enum() *TccKeyType {
|
||||
p := new(TccKeyType)
|
||||
func (x TccElementColor) Enum() *TccElementColor {
|
||||
p := new(TccElementColor)
|
||||
*p = x
|
||||
return p
|
||||
}
|
||||
|
||||
func (x TccKeyType) String() string {
|
||||
func (x TccElementColor) String() string {
|
||||
return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
|
||||
}
|
||||
|
||||
func (TccKeyType) Descriptor() protoreflect.EnumDescriptor {
|
||||
func (TccElementColor) Descriptor() protoreflect.EnumDescriptor {
|
||||
return file_tccGraphics_proto_enumTypes[0].Descriptor()
|
||||
}
|
||||
|
||||
func (TccKeyType) Type() protoreflect.EnumType {
|
||||
func (TccElementColor) Type() protoreflect.EnumType {
|
||||
return &file_tccGraphics_proto_enumTypes[0]
|
||||
}
|
||||
|
||||
func (x TccKeyType) Number() protoreflect.EnumNumber {
|
||||
func (x TccElementColor) Number() protoreflect.EnumNumber {
|
||||
return protoreflect.EnumNumber(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use TccKeyType.Descriptor instead.
|
||||
func (TccKeyType) EnumDescriptor() ([]byte, []int) {
|
||||
// Deprecated: Use TccElementColor.Descriptor instead.
|
||||
func (TccElementColor) EnumDescriptor() ([]byte, []int) {
|
||||
return file_tccGraphics_proto_rawDescGZIP(), []int{0}
|
||||
}
|
||||
|
||||
type TccKey_TccKeyType int32
|
||||
|
||||
const (
|
||||
TccKey_driverControllerActivationClint TccKey_TccKeyType = 0 //司控器激活端
|
||||
TccKey_frontAndRearDirectionalControl TccKey_TccKeyType = 1 //前后方向控制
|
||||
TccKey_trainDoorMode TccKey_TccKeyType = 2 //列车门模式
|
||||
)
|
||||
|
||||
// Enum value maps for TccKey_TccKeyType.
|
||||
var (
|
||||
TccKey_TccKeyType_name = map[int32]string{
|
||||
0: "driverControllerActivationClint",
|
||||
1: "frontAndRearDirectionalControl",
|
||||
2: "trainDoorMode",
|
||||
}
|
||||
TccKey_TccKeyType_value = map[string]int32{
|
||||
"driverControllerActivationClint": 0,
|
||||
"frontAndRearDirectionalControl": 1,
|
||||
"trainDoorMode": 2,
|
||||
}
|
||||
)
|
||||
|
||||
func (x TccKey_TccKeyType) Enum() *TccKey_TccKeyType {
|
||||
p := new(TccKey_TccKeyType)
|
||||
*p = x
|
||||
return p
|
||||
}
|
||||
|
||||
func (x TccKey_TccKeyType) String() string {
|
||||
return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
|
||||
}
|
||||
|
||||
func (TccKey_TccKeyType) Descriptor() protoreflect.EnumDescriptor {
|
||||
return file_tccGraphics_proto_enumTypes[1].Descriptor()
|
||||
}
|
||||
|
||||
func (TccKey_TccKeyType) Type() protoreflect.EnumType {
|
||||
return &file_tccGraphics_proto_enumTypes[1]
|
||||
}
|
||||
|
||||
func (x TccKey_TccKeyType) Number() protoreflect.EnumNumber {
|
||||
return protoreflect.EnumNumber(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use TccKey_TccKeyType.Descriptor instead.
|
||||
func (TccKey_TccKeyType) EnumDescriptor() ([]byte, []int) {
|
||||
return file_tccGraphics_proto_rawDescGZIP(), []int{3, 0}
|
||||
}
|
||||
|
||||
type TccGraphicStorage struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
@ -76,6 +128,7 @@ type TccGraphicStorage struct {
|
||||
TccTexts []*TccText `protobuf:"bytes,3,rep,name=tccTexts,proto3" json:"tccTexts,omitempty"`
|
||||
TccKeys []*TccKey `protobuf:"bytes,4,rep,name=tccKeys,proto3" json:"tccKeys,omitempty"`
|
||||
TccHandles []*TccHandle `protobuf:"bytes,5,rep,name=tccHandles,proto3" json:"tccHandles,omitempty"`
|
||||
TccLights []*TccLight `protobuf:"bytes,6,rep,name=tccLights,proto3" json:"tccLights,omitempty"`
|
||||
}
|
||||
|
||||
func (x *TccGraphicStorage) Reset() {
|
||||
@ -145,6 +198,13 @@ func (x *TccGraphicStorage) GetTccHandles() []*TccHandle {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *TccGraphicStorage) GetTccLights() []*TccLight {
|
||||
if x != nil {
|
||||
return x.TccLights
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// * TCC按钮
|
||||
type TccButton struct {
|
||||
state protoimpl.MessageState
|
||||
@ -295,9 +355,9 @@ type TccKey struct {
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Common *CommonInfo `protobuf:"bytes,1,opt,name=common,proto3" json:"common,omitempty"`
|
||||
Code string `protobuf:"bytes,2,opt,name=code,proto3" json:"code,omitempty"`
|
||||
Type TccKeyType `protobuf:"varint,3,opt,name=type,proto3,enum=tccGraphicData.TccKeyType" json:"type,omitempty"`
|
||||
Common *CommonInfo `protobuf:"bytes,1,opt,name=common,proto3" json:"common,omitempty"`
|
||||
Code string `protobuf:"bytes,2,opt,name=code,proto3" json:"code,omitempty"`
|
||||
Type TccKey_TccKeyType `protobuf:"varint,3,opt,name=type,proto3,enum=tccGraphicData.TccKey_TccKeyType" json:"type,omitempty"`
|
||||
}
|
||||
|
||||
func (x *TccKey) Reset() {
|
||||
@ -346,11 +406,11 @@ func (x *TccKey) GetCode() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *TccKey) GetType() TccKeyType {
|
||||
func (x *TccKey) GetType() TccKey_TccKeyType {
|
||||
if x != nil {
|
||||
return x.Type
|
||||
}
|
||||
return TccKeyType_driverControllerActivationClint
|
||||
return TccKey_driverControllerActivationClint
|
||||
}
|
||||
|
||||
// * TCC手柄
|
||||
@ -409,6 +469,86 @@ func (x *TccHandle) GetCode() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
// * TCC灯
|
||||
type TccLight struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Common *CommonInfo `protobuf:"bytes,1,opt,name=common,proto3" json:"common,omitempty"`
|
||||
Code string `protobuf:"bytes,2,opt,name=code,proto3" json:"code,omitempty"`
|
||||
LightColor TccElementColor `protobuf:"varint,3,opt,name=lightColor,proto3,enum=tccGraphicData.TccElementColor" json:"lightColor,omitempty"`
|
||||
ActiveLevel bool `protobuf:"varint,4,opt,name=activeLevel,proto3" json:"activeLevel,omitempty"` //有效电平
|
||||
InitialState bool `protobuf:"varint,5,opt,name=initialState,proto3" json:"initialState,omitempty"` //初始状态,与有效电平对比,如何相同,刚开始打开驾驶台的时候就是亮着的
|
||||
}
|
||||
|
||||
func (x *TccLight) Reset() {
|
||||
*x = TccLight{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_tccGraphics_proto_msgTypes[5]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *TccLight) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*TccLight) ProtoMessage() {}
|
||||
|
||||
func (x *TccLight) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_tccGraphics_proto_msgTypes[5]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use TccLight.ProtoReflect.Descriptor instead.
|
||||
func (*TccLight) Descriptor() ([]byte, []int) {
|
||||
return file_tccGraphics_proto_rawDescGZIP(), []int{5}
|
||||
}
|
||||
|
||||
func (x *TccLight) GetCommon() *CommonInfo {
|
||||
if x != nil {
|
||||
return x.Common
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *TccLight) GetCode() string {
|
||||
if x != nil {
|
||||
return x.Code
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *TccLight) GetLightColor() TccElementColor {
|
||||
if x != nil {
|
||||
return x.LightColor
|
||||
}
|
||||
return TccElementColor_green
|
||||
}
|
||||
|
||||
func (x *TccLight) GetActiveLevel() bool {
|
||||
if x != nil {
|
||||
return x.ActiveLevel
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (x *TccLight) GetInitialState() bool {
|
||||
if x != nil {
|
||||
return x.InitialState
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
var File_tccGraphics_proto protoreflect.FileDescriptor
|
||||
|
||||
var file_tccGraphics_proto_rawDesc = []byte{
|
||||
@ -416,7 +556,7 @@ var file_tccGraphics_proto_rawDesc = []byte{
|
||||
0x6f, 0x74, 0x6f, 0x12, 0x0e, 0x74, 0x63, 0x63, 0x47, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x44,
|
||||
0x61, 0x74, 0x61, 0x1a, 0x1b, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4c, 0x61, 0x79, 0x6f,
|
||||
0x75, 0x74, 0x47, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
|
||||
0x22, 0x9d, 0x02, 0x0a, 0x11, 0x54, 0x63, 0x63, 0x47, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x53,
|
||||
0x22, 0xd5, 0x02, 0x0a, 0x11, 0x54, 0x63, 0x63, 0x47, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x53,
|
||||
0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x12, 0x2b, 0x0a, 0x06, 0x63, 0x61, 0x6e, 0x76, 0x61, 0x73,
|
||||
0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x67, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63,
|
||||
0x44, 0x61, 0x74, 0x61, 0x2e, 0x43, 0x61, 0x6e, 0x76, 0x61, 0x73, 0x52, 0x06, 0x63, 0x61, 0x6e,
|
||||
@ -434,45 +574,67 @@ var file_tccGraphics_proto_rawDesc = []byte{
|
||||
0x6c, 0x65, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x74, 0x63, 0x63, 0x47,
|
||||
0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x44, 0x61, 0x74, 0x61, 0x2e, 0x54, 0x63, 0x63, 0x48, 0x61,
|
||||
0x6e, 0x64, 0x6c, 0x65, 0x52, 0x0a, 0x74, 0x63, 0x63, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x73,
|
||||
0x22, 0x72, 0x0a, 0x09, 0x54, 0x63, 0x63, 0x42, 0x75, 0x74, 0x74, 0x6f, 0x6e, 0x12, 0x2f, 0x0a,
|
||||
0x06, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e,
|
||||
0x67, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x44, 0x61, 0x74, 0x61, 0x2e, 0x43, 0x6f, 0x6d, 0x6d,
|
||||
0x6f, 0x6e, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x06, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x12, 0x12,
|
||||
0x0a, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x63, 0x6f,
|
||||
0x64, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x69, 0x73, 0x53, 0x65, 0x6c, 0x66, 0x52, 0x65, 0x73, 0x65,
|
||||
0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x69, 0x73, 0x53, 0x65, 0x6c, 0x66, 0x52,
|
||||
0x65, 0x73, 0x65, 0x74, 0x22, 0x9a, 0x01, 0x0a, 0x07, 0x54, 0x63, 0x63, 0x54, 0x65, 0x78, 0x74,
|
||||
0x12, 0x2f, 0x0a, 0x06, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b,
|
||||
0x32, 0x17, 0x2e, 0x67, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x44, 0x61, 0x74, 0x61, 0x2e, 0x43,
|
||||
0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x06, 0x63, 0x6f, 0x6d, 0x6d, 0x6f,
|
||||
0x6e, 0x12, 0x12, 0x0a, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52,
|
||||
0x04, 0x63, 0x6f, 0x64, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74,
|
||||
0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x12,
|
||||
0x14, 0x0a, 0x05, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05,
|
||||
0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x12, 0x1a, 0x0a, 0x08, 0x66, 0x6f, 0x6e, 0x74, 0x53, 0x69, 0x7a,
|
||||
0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x66, 0x6f, 0x6e, 0x74, 0x53, 0x69, 0x7a,
|
||||
0x65, 0x22, 0x7d, 0x0a, 0x06, 0x54, 0x63, 0x63, 0x4b, 0x65, 0x79, 0x12, 0x2f, 0x0a, 0x06, 0x63,
|
||||
0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x72,
|
||||
0x61, 0x70, 0x68, 0x69, 0x63, 0x44, 0x61, 0x74, 0x61, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e,
|
||||
0x49, 0x6e, 0x66, 0x6f, 0x52, 0x06, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04,
|
||||
0x63, 0x6f, 0x64, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x63, 0x6f, 0x64, 0x65,
|
||||
0x12, 0x2e, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1a,
|
||||
0x2e, 0x74, 0x63, 0x63, 0x47, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x44, 0x61, 0x74, 0x61, 0x2e,
|
||||
0x12, 0x36, 0x0a, 0x09, 0x74, 0x63, 0x63, 0x4c, 0x69, 0x67, 0x68, 0x74, 0x73, 0x18, 0x06, 0x20,
|
||||
0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x74, 0x63, 0x63, 0x47, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63,
|
||||
0x44, 0x61, 0x74, 0x61, 0x2e, 0x54, 0x63, 0x63, 0x4c, 0x69, 0x67, 0x68, 0x74, 0x52, 0x09, 0x74,
|
||||
0x63, 0x63, 0x4c, 0x69, 0x67, 0x68, 0x74, 0x73, 0x22, 0x72, 0x0a, 0x09, 0x54, 0x63, 0x63, 0x42,
|
||||
0x75, 0x74, 0x74, 0x6f, 0x6e, 0x12, 0x2f, 0x0a, 0x06, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x18,
|
||||
0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x44,
|
||||
0x61, 0x74, 0x61, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x06,
|
||||
0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x02,
|
||||
0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x69, 0x73,
|
||||
0x53, 0x65, 0x6c, 0x66, 0x52, 0x65, 0x73, 0x65, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52,
|
||||
0x0b, 0x69, 0x73, 0x53, 0x65, 0x6c, 0x66, 0x52, 0x65, 0x73, 0x65, 0x74, 0x22, 0x9a, 0x01, 0x0a,
|
||||
0x07, 0x54, 0x63, 0x63, 0x54, 0x65, 0x78, 0x74, 0x12, 0x2f, 0x0a, 0x06, 0x63, 0x6f, 0x6d, 0x6d,
|
||||
0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x72, 0x61, 0x70, 0x68,
|
||||
0x69, 0x63, 0x44, 0x61, 0x74, 0x61, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x49, 0x6e, 0x66,
|
||||
0x6f, 0x52, 0x06, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x63, 0x6f, 0x64,
|
||||
0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x12, 0x18, 0x0a,
|
||||
0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07,
|
||||
0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x6f, 0x6c, 0x6f, 0x72,
|
||||
0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x12, 0x1a, 0x0a,
|
||||
0x08, 0x66, 0x6f, 0x6e, 0x74, 0x53, 0x69, 0x7a, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, 0x52,
|
||||
0x08, 0x66, 0x6f, 0x6e, 0x74, 0x53, 0x69, 0x7a, 0x65, 0x22, 0xee, 0x01, 0x0a, 0x06, 0x54, 0x63,
|
||||
0x63, 0x4b, 0x65, 0x79, 0x12, 0x2f, 0x0a, 0x06, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x18, 0x01,
|
||||
0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x44, 0x61,
|
||||
0x74, 0x61, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x06, 0x63,
|
||||
0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x02, 0x20,
|
||||
0x01, 0x28, 0x09, 0x52, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x12, 0x35, 0x0a, 0x04, 0x74, 0x79, 0x70,
|
||||
0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x21, 0x2e, 0x74, 0x63, 0x63, 0x47, 0x72, 0x61,
|
||||
0x70, 0x68, 0x69, 0x63, 0x44, 0x61, 0x74, 0x61, 0x2e, 0x54, 0x63, 0x63, 0x4b, 0x65, 0x79, 0x2e,
|
||||
0x54, 0x63, 0x63, 0x4b, 0x65, 0x79, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65,
|
||||
0x22, 0x50, 0x0a, 0x09, 0x54, 0x63, 0x63, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x12, 0x2f, 0x0a,
|
||||
0x06, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e,
|
||||
0x67, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x44, 0x61, 0x74, 0x61, 0x2e, 0x43, 0x6f, 0x6d, 0x6d,
|
||||
0x6f, 0x6e, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x06, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x12, 0x12,
|
||||
0x0a, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x63, 0x6f,
|
||||
0x64, 0x65, 0x2a, 0x55, 0x0a, 0x0a, 0x54, 0x63, 0x63, 0x4b, 0x65, 0x79, 0x54, 0x79, 0x70, 0x65,
|
||||
0x12, 0x23, 0x0a, 0x1f, 0x64, 0x72, 0x69, 0x76, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f,
|
||||
0x6c, 0x6c, 0x65, 0x72, 0x41, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x43, 0x6c,
|
||||
0x69, 0x6e, 0x74, 0x10, 0x00, 0x12, 0x22, 0x0a, 0x1e, 0x66, 0x72, 0x6f, 0x6e, 0x74, 0x41, 0x6e,
|
||||
0x64, 0x52, 0x65, 0x61, 0x72, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c,
|
||||
0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x10, 0x01, 0x42, 0x2d, 0x5a, 0x2b, 0x6a, 0x6f, 0x79,
|
||||
0x6c, 0x69, 0x6e, 0x6b, 0x2e, 0x63, 0x6c, 0x75, 0x62, 0x2f, 0x62, 0x6a, 0x2d, 0x72, 0x74, 0x73,
|
||||
0x74, 0x73, 0x2d, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2f, 0x64, 0x74, 0x6f, 0x2f, 0x64, 0x61,
|
||||
0x74, 0x61, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
0x22, 0x68, 0x0a, 0x0a, 0x54, 0x63, 0x63, 0x4b, 0x65, 0x79, 0x54, 0x79, 0x70, 0x65, 0x12, 0x23,
|
||||
0x0a, 0x1f, 0x64, 0x72, 0x69, 0x76, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c,
|
||||
0x65, 0x72, 0x41, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x43, 0x6c, 0x69, 0x6e,
|
||||
0x74, 0x10, 0x00, 0x12, 0x22, 0x0a, 0x1e, 0x66, 0x72, 0x6f, 0x6e, 0x74, 0x41, 0x6e, 0x64, 0x52,
|
||||
0x65, 0x61, 0x72, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x43, 0x6f,
|
||||
0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x10, 0x01, 0x12, 0x11, 0x0a, 0x0d, 0x74, 0x72, 0x61, 0x69, 0x6e,
|
||||
0x44, 0x6f, 0x6f, 0x72, 0x4d, 0x6f, 0x64, 0x65, 0x10, 0x02, 0x22, 0x50, 0x0a, 0x09, 0x54, 0x63,
|
||||
0x63, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x12, 0x2f, 0x0a, 0x06, 0x63, 0x6f, 0x6d, 0x6d, 0x6f,
|
||||
0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x72, 0x61, 0x70, 0x68, 0x69,
|
||||
0x63, 0x44, 0x61, 0x74, 0x61, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x49, 0x6e, 0x66, 0x6f,
|
||||
0x52, 0x06, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x63, 0x6f, 0x64, 0x65,
|
||||
0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x22, 0xd6, 0x01, 0x0a,
|
||||
0x08, 0x54, 0x63, 0x63, 0x4c, 0x69, 0x67, 0x68, 0x74, 0x12, 0x2f, 0x0a, 0x06, 0x63, 0x6f, 0x6d,
|
||||
0x6d, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x72, 0x61, 0x70,
|
||||
0x68, 0x69, 0x63, 0x44, 0x61, 0x74, 0x61, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x49, 0x6e,
|
||||
0x66, 0x6f, 0x52, 0x06, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x63, 0x6f,
|
||||
0x64, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x12, 0x3f,
|
||||
0x0a, 0x0a, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x18, 0x03, 0x20, 0x01,
|
||||
0x28, 0x0e, 0x32, 0x1f, 0x2e, 0x74, 0x63, 0x63, 0x47, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x44,
|
||||
0x61, 0x74, 0x61, 0x2e, 0x54, 0x63, 0x63, 0x45, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x43, 0x6f,
|
||||
0x6c, 0x6f, 0x72, 0x52, 0x0a, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x12,
|
||||
0x20, 0x0a, 0x0b, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x18, 0x04,
|
||||
0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x4c, 0x65, 0x76, 0x65,
|
||||
0x6c, 0x12, 0x22, 0x0a, 0x0c, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x53, 0x74, 0x61, 0x74,
|
||||
0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c,
|
||||
0x53, 0x74, 0x61, 0x74, 0x65, 0x2a, 0x2f, 0x0a, 0x0f, 0x54, 0x63, 0x63, 0x45, 0x6c, 0x65, 0x6d,
|
||||
0x65, 0x6e, 0x74, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x12, 0x09, 0x0a, 0x05, 0x67, 0x72, 0x65, 0x65,
|
||||
0x6e, 0x10, 0x00, 0x12, 0x07, 0x0a, 0x03, 0x72, 0x65, 0x64, 0x10, 0x01, 0x12, 0x08, 0x0a, 0x04,
|
||||
0x62, 0x6c, 0x75, 0x65, 0x10, 0x02, 0x42, 0x2d, 0x5a, 0x2b, 0x6a, 0x6f, 0x79, 0x6c, 0x69, 0x6e,
|
||||
0x6b, 0x2e, 0x63, 0x6c, 0x75, 0x62, 0x2f, 0x62, 0x6a, 0x2d, 0x72, 0x74, 0x73, 0x74, 0x73, 0x2d,
|
||||
0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2f, 0x64, 0x74, 0x6f, 0x2f, 0x64, 0x61, 0x74, 0x61, 0x5f,
|
||||
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
}
|
||||
|
||||
var (
|
||||
@ -487,34 +649,39 @@ func file_tccGraphics_proto_rawDescGZIP() []byte {
|
||||
return file_tccGraphics_proto_rawDescData
|
||||
}
|
||||
|
||||
var file_tccGraphics_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
|
||||
var file_tccGraphics_proto_msgTypes = make([]protoimpl.MessageInfo, 5)
|
||||
var file_tccGraphics_proto_enumTypes = make([]protoimpl.EnumInfo, 2)
|
||||
var file_tccGraphics_proto_msgTypes = make([]protoimpl.MessageInfo, 6)
|
||||
var file_tccGraphics_proto_goTypes = []interface{}{
|
||||
(TccKeyType)(0), // 0: tccGraphicData.TccKeyType
|
||||
(*TccGraphicStorage)(nil), // 1: tccGraphicData.TccGraphicStorage
|
||||
(*TccButton)(nil), // 2: tccGraphicData.TccButton
|
||||
(*TccText)(nil), // 3: tccGraphicData.TccText
|
||||
(*TccKey)(nil), // 4: tccGraphicData.TccKey
|
||||
(*TccHandle)(nil), // 5: tccGraphicData.TccHandle
|
||||
(*Canvas)(nil), // 6: graphicData.Canvas
|
||||
(*CommonInfo)(nil), // 7: graphicData.CommonInfo
|
||||
(TccElementColor)(0), // 0: tccGraphicData.TccElementColor
|
||||
(TccKey_TccKeyType)(0), // 1: tccGraphicData.TccKey.TccKeyType
|
||||
(*TccGraphicStorage)(nil), // 2: tccGraphicData.TccGraphicStorage
|
||||
(*TccButton)(nil), // 3: tccGraphicData.TccButton
|
||||
(*TccText)(nil), // 4: tccGraphicData.TccText
|
||||
(*TccKey)(nil), // 5: tccGraphicData.TccKey
|
||||
(*TccHandle)(nil), // 6: tccGraphicData.TccHandle
|
||||
(*TccLight)(nil), // 7: tccGraphicData.TccLight
|
||||
(*Canvas)(nil), // 8: graphicData.Canvas
|
||||
(*CommonInfo)(nil), // 9: graphicData.CommonInfo
|
||||
}
|
||||
var file_tccGraphics_proto_depIdxs = []int32{
|
||||
6, // 0: tccGraphicData.TccGraphicStorage.canvas:type_name -> graphicData.Canvas
|
||||
2, // 1: tccGraphicData.TccGraphicStorage.tccButtons:type_name -> tccGraphicData.TccButton
|
||||
3, // 2: tccGraphicData.TccGraphicStorage.tccTexts:type_name -> tccGraphicData.TccText
|
||||
4, // 3: tccGraphicData.TccGraphicStorage.tccKeys:type_name -> tccGraphicData.TccKey
|
||||
5, // 4: tccGraphicData.TccGraphicStorage.tccHandles:type_name -> tccGraphicData.TccHandle
|
||||
7, // 5: tccGraphicData.TccButton.common:type_name -> graphicData.CommonInfo
|
||||
7, // 6: tccGraphicData.TccText.common:type_name -> graphicData.CommonInfo
|
||||
7, // 7: tccGraphicData.TccKey.common:type_name -> graphicData.CommonInfo
|
||||
0, // 8: tccGraphicData.TccKey.type:type_name -> tccGraphicData.TccKeyType
|
||||
7, // 9: tccGraphicData.TccHandle.common:type_name -> graphicData.CommonInfo
|
||||
10, // [10:10] is the sub-list for method output_type
|
||||
10, // [10:10] is the sub-list for method input_type
|
||||
10, // [10:10] is the sub-list for extension type_name
|
||||
10, // [10:10] is the sub-list for extension extendee
|
||||
0, // [0:10] is the sub-list for field type_name
|
||||
8, // 0: tccGraphicData.TccGraphicStorage.canvas:type_name -> graphicData.Canvas
|
||||
3, // 1: tccGraphicData.TccGraphicStorage.tccButtons:type_name -> tccGraphicData.TccButton
|
||||
4, // 2: tccGraphicData.TccGraphicStorage.tccTexts:type_name -> tccGraphicData.TccText
|
||||
5, // 3: tccGraphicData.TccGraphicStorage.tccKeys:type_name -> tccGraphicData.TccKey
|
||||
6, // 4: tccGraphicData.TccGraphicStorage.tccHandles:type_name -> tccGraphicData.TccHandle
|
||||
7, // 5: tccGraphicData.TccGraphicStorage.tccLights:type_name -> tccGraphicData.TccLight
|
||||
9, // 6: tccGraphicData.TccButton.common:type_name -> graphicData.CommonInfo
|
||||
9, // 7: tccGraphicData.TccText.common:type_name -> graphicData.CommonInfo
|
||||
9, // 8: tccGraphicData.TccKey.common:type_name -> graphicData.CommonInfo
|
||||
1, // 9: tccGraphicData.TccKey.type:type_name -> tccGraphicData.TccKey.TccKeyType
|
||||
9, // 10: tccGraphicData.TccHandle.common:type_name -> graphicData.CommonInfo
|
||||
9, // 11: tccGraphicData.TccLight.common:type_name -> graphicData.CommonInfo
|
||||
0, // 12: tccGraphicData.TccLight.lightColor:type_name -> tccGraphicData.TccElementColor
|
||||
13, // [13:13] is the sub-list for method output_type
|
||||
13, // [13:13] is the sub-list for method input_type
|
||||
13, // [13:13] is the sub-list for extension type_name
|
||||
13, // [13:13] is the sub-list for extension extendee
|
||||
0, // [0:13] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_tccGraphics_proto_init() }
|
||||
@ -584,14 +751,26 @@ func file_tccGraphics_proto_init() {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_tccGraphics_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*TccLight); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
type x struct{}
|
||||
out := protoimpl.TypeBuilder{
|
||||
File: protoimpl.DescBuilder{
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: file_tccGraphics_proto_rawDesc,
|
||||
NumEnums: 1,
|
||||
NumMessages: 5,
|
||||
NumEnums: 2,
|
||||
NumMessages: 6,
|
||||
NumExtensions: 0,
|
||||
NumServices: 0,
|
||||
},
|
||||
|
@ -40,6 +40,7 @@ type RunConfigSelectOption struct {
|
||||
}
|
||||
|
||||
type TrainConnTypeConfigDto struct {
|
||||
TypeName string `json:"typeName" form:"typeName"` // 连接名称; //连接名称
|
||||
ConnType state_proto.TrainConnState_TrainConnType `json:"connType" form:"connType"` // NONE = 0 未知连接 ;VOBC = 1; //半实物;PC_SIM = 2; //PC仿真
|
||||
}
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -71,7 +71,13 @@ type AddTrainReqDtoNew struct {
|
||||
//车头所在link内的偏移量,单位为mm
|
||||
HeadOffset int64 `json:"headOffset" form:"headOffset"`
|
||||
//列车长度
|
||||
TrainLength int64 `json:"trainLength" from:"trainLength"`
|
||||
TrainLength int64 `json:"trainLength" from:"trainLength"`
|
||||
TrainLoad int32 `json:"trainLoad" from:"trainLoad"`
|
||||
TrainMaxSpeed float32 `json:"trainMaxSpeed" from:"trainMaxSpeed"`
|
||||
TrainMaxAcc float32 `json:"trainMaxAcc" from:"trainMaxAcc"`
|
||||
TrainMaxBrake float32 `json:"trainMaxBrake" from:"trainMaxBrake"`
|
||||
TrainEmergencyBrake float32 `json:"trainEmergencyBrake" from:"trainEmergencyBrake"`
|
||||
TrainCoachNum uint32 `json:"trainCoachNum" from:"trainCoachNum"`
|
||||
// 场景ID
|
||||
MapId int32 `json:"mapId" from:"mapId"`
|
||||
WheelDiameter int32 `json:"wheelDiameter" from:"wheelDiameter"`
|
||||
@ -99,14 +105,15 @@ type ConfigTrainEnds struct {
|
||||
SpeedSensorEnableA bool `json:"speedSensorEnableA"` // 2端速度传感器是否有效
|
||||
SpeedSensorEnableB bool `json:"speedSensorEnableB"` // 2端速度传感器是否有效
|
||||
RadarEnable bool `json:"radarEnable"` // 雷达是否有效
|
||||
RadarCheckSpeedDiff float32 `json:"radarCheckSpeedDiff"` // 雷达测速数值
|
||||
RadarCheckSpeedDiff float32 `json:"radarCheckSpeedDiff"` // 雷达测速数值(千米/小时)
|
||||
RadarCheckTime int32 `json:"radarCheckTime"` // 雷达检测时间(秒)
|
||||
|
||||
AccEnable bool `json:"accEnable"` // 加速计是否有效
|
||||
AccCheckSpeedDiff float32 `json:"accCheckSpeedDiff"` // 加速计速度差
|
||||
AccCheckTime int32 `json:"accCheckTime"` // 加速计储蓄时间
|
||||
AccOutSpeed int32 `json:"accOutSpeed"` // 速传速度输出
|
||||
RadarOutSpeed int32 `json:"radarOutSpeed"` // 雷达速度输出
|
||||
AccOutSpeed int32 `json:"accOutSpeed"` // 速传速度输出(千米/小时)
|
||||
RadarOutSpeed int32 `json:"radarOutSpeed"` // 雷达速度输出(千米/小时)
|
||||
|
||||
}
|
||||
type ConfigTrainData struct {
|
||||
//Mass int32 `json:"mass" form:"mass"` // 列车的质量(100=1ton)
|
||||
@ -151,6 +158,7 @@ type TrainConnThirdDto struct {
|
||||
SimulationId string `json:"simulationId" form:"simulationId"`
|
||||
Id string `json:"id" form:"id"` // 列车Id
|
||||
ConnType state_proto.TrainConnState_TrainConnType `json:"connType" form:"connType"` //连接类型 0=未连接;1=半实物;2= 车载仿真
|
||||
TypeName string `json:"typeName" form:"typeName"` //连接名称
|
||||
}
|
||||
|
||||
// 为仿真添加测试车请求
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -17,11 +17,16 @@ type TrainInfoReqDto struct {
|
||||
}
|
||||
|
||||
type TrainInfoDto struct {
|
||||
Id int32 `json:"id" form:"id"`
|
||||
Name string `json:"name" form:"name"`
|
||||
TrainModel int32 `json:"train_model" form:"train_model"`
|
||||
CarriageLength int32 `json:"carriage_length" form:"carriage_length"`
|
||||
TotalLength int32 `json:"total_length" form:"total_length"`
|
||||
Id int32 `json:"id" form:"id"`
|
||||
Name string `json:"name" form:"name"`
|
||||
TrainModel int32 `json:"train_model" form:"train_model"`
|
||||
CarriageLength int32 `json:"carriage_length" form:"carriage_length"`
|
||||
TotalLength int32 `json:"total_length" form:"total_length"`
|
||||
TrainLoad int32 `json:"train_load" form:"train_load"`
|
||||
TrainMaxSpeed float32 `json:"train_max_speed" form:"train_max_speed"`
|
||||
TrainMaxAcc float32 `json:"train_max_acc" form:"train_max_acc"`
|
||||
TrainMaxBrake float32 `json:"train_max_brake" form:"train_max_brake"`
|
||||
TrainEmergencyBrake float32 `json:"train_emergency_brake" form:"train_emergency_brake"`
|
||||
//MinDiameter int32 `json:"min_diameter" form:"min_diameter"`
|
||||
//MaxDiameter int32 `json:"max_diameter" form:"max_diameter"`
|
||||
TrainSets string `json:"train_sets" form:"train_sets"`
|
||||
@ -53,13 +58,18 @@ func ConvertDtoFromTrain(t *PublishedDto) *TrainInfoDto {
|
||||
message := &data_proto.Train{}
|
||||
proto.Unmarshal(t.Proto, message)
|
||||
return &TrainInfoDto{
|
||||
Id: t.ID,
|
||||
Name: t.Name,
|
||||
Description: t.Note,
|
||||
TrainModel: int32(message.TrainModel),
|
||||
CarriageLength: message.CarriageLength,
|
||||
TotalLength: message.TotalLength,
|
||||
TrainControlMapId: message.TrainControlMapId,
|
||||
Id: t.ID,
|
||||
Name: t.Name,
|
||||
Description: t.Note,
|
||||
TrainModel: int32(message.TrainModel),
|
||||
CarriageLength: message.CarriageLength,
|
||||
TotalLength: message.TotalLength,
|
||||
TrainControlMapId: message.TrainControlMapId,
|
||||
TrainLoad: message.TrainLoad,
|
||||
TrainMaxSpeed: message.TrainMaxSpeed,
|
||||
TrainMaxAcc: message.TrainMaxAcc,
|
||||
TrainMaxBrake: message.TrainMaxBrake,
|
||||
TrainEmergencyBrake: message.TrainEmergencyBrake,
|
||||
//TrainControlMapCode: message.TrainControlMapCode,
|
||||
//MinDiameter: message.MinDiameter,
|
||||
//MaxDiameter: message.MaxDiameter,
|
||||
|
406
example/balise_1023_830/main.go
Normal file
406
example/balise_1023_830/main.go
Normal file
@ -0,0 +1,406 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"strconv"
|
||||
|
||||
"joylink.club/bj-rtsts-server/third_party/balisecodec"
|
||||
)
|
||||
|
||||
type Transponder struct {
|
||||
// 应答器编号
|
||||
Code int
|
||||
// 应答器名称
|
||||
Name string
|
||||
// 应答器类型
|
||||
Type string
|
||||
// 校验码
|
||||
CheckSum string
|
||||
// 830报文
|
||||
Msg830 string
|
||||
// 1023报文
|
||||
Msg1023 string
|
||||
}
|
||||
|
||||
func main() {
|
||||
transponderMap := ReadTransponders()
|
||||
// v := transponderMap["FB121_BGZ"]
|
||||
// b1023 := convertToBalise1023(v.Msg1023)
|
||||
// replaced830 := replaceFirst10Bits(convertTo830(v.Msg830))
|
||||
// scrambled830 := scrambling(replaced830, b1023.S)
|
||||
// compare830(scrambled830, b1023.data830)
|
||||
|
||||
// count := 0
|
||||
for _, v := range transponderMap {
|
||||
// slog.Info("transponder", "name", v.Name, "code", v.Code, "checksum", v.CheckSum, "msg830", v.Msg830, "msg1023", v.Msg1023)
|
||||
byte104, err := balisecodec.DecodeByteString(v.Msg1023)
|
||||
if err != nil {
|
||||
slog.Error("解码应答器数据失败", "name", v.Name, "code", v.Code, "err", err)
|
||||
continue
|
||||
}
|
||||
compare104Bytes(byte104, balisecodec.ConvertByteStringToBytes(v.Msg830))
|
||||
// b1023 := convertToBalise1023(v.Msg1023)
|
||||
// source830 := convertTo830(v.Msg830)
|
||||
// // replaced830 := replaceFirst10Bits(source830)
|
||||
// // compare830(reverted830, source830)
|
||||
// descrambled830 := descrambling(b1023.data830, b1023.S)
|
||||
// reverted830 := revertFirst10Bits(descrambled830)
|
||||
// // // scrambled830 := scrambling(replaced830, b1023.S)
|
||||
// compare830(source830, reverted830)
|
||||
// count++
|
||||
// if count >= 10 {
|
||||
// break
|
||||
// }
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// 比较830位数据
|
||||
func compare104Bytes(bytes104 []byte, compare104 []byte) {
|
||||
if len(bytes104) != 104 {
|
||||
panic("invalid length")
|
||||
}
|
||||
for i := 0; i < 104; i++ {
|
||||
fmt.Printf("%02x\n", bytes104[i])
|
||||
fmt.Printf("%02x\n\n", compare104[i])
|
||||
if bytes104[i] != compare104[i] {
|
||||
slog.Info("error", "index", i, "bytes104", fmt.Sprintf("%02x", bytes104[i]), "compare", fmt.Sprintf("%02x", compare104[i]))
|
||||
panic("104 bytes compare error")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type Balise1023 struct {
|
||||
bits []byte // 1023报文,1023bit
|
||||
data []byte // 数据位,913bit
|
||||
data830 []byte // 830位数据
|
||||
cb []byte // 控制位,3bit
|
||||
sb []byte // 加扰位,12bit
|
||||
S uint32 // 由加扰计算得到的S,作为初始状态为S的32位线性反馈移位寄存器对数据进行加扰
|
||||
esb []byte // 额外修正位,10bit
|
||||
checkSum []byte // 校验位,85bit
|
||||
}
|
||||
|
||||
func newBalise1023(bits []byte) *Balise1023 {
|
||||
if len(bits) != 1023 {
|
||||
panic("invalid length")
|
||||
}
|
||||
// for i := 0; i < 1023; i++ {
|
||||
// if i%10 == 0 {
|
||||
// println()
|
||||
// }
|
||||
// print(1022-i, ":", bits[i], ",")
|
||||
// }
|
||||
// println()
|
||||
balise1023 := &Balise1023{
|
||||
bits: bits,
|
||||
}
|
||||
balise1023.data = balise1023.getRange(1022, 110)
|
||||
if len(balise1023.data) != 913 {
|
||||
panic("invalid data length")
|
||||
}
|
||||
balise1023.data830 = convert913To830(balise1023.data)
|
||||
balise1023.check11To10()
|
||||
balise1023.cb = balise1023.getRange(109, 107)
|
||||
for i, v := range balise1023.cb {
|
||||
n := 109 - i
|
||||
name := "b" + strconv.Itoa(109-i)
|
||||
slog.Info("cb", name, v)
|
||||
if n == 109 && v == 1 {
|
||||
slog.Error("控制位cb错误:cb109应该为0,实际为1")
|
||||
} else if n == 108 && v == 1 {
|
||||
slog.Error("控制位cb错误:cb108应该为0,实际为1")
|
||||
} else if n == 107 && v == 0 {
|
||||
slog.Error("控制位cb错误:cb107应该为1,实际为0")
|
||||
}
|
||||
}
|
||||
balise1023.sb = balise1023.getRange(106, 95)
|
||||
balise1023.S = calculateS(balise1023.sb)
|
||||
balise1023.esb = balise1023.getRange(94, 85)
|
||||
balise1023.checkSum = balise1023.getRange(84, 0)
|
||||
slog.Info("msg length", "datalen", len(balise1023.data), "cblen", len(balise1023.cb), "sblen", len(balise1023.sb), "esblen", len(balise1023.esb), "checkSumlen", len(balise1023.checkSum))
|
||||
return balise1023
|
||||
}
|
||||
|
||||
func (b *Balise1023) getRange(start, end int) []byte {
|
||||
if start < 0 || end < 0 || start < end || start > 1022 {
|
||||
panic("invalid range")
|
||||
}
|
||||
return b.bits[1022-start : 1022-(end-1)]
|
||||
}
|
||||
|
||||
func (b *Balise1023) check11To10() {
|
||||
b913 := b.data
|
||||
compare := convert830To913(b.data830)
|
||||
for i := 0; i < 913; i++ {
|
||||
if b913[i] != compare[i] {
|
||||
slog.Info("error", "idx", i, "b913", b913[i], "compare", compare[i])
|
||||
panic("10 to 11 bit error")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 转换字节字符串到bit数组,左边为最高有效位(MSB)
|
||||
func convertStringBytesToBits(msg string) []byte {
|
||||
length := len(msg)
|
||||
println("msg:", msg)
|
||||
bytes := make([]byte, length/2)
|
||||
for i := 0; i < length; i += 2 {
|
||||
v, err := strconv.ParseUint(msg[i:i+2], 16, 8)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
bytes[i/2] = byte(v)
|
||||
// slog.Info("i", "byteidx", i/2, "byte", fmt.Sprintf("%02x", v))
|
||||
}
|
||||
// 字节转换为bit数组
|
||||
bits := make([]byte, length/2*8)
|
||||
for i, bt := range bytes {
|
||||
for j := 0; j < 8; j++ {
|
||||
move := 7 - j
|
||||
idx := i*8 + j
|
||||
bits[idx] = (bt >> move) & 1
|
||||
}
|
||||
}
|
||||
return bits
|
||||
}
|
||||
|
||||
func convertTo830(msg string) []byte {
|
||||
length := len(msg)
|
||||
if length != 208 {
|
||||
panic("invalid length")
|
||||
}
|
||||
// 字节转换为bit数组
|
||||
bits := convertStringBytesToBits(msg)
|
||||
bits830 := bits[0:830]
|
||||
return bits830
|
||||
}
|
||||
|
||||
func convertToBalise1023(msg string) *Balise1023 {
|
||||
length := len(msg)
|
||||
if length != 256 {
|
||||
panic("invalid length")
|
||||
}
|
||||
// 字节转换为bit数组
|
||||
bits := convertStringBytesToBits(msg)
|
||||
bits1023 := bits[0:1023]
|
||||
slog.Info("bits length", "len", len(bits1023))
|
||||
return newBalise1023(bits1023)
|
||||
}
|
||||
|
||||
// 将830位的二进制数组以10位为单位组成一个左边为最高有效位(MSB)的无符号整数数组,除了第一个10位值,其余值求和,然后循环2的10次方次与其他值求和结果相加后模2的10次方,若结果和第一个10位值相同,则结束,此值即为原始的第一个10位值,将此值替换为第一个10位二进制数组,依然是左边为MSB
|
||||
func revertFirst10Bits(b []byte) []byte {
|
||||
if len(b) != 830 {
|
||||
panic("invalid length")
|
||||
}
|
||||
// 将830位的二进制数组以10位为单位组成一个左边为最高有效位(MSB)的无符号整数数组
|
||||
bits := make([]uint16, 83)
|
||||
for i := 0; i < 83; i++ {
|
||||
bits[i] = uint16(balisecodec.ToValLeftMsb(b[i*10 : i*10+10]))
|
||||
// 打印输出
|
||||
for j := 0; j < 10; j++ {
|
||||
fmt.Printf("%01b", b[i*10+j])
|
||||
}
|
||||
print(" ")
|
||||
if i != 0 && i%10 == 9 {
|
||||
println()
|
||||
}
|
||||
}
|
||||
println()
|
||||
// 将除了第一个10位字整数求和
|
||||
sum := uint64(0)
|
||||
for i := 1; i < 83; i++ {
|
||||
sum += uint64(bits[i])
|
||||
}
|
||||
// 循环2的10次方次与其他值求和结果相加后模2的10次方
|
||||
for i := 0; i < 1024; i++ {
|
||||
test := sum + uint64(i)
|
||||
if test%1024 == uint64(bits[0]) {
|
||||
bits[0] = uint16(i)
|
||||
break
|
||||
}
|
||||
}
|
||||
slog.Info("还原第一个10位值", "sum", sum, "bits[0]", bits[0], "bits[0]b", fmt.Sprintf("%010b", bits[0]))
|
||||
rbits := make([]byte, 830)
|
||||
// 将整个10位数组转换为二进制数组,依然是MSB
|
||||
u0bits := balisecodec.ToBitsLeftMsb(int(bits[0]), 10)
|
||||
for i := 0; i < 10; i++ {
|
||||
rbits[i] = u0bits[i]
|
||||
}
|
||||
for i := 10; i < 830; i++ {
|
||||
rbits[i] = b[i]
|
||||
}
|
||||
// compare830(b, rbits)
|
||||
return rbits
|
||||
}
|
||||
|
||||
// 将830位的二进制数组以10位为单位组成一个左边为最高有效位(MSB)的无符号整数数组,然后求和后模2的10次方,得到的结果覆盖第一个10位值,然后将整个10位数组转换为二进制数组,依然是左边为MSB
|
||||
func replaceFirst10Bits(b []byte) []byte {
|
||||
if len(b) != 830 {
|
||||
panic("invalid length")
|
||||
}
|
||||
// 将830位的二进制数组以10位为单位组成一个左边为最高有效位(MSB)的无符号整数数组
|
||||
bits := make([]uint16, 83)
|
||||
for i := 0; i < 83; i++ {
|
||||
bits[i] = uint16(balisecodec.ToValLeftMsb(b[i*10 : i*10+10]))
|
||||
// 打印输出
|
||||
for j := 0; j < 10; j++ {
|
||||
fmt.Printf("%01b", b[i*10+j])
|
||||
}
|
||||
print(" ")
|
||||
if i != 0 && i%10 == 9 {
|
||||
println()
|
||||
}
|
||||
}
|
||||
println()
|
||||
// 将每一个10位字整数求和后模2的10次方,得到的结果覆盖第一个10位值
|
||||
sum := uint64(0)
|
||||
for i := 0; i < 83; i++ {
|
||||
sum += uint64(bits[i])
|
||||
// fmt.Printf("i=%d, v10=%d, v10b=%010b\n", i, bits[i], bits[i])
|
||||
}
|
||||
bits[0] = uint16(sum % 1024)
|
||||
slog.Info("替换第一个10位值", "sum", sum, "bits[0]", bits[0], "bits[0]b", fmt.Sprintf("%010b", bits[0]))
|
||||
rbits := make([]byte, 830)
|
||||
// 将整个10位数组转换为二进制数组,依然是MSB
|
||||
u0bits := balisecodec.ToBitsLeftMsb(int(bits[0]), 10)
|
||||
for i := 0; i < 10; i++ {
|
||||
rbits[i] = u0bits[i]
|
||||
}
|
||||
for i := 10; i < 830; i++ {
|
||||
rbits[i] = b[i]
|
||||
}
|
||||
// compare830(b, rbits)
|
||||
return rbits
|
||||
}
|
||||
|
||||
// 由加扰位计算得到的S,作为初始状态为S的32位线性反馈移位寄存器对数据进行加扰
|
||||
func calculateS(sb []byte) uint32 {
|
||||
// 由加扰计算得到的S,作为初始状态为S的32位线性反馈移位寄存器对数据进行加扰
|
||||
if len(sb) != 12 {
|
||||
panic("invalid length")
|
||||
}
|
||||
B := balisecodec.ToValLeftMsb(sb)
|
||||
const A uint64 = 2801775573
|
||||
S := uint32((A * uint64(B)) % (1 << 32))
|
||||
slog.Info("由12位加扰位计算得到整数S", "B", B, "S", S, "Sb", fmt.Sprintf("%032b", S))
|
||||
return S
|
||||
}
|
||||
|
||||
// 由加扰计算得到的S,作为初始状态为S的32位线性反馈移位寄存器对数据进行加扰
|
||||
// 1. 生成一个32位的线性反馈移位寄存器,其初始状态为S(左边为MSB)
|
||||
// 2. 系数h31,h30,h29,h27,h25和h0等于1(表示连接),所有其他系数都为0(表示不连接)
|
||||
// 3. 然后电路被时钟驱动m-1次,其中m是数据位的数量,同时输入dn的每一位dn(m-1),dn(m-2),...,dn(0),便生成加扰后的码位(在第一个时钟之前读取第一个输出out(m-1))
|
||||
// 4. 生成的加扰码位是dn的每一位与S的最高位的异或值
|
||||
// 5. 生成的加扰码位会根据系数进行异或反馈回S的最低位
|
||||
// 几种可能性:
|
||||
func scrambling(dn []byte, S uint32) []byte {
|
||||
if len(dn) != 830 {
|
||||
panic("invalid length")
|
||||
}
|
||||
// const Polynomial = 0x000000AF
|
||||
out := make([]byte, len(dn))
|
||||
t := S // 寄存器初始值
|
||||
for i := 0; i < len(dn); i++ {
|
||||
msb := (t >> 31) & 1
|
||||
out[i] = (dn[i] ^ byte(msb)) & 1
|
||||
// fmt.Printf("i=%d, t=%032b, msb=%d, dn=%d, out=%d\n", i, t, msb, dn[i], out[i])
|
||||
xor := uint32(out[i])
|
||||
t = (xor << 30) ^ (xor << 29) ^ (xor << 28) ^ (xor << 26) ^ (xor << 24) ^ t
|
||||
t = (t << 1) | xor
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func descrambling(dn []byte, S uint32) []byte {
|
||||
if len(dn) != 830 {
|
||||
panic("invalid length")
|
||||
}
|
||||
// const Polynomial = 0x000000AF
|
||||
out := make([]byte, len(dn))
|
||||
t := S // 寄存器初始值
|
||||
for i := 0; i < len(dn); i++ {
|
||||
msb := (t >> 31) & 1
|
||||
out[i] = (dn[i] ^ byte(msb)) & 1
|
||||
// fmt.Printf("i=%d, t=%032b, msb=%d, dn=%d, out=%d\n", i, t, msb, dn[i], out[i])
|
||||
xor := uint32(dn[i])
|
||||
t = (xor << 30) ^ (xor << 29) ^ (xor << 28) ^ (xor << 26) ^ (xor << 24) ^ t
|
||||
t = (t << 1) | xor
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// 将830位的二进制数组先以10位为一组分别转换为11位并组合
|
||||
func convert830To913(b830 []byte) []byte {
|
||||
if len(b830) != 830 {
|
||||
panic("invalid length")
|
||||
}
|
||||
b913 := make([]byte, 913)
|
||||
for i := 0; i < 83; i++ {
|
||||
b11 := balisecodec.To11(b830[i*10 : i*10+10])
|
||||
for j := 0; j < 11; j++ {
|
||||
b913[i*11+j] = b11[j]
|
||||
}
|
||||
}
|
||||
return b913
|
||||
}
|
||||
|
||||
func convert913To830(b913 []byte) []byte {
|
||||
if len(b913) != 913 {
|
||||
panic("invalid length")
|
||||
}
|
||||
b830 := make([]byte, 830)
|
||||
for i := 0; i < 83; i++ {
|
||||
b10, err := balisecodec.From11(b913[i*11 : i*11+11])
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
for j := 0; j < 10; j++ {
|
||||
b830[i*10+j] = b10[j]
|
||||
}
|
||||
}
|
||||
return b830
|
||||
}
|
||||
|
||||
func compare830(b830 []byte, compare830 []byte) {
|
||||
if len(b830) != 830 {
|
||||
panic("invalid length")
|
||||
}
|
||||
for i := 0; i < 83; i++ {
|
||||
for j := 0; j < 10; j++ {
|
||||
fmt.Printf("%01b", b830[i*10+j])
|
||||
}
|
||||
println()
|
||||
for j := 0; j < 10; j++ {
|
||||
fmt.Printf("%01b", compare830[i*10+j])
|
||||
}
|
||||
println()
|
||||
println()
|
||||
}
|
||||
for i := 0; i < 830; i++ {
|
||||
if b830[i] != compare830[i] {
|
||||
slog.Info("error", "index", i, "b830", b830[i], "compare", compare830[i])
|
||||
panic("830 bit compare error")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 以11位为一组比较两个913位的报文
|
||||
func compare913(b913 []byte, b1023 *Balise1023) {
|
||||
if len(b913) != 913 {
|
||||
panic("invalid length")
|
||||
}
|
||||
compare := b1023.data
|
||||
for i := 0; i < 913; i += 11 {
|
||||
for j := 0; j < 11; j++ {
|
||||
print(b913[i+j])
|
||||
}
|
||||
println()
|
||||
for j := 0; j < 11; j++ {
|
||||
print(compare[i+j])
|
||||
}
|
||||
println()
|
||||
println()
|
||||
}
|
||||
}
|
156
example/balise_1023_830/read_excel.go
Normal file
156
example/balise_1023_830/read_excel.go
Normal file
@ -0,0 +1,156 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"log/slog"
|
||||
"runtime/debug"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/xuri/excelize/v2"
|
||||
)
|
||||
|
||||
const (
|
||||
fileName = "北岗子-应答器报文清单.xlsx"
|
||||
sheetName = "应答器报文清单"
|
||||
codeColumn = "应答器编号"
|
||||
nameColumn = "应答器名称"
|
||||
typeColumn = "类型"
|
||||
checkSumColumn = "校验码"
|
||||
msg830strColumn = "用户报文(830bits)"
|
||||
msg1023strColumn = "报文(1023bits)"
|
||||
)
|
||||
|
||||
var heads []string
|
||||
|
||||
func init() {
|
||||
initHeads()
|
||||
}
|
||||
|
||||
func initHeads() {
|
||||
heads = append(heads, codeColumn)
|
||||
heads = append(heads, nameColumn)
|
||||
heads = append(heads, typeColumn)
|
||||
heads = append(heads, checkSumColumn)
|
||||
heads = append(heads, msg830strColumn)
|
||||
heads = append(heads, msg1023strColumn)
|
||||
}
|
||||
|
||||
func buildHeadIndex(row []string) map[string]int {
|
||||
headIdx := make(map[string]int)
|
||||
for i, column := range row {
|
||||
column = HandleStringSpace(column)
|
||||
if column == codeColumn {
|
||||
headIdx[column] = i
|
||||
} else if column == nameColumn {
|
||||
headIdx[column] = i
|
||||
} else if column == typeColumn {
|
||||
headIdx[column] = i
|
||||
} else if column == checkSumColumn {
|
||||
headIdx[column] = i
|
||||
} else if column == msg830strColumn {
|
||||
headIdx[column] = i
|
||||
} else if column == msg1023strColumn {
|
||||
headIdx[column] = i
|
||||
}
|
||||
}
|
||||
// 检查headIndex是否完整
|
||||
if len(headIdx) <= 0 {
|
||||
return nil
|
||||
}
|
||||
checkHeadsIndex(headIdx, fileName, sheetName, heads)
|
||||
return headIdx
|
||||
}
|
||||
|
||||
func checkHeadsIndex(headIdx map[string]int, fileName, sheetName string, heads []string) {
|
||||
// 检查headIndex是否完整
|
||||
for _, v := range heads {
|
||||
if _, ok := headIdx[v]; !ok {
|
||||
slog.Error("表头缺失", "文件名", fileName, "SheetName", sheetName, "表头", v)
|
||||
panic("课时表头缺失")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func ReadTransponders() map[string]Transponder {
|
||||
return readExcel(fileName, sheetName, readRows)
|
||||
}
|
||||
|
||||
func readExcel[T any](fileName, sheetName string, handle func(rows [][]string) map[string]T) map[string]T {
|
||||
f, err := excelize.OpenFile(fileName)
|
||||
if err != nil {
|
||||
slog.Error("打开表文件异常", "表名", fileName, "error", err)
|
||||
debug.PrintStack()
|
||||
panic("打开表文件异常")
|
||||
}
|
||||
defer func() {
|
||||
if err := f.Close(); err != nil {
|
||||
slog.Error("文件关闭异常", "error", err)
|
||||
}
|
||||
}()
|
||||
|
||||
rows, err := f.GetRows(sheetName)
|
||||
if err != nil {
|
||||
slog.Error("读取Sheet异常", "SheetName", sheetName, "error", err)
|
||||
panic(err)
|
||||
}
|
||||
// fmt.Println(rows)
|
||||
return handle(rows)
|
||||
}
|
||||
|
||||
func readRows(rows [][]string) map[string]Transponder {
|
||||
dataMap := make(map[string]Transponder)
|
||||
var headIdx map[string]int
|
||||
for _, row := range rows {
|
||||
if headIdx == nil {
|
||||
headIdx = buildHeadIndex(row)
|
||||
// if headIdx != nil {
|
||||
// slog.Info("读取到表头索引", "文件名", fileName, "表名", sheetName, "索引", headIdx)
|
||||
// }
|
||||
} else {
|
||||
rowSize := len(row)
|
||||
if rowSize <= 0 {
|
||||
continue
|
||||
}
|
||||
if rowSize <= headIdx[msg1023strColumn] {
|
||||
// slog.Info("非数据行", "row", row, "rowIndex", i)
|
||||
continue
|
||||
}
|
||||
codeStr := row[headIdx[codeColumn]]
|
||||
if codeStr == "" {
|
||||
continue
|
||||
}
|
||||
codeStr = HandleStringSpace(codeStr)
|
||||
code, err := strconv.ParseInt(codeStr, 10, 32)
|
||||
if err != nil {
|
||||
slog.Error("应答器编号错误", "编号", codeStr, "error", err)
|
||||
panic("应答器编号错误")
|
||||
}
|
||||
name := row[headIdx[nameColumn]]
|
||||
if name == "" {
|
||||
continue
|
||||
}
|
||||
name = HandleStringSpace(name)
|
||||
tp := row[headIdx[typeColumn]]
|
||||
checkSum := row[headIdx[checkSumColumn]]
|
||||
msg830 := row[headIdx[msg830strColumn]]
|
||||
msg1023 := row[headIdx[msg1023strColumn]]
|
||||
dataMap[name] = Transponder{
|
||||
Code: int(code),
|
||||
Name: name,
|
||||
Type: tp,
|
||||
CheckSum: checkSum,
|
||||
Msg830: msg830,
|
||||
Msg1023: msg1023,
|
||||
}
|
||||
}
|
||||
}
|
||||
slog.Info("读取结果", "文件名", fileName, "SheetName", sheetName, "总数", len(dataMap))
|
||||
// fmt.Println(dataMap)
|
||||
return dataMap
|
||||
}
|
||||
|
||||
func HandleStringSpace(s string) string {
|
||||
s = strings.ReplaceAll(s, " ", "")
|
||||
s = strings.ReplaceAll(s, "\n", "")
|
||||
return s
|
||||
}
|
32
example/ex1/main.go
Normal file
32
example/ex1/main.go
Normal file
@ -0,0 +1,32 @@
|
||||
package main
|
||||
|
||||
import "log/slog"
|
||||
|
||||
func main() {
|
||||
a1 := calculateAvgAcc(136960.88, 24.41978)
|
||||
a2 := calculateAvgAcc(34874, 14.97515)
|
||||
slog.Info("根据位移和时间计算平均加速度", "通号平均加速度", a1, "动力学平均加速度", a2)
|
||||
avt1 := calculateAvgAccByV(6.94444, 24.41978)
|
||||
avt2 := calculateAvgAccByV(6.94444, 14.97515)
|
||||
slog.Info("根据速度和时间计算平均加速度", "通号平均加速度", avt1, "动力学平均加速度", avt2)
|
||||
s1 := calculateS(0.28437766432762146, 24.41978)
|
||||
s2 := calculateS(0.46373090147972107, 14.97515)
|
||||
slog.Info("根据加速度和时间计算位移", "通号最小位移", s1, "动力学最小位移", s2)
|
||||
slog.Info("实际位移", "通号实际位移差", 136960.88-s1, "动力学实际位移差", 34874-s2)
|
||||
}
|
||||
|
||||
// 根据位移和时间计算平均加速度(初始速度为0)
|
||||
// s单位为mm,t单位为s
|
||||
func calculateAvgAcc(s float32, t float32) float32 {
|
||||
return 2 * s / (t * t) / 1000
|
||||
}
|
||||
|
||||
// 根据速度和时间计算平均加速度(初始速度为0)
|
||||
func calculateAvgAccByV(v float32, t float32) float32 {
|
||||
return v / t
|
||||
}
|
||||
|
||||
// 根据加速度和时间计算位移(初始速度为0)
|
||||
func calculateS(a float32, t float32) float32 {
|
||||
return 0.5 * a * t * t * 1000
|
||||
}
|
56
example/lfsr/main.go
Normal file
56
example/lfsr/main.go
Normal file
@ -0,0 +1,56 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log/slog"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// fib_lfsr()
|
||||
galois_lfsr()
|
||||
}
|
||||
|
||||
/* taps: 16 14 13 11; feedback polynomial: x^16 + x^14 + x^13 + x^11 + 1 */
|
||||
func fib_lfsr() {
|
||||
startState := uint16(0xACE1)
|
||||
lfsr := startState
|
||||
var bit uint16
|
||||
period := uint64(0)
|
||||
for {
|
||||
slog.Info("fib_lfsr", "bit", fmt.Sprintf("%01b", lfsr&1), "lfsr", fmt.Sprintf("%016b", lfsr))
|
||||
bit = ((lfsr >> 0) ^ (lfsr >> 2) ^ (lfsr >> 3) ^ (lfsr >> 5)) & 1
|
||||
lfsr = (lfsr >> 1) | (bit << 15)
|
||||
period++
|
||||
if period == 15 {
|
||||
break
|
||||
}
|
||||
if lfsr == startState {
|
||||
break
|
||||
}
|
||||
}
|
||||
println(period)
|
||||
}
|
||||
|
||||
/* taps: 16 14 13 11; feedback polynomial: x^16 + x^14 + x^13 + x^11 + 1 */
|
||||
func galois_lfsr() {
|
||||
startState := uint16(0xACE1)
|
||||
lfsr := startState
|
||||
var bit uint16
|
||||
period := uint64(0)
|
||||
for {
|
||||
slog.Info("galois_lfsr", "bit", fmt.Sprintf("%01b", lfsr&1), "lfsr", fmt.Sprintf("%016b", lfsr))
|
||||
bit = lfsr & 1
|
||||
lfsr >>= 1
|
||||
if bit == 1 {
|
||||
lfsr ^= 0xB400
|
||||
}
|
||||
period++
|
||||
// if period == 15 {
|
||||
// break
|
||||
// }
|
||||
if lfsr == startState {
|
||||
break
|
||||
}
|
||||
}
|
||||
println(period)
|
||||
}
|
52
example/test/main.go
Normal file
52
example/test/main.go
Normal file
@ -0,0 +1,52 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
)
|
||||
|
||||
func main() {
|
||||
d2, d1, d0 := encodeAcc(6.742071875)
|
||||
a := decode2Acc(d2, d1, d0)
|
||||
fmt.Println(a)
|
||||
}
|
||||
|
||||
const G = 9.80665
|
||||
|
||||
func encodeAcc(a float32) (d2, d1, d0 byte) {
|
||||
d2 = 0
|
||||
d1 = 0
|
||||
d0 = 0
|
||||
x := a / G
|
||||
v := uint32(0)
|
||||
for i := 17; i >= 0; i-- {
|
||||
t := float32(1.0 / math.Pow(2, float64(17-i)))
|
||||
if t > x {
|
||||
continue
|
||||
} else {
|
||||
v |= 1 << i
|
||||
x -= t
|
||||
}
|
||||
}
|
||||
fmt.Printf("%b, %b\n", v, v<<6)
|
||||
v <<= 6
|
||||
d0 = byte(v)
|
||||
d1 = byte(v >> 8)
|
||||
d2 = byte(v >> 16)
|
||||
fmt.Printf("%b, %b, %b\n", d2, d1, d0)
|
||||
return
|
||||
}
|
||||
|
||||
func decode2Acc(d2, d1, d0 byte) float32 {
|
||||
v := uint32(d2)<<10 | uint32(d1)<<2 | uint32(d0>>6)
|
||||
fmt.Printf("%b\n", v)
|
||||
x := float32(0)
|
||||
for i := 17; i >= 0; i-- {
|
||||
if v&(1<<i) != 0 {
|
||||
t := float32(1.0 / math.Pow(2, float64(17-i)))
|
||||
x += t
|
||||
}
|
||||
}
|
||||
fmt.Println(x)
|
||||
return x * G
|
||||
}
|
21
go.mod
21
go.mod
@ -4,10 +4,11 @@ go 1.21
|
||||
|
||||
require (
|
||||
github.com/appleboy/gin-jwt/v2 v2.9.1
|
||||
github.com/eclipse/paho.golang v0.12.0
|
||||
//github.com/eclipse/paho.golang v0.12.0
|
||||
github.com/eclipse/paho.golang v0.21.0
|
||||
github.com/gin-contrib/cors v1.4.0
|
||||
github.com/golang/protobuf v1.5.3
|
||||
github.com/google/uuid v1.4.0
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/sagikazarmark/slog-shim v0.1.0
|
||||
github.com/snksoft/crc v1.1.0
|
||||
github.com/spf13/viper v1.18.1
|
||||
@ -40,21 +41,25 @@ require (
|
||||
github.com/go-sql-driver/mysql v1.7.0 // indirect
|
||||
github.com/goburrow/serial v0.1.0 // indirect
|
||||
github.com/golang-jwt/jwt/v4 v4.4.3 // indirect
|
||||
github.com/gorilla/websocket v1.5.0 // indirect
|
||||
github.com/gorilla/websocket v1.5.1 // indirect
|
||||
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||
github.com/jinzhu/now v1.1.5 // indirect
|
||||
github.com/josharian/intern v1.0.0 // indirect
|
||||
github.com/mailru/easyjson v0.7.7 // indirect
|
||||
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
||||
github.com/richardlehane/mscfb v1.0.4 // indirect
|
||||
github.com/richardlehane/msoleps v1.0.3 // indirect
|
||||
github.com/sagikazarmark/locafero v0.4.0 // indirect
|
||||
github.com/simonvetter/modbus v1.6.0 // indirect
|
||||
github.com/sourcegraph/conc v0.3.0 // indirect
|
||||
github.com/xuri/efp v0.0.0-20231025114914-d1ff6096ae53 // indirect
|
||||
github.com/xuri/nfp v0.0.0-20230919160717-d98342af3f05 // indirect
|
||||
github.com/yohamta/donburi v1.3.9 // indirect
|
||||
go.uber.org/atomic v1.9.0 // indirect
|
||||
go.uber.org/multierr v1.9.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect
|
||||
golang.org/x/mod v0.12.0 // indirect
|
||||
golang.org/x/sync v0.5.0 // indirect
|
||||
golang.org/x/tools v0.13.0 // indirect
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect
|
||||
gorm.io/datatypes v1.1.1-0.20230130040222-c43177d3cf8c // indirect
|
||||
@ -92,11 +97,13 @@ require (
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||
github.com/ugorji/go/codec v1.2.11 // indirect
|
||||
golang.org/x/arch v0.3.0 // indirect
|
||||
golang.org/x/crypto v0.16.0 // indirect
|
||||
golang.org/x/net v0.19.0 // indirect
|
||||
golang.org/x/sys v0.15.0 // indirect
|
||||
golang.org/x/crypto v0.19.0 // indirect
|
||||
golang.org/x/net v0.21.0 // indirect
|
||||
golang.org/x/sys v0.17.0 // indirect
|
||||
golang.org/x/text v0.14.0 // indirect
|
||||
google.golang.org/protobuf v1.32.0
|
||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
||||
require github.com/xuri/excelize/v2 v2.8.1
|
||||
|
39
go.sum
39
go.sum
@ -17,8 +17,8 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/eclipse/paho.golang v0.12.0 h1:EXQFJbJklDnUqW6lyAknMWRhM2NgpHxwrrL8riUmp3Q=
|
||||
github.com/eclipse/paho.golang v0.12.0/go.mod h1:TSDCUivu9JnoR9Hl+H7sQMcHkejWH2/xKK1NJGtLbIE=
|
||||
github.com/eclipse/paho.golang v0.21.0 h1:cxxEReu+iFbA5RrHfRGxJOh8tXZKDywuehneoeBeyn8=
|
||||
github.com/eclipse/paho.golang v0.21.0/go.mod h1:GHF6vy7SvDbDHBguaUpfuBkEB5G6j0zKxMG4gbh6QRQ=
|
||||
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
|
||||
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
|
||||
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
|
||||
@ -83,10 +83,10 @@ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4=
|
||||
github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
|
||||
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY=
|
||||
github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY=
|
||||
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
|
||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||
github.com/jackc/chunkreader/v2 v2.0.1 h1:i+RDz65UE+mmpjTfyz0MoVTnzeYxroil2G82ki7MGG8=
|
||||
@ -153,6 +153,8 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw=
|
||||
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8=
|
||||
github.com/natefinch/lumberjack v2.0.0+incompatible h1:4QJd3OLAMgj7ph+yZTuX13Ld4UpgHp07nNdFX7mqFfM=
|
||||
github.com/natefinch/lumberjack v2.0.0+incompatible/go.mod h1:Wi9p2TTF5DG5oU+6YfsmYQpsTIOm0B1VNzQg9Mw6nPk=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||
@ -164,6 +166,11 @@ github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsK
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/richardlehane/mscfb v1.0.4 h1:WULscsljNPConisD5hR0+OyZjwK46Pfyr6mPu5ZawpM=
|
||||
github.com/richardlehane/mscfb v1.0.4/go.mod h1:YzVpcZg9czvAuhk9T+a3avCpcFPMUWm7gK3DypaEsUk=
|
||||
github.com/richardlehane/msoleps v1.0.1/go.mod h1:BWev5JBpU9Ko2WAgmZEuiz4/u3ZYTKbjLycmwiWUfWg=
|
||||
github.com/richardlehane/msoleps v1.0.3 h1:aznSZzrwYRl3rLKRT3gUk9am7T/mLNSnJINvN0AQoVM=
|
||||
github.com/richardlehane/msoleps v1.0.3/go.mod h1:BWev5JBpU9Ko2WAgmZEuiz4/u3ZYTKbjLycmwiWUfWg=
|
||||
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
|
||||
github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
|
||||
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
|
||||
@ -220,6 +227,12 @@ github.com/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6
|
||||
github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY=
|
||||
github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU=
|
||||
github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
|
||||
github.com/xuri/efp v0.0.0-20231025114914-d1ff6096ae53 h1:Chd9DkqERQQuHpXjR/HSV1jLZA6uaoiwwH3vSuF3IW0=
|
||||
github.com/xuri/efp v0.0.0-20231025114914-d1ff6096ae53/go.mod h1:ybY/Jr0T0GTCnYjKqmdwxyxn2BQf2RcQIIvex5QldPI=
|
||||
github.com/xuri/excelize/v2 v2.8.1 h1:pZLMEwK8ep+CLIUWpWmvW8IWE/yxqG0I1xcN6cVMGuQ=
|
||||
github.com/xuri/excelize/v2 v2.8.1/go.mod h1:oli1E4C3Pa5RXg1TBXn4ENCXDV5JUMlBluUhG7c+CEE=
|
||||
github.com/xuri/nfp v0.0.0-20230919160717-d98342af3f05 h1:qhbILQo1K3mphbwKh1vNm4oGezE1eF9fQWmNiIpSfI4=
|
||||
github.com/xuri/nfp v0.0.0-20230919160717-d98342af3f05/go.mod h1:WwHg+CVyzlv/TX9xqBFXEZAuxOPxn2k1GNHwG41IIUQ=
|
||||
github.com/yohamta/donburi v1.3.9 h1:sYAPaelSnxmoTGjgH9ZlYt4pUKrnwvAv4YGXxLZCK6E=
|
||||
github.com/yohamta/donburi v1.3.9/go.mod h1:5QkyraUjkzbMVTD2b8jaPFy1Uwjm/zdFN1c1lZGaezg=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
@ -237,10 +250,12 @@ golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5y
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.4.0/go.mod h1:3quD/ATkf6oY+rnes5c3ExXTbLc8mueNue5/DoinL80=
|
||||
golang.org/x/crypto v0.16.0 h1:mMMrFzRSCF0GvB7Ne27XVtVAaXLrPmgPC7/v0tkwHaY=
|
||||
golang.org/x/crypto v0.16.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
|
||||
golang.org/x/crypto v0.19.0 h1:ENy+Az/9Y1vSrlrvBSyna3PITt4tiZLf7sgCjZBX7Wo=
|
||||
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
|
||||
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g=
|
||||
golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k=
|
||||
golang.org/x/image v0.14.0 h1:tNgSxAFe3jC4uYqvZdTr84SZoM1KfwdC9SKIFrLjFn4=
|
||||
golang.org/x/image v0.14.0/go.mod h1:HUYqC05R2ZcZ3ejNQsIHQDQiwWM4JBqmm6MKANTp4LE=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc=
|
||||
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
@ -251,8 +266,8 @@ golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug
|
||||
golang.org/x/net v0.3.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE=
|
||||
golang.org/x/net v0.4.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE=
|
||||
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c=
|
||||
golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U=
|
||||
golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4=
|
||||
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE=
|
||||
@ -270,8 +285,8 @@ golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
|
||||
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y=
|
||||
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA=
|
||||
|
@ -176,6 +176,7 @@ github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
||||
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/gofuzz v1.0.0 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw=
|
||||
github.com/google/s2a-go v0.1.7 h1:60BLSyTrOV4/haCDW4zb1guZItoSq8foHCXrAnjBo/o=
|
||||
github.com/google/s2a-go v0.1.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8F0Qdw=
|
||||
@ -265,12 +266,17 @@ go.uber.org/zap v1.21.0 h1:WefMeulhovoZ2sYXz7st6K0sLj7bBhpiFaud4r4zST8=
|
||||
go.uber.org/zap v1.21.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw=
|
||||
golang.org/x/image v0.1.0 h1:r8Oj8ZA2Xy12/b5KZYj3tuv7NG/fBz3TwQVvpJ9l8Rk=
|
||||
golang.org/x/image v0.1.0/go.mod h1:iyPr49SD/G/TBxYVB/9RRtGUT5eNbo2u4NamWeQcD5c=
|
||||
golang.org/x/image v0.14.0/go.mod h1:HUYqC05R2ZcZ3ejNQsIHQDQiwWM4JBqmm6MKANTp4LE=
|
||||
golang.org/x/mobile v0.0.0-20221012134814-c746ac228303 h1:K4fp1rDuJBz0FCPAWzIJwnzwNEM7S6yobdZzMrZ/Zws=
|
||||
golang.org/x/mobile v0.0.0-20221012134814-c746ac228303/go.mod h1:M32cGdzp91A8Ex9qQtyZinr19EYxzkFqDjW2oyHzTDQ=
|
||||
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||
golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
|
||||
golang.org/x/oauth2 v0.15.0 h1:s8pnnxNVzjWyrvYdFUQq5llS1PX2zhPXmccZv99h7uQ=
|
||||
golang.org/x/oauth2 v0.15.0/go.mod h1:q48ptWNTY5XWf+JNten23lcvHpLJ0ZSxF5ttTHKVCAM=
|
||||
golang.org/x/term v0.15.0 h1:y/Oo/a/q3IXu26lQgl04j/gjuBDOBlx7X6Om1j2CPW4=
|
||||
golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0=
|
||||
golang.org/x/term v0.17.0 h1:mkTF7LCd6WGJNL3K1Ad7kwxNfYAW6a8a8QqtMblp/4U=
|
||||
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
|
||||
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
|
||||
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 h1:H2TDz8ibqkAF6YGhCdN3jS9O0/s90v0rJh3X/OLHEUk=
|
||||
|
@ -36,7 +36,11 @@ func InitSlog() {
|
||||
|
||||
if logging.Stdout {
|
||||
// 日志输出到控制台
|
||||
slog.SetDefault(slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{
|
||||
//slog.SetDefault(slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{
|
||||
// Level: level,
|
||||
// AddSource: false,
|
||||
//})))
|
||||
slog.SetDefault(slog.New(newMyJsonHandler(os.Stdout, &slog.HandlerOptions{
|
||||
Level: level,
|
||||
AddSource: false,
|
||||
})))
|
||||
@ -50,7 +54,11 @@ func InitSlog() {
|
||||
LocalTime: true, // 日志备份使用本地时间
|
||||
Compress: logging.Compress, // 是否压缩日志
|
||||
}
|
||||
slog.SetDefault(slog.New(slog.NewJSONHandler(lumberJackLogger, &slog.HandlerOptions{
|
||||
//slog.SetDefault(slog.New(slog.NewJSONHandler(lumberJackLogger, &slog.HandlerOptions{
|
||||
// Level: level,
|
||||
// AddSource: false,
|
||||
//})))
|
||||
slog.SetDefault(slog.New(newMyJsonHandler(lumberJackLogger, &slog.HandlerOptions{
|
||||
Level: level,
|
||||
AddSource: false,
|
||||
})))
|
||||
|
63
logger/my_json_handler.go
Normal file
63
logger/my_json_handler.go
Normal file
@ -0,0 +1,63 @@
|
||||
package logger
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"log/slog"
|
||||
"sync"
|
||||
)
|
||||
|
||||
type MyJsonHandler struct {
|
||||
output io.Writer
|
||||
mu sync.Mutex
|
||||
jsonHandler slog.Handler
|
||||
}
|
||||
|
||||
var stackChan = make(chan string, 100)
|
||||
|
||||
func newMyJsonHandler(output io.Writer, opts *slog.HandlerOptions) *MyJsonHandler {
|
||||
if opts == nil {
|
||||
opts = &slog.HandlerOptions{}
|
||||
}
|
||||
ra := opts.ReplaceAttr
|
||||
opts.ReplaceAttr = func(groups []string, a slog.Attr) slog.Attr {
|
||||
if a.Key == "stack" {
|
||||
stackChan <- a.Value.String()
|
||||
return slog.Attr{}
|
||||
} else {
|
||||
if ra != nil {
|
||||
return ra(groups, a)
|
||||
} else {
|
||||
return a
|
||||
}
|
||||
}
|
||||
}
|
||||
return &MyJsonHandler{output: output, mu: sync.Mutex{}, jsonHandler: slog.NewJSONHandler(output, opts)}
|
||||
}
|
||||
|
||||
func (s *MyJsonHandler) Enabled(ctx context.Context, level slog.Level) bool {
|
||||
return s.jsonHandler.Enabled(ctx, level)
|
||||
}
|
||||
|
||||
func (s *MyJsonHandler) Handle(ctx context.Context, record slog.Record) error {
|
||||
err := s.jsonHandler.Handle(ctx, record)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
select {
|
||||
case stack := <-stackChan:
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
_, err = s.output.Write([]byte(stack))
|
||||
default:
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *MyJsonHandler) WithAttrs(attrs []slog.Attr) slog.Handler {
|
||||
return s.jsonHandler.WithAttrs(attrs)
|
||||
}
|
||||
|
||||
func (s *MyJsonHandler) WithGroup(name string) slog.Handler {
|
||||
return s.jsonHandler.WithGroup(name)
|
||||
}
|
21
logger/my_json_handler_test.go
Normal file
21
logger/my_json_handler_test.go
Normal file
@ -0,0 +1,21 @@
|
||||
package logger
|
||||
|
||||
import (
|
||||
"log/slog"
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func BenchmarkMyJsonHandler_Handle(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
//slog.SetDefault(slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{
|
||||
// AddSource: false,
|
||||
// Level: nil,
|
||||
//})))
|
||||
slog.SetDefault(slog.New(newMyJsonHandler(os.Stdout, &slog.HandlerOptions{
|
||||
AddSource: false,
|
||||
Level: nil,
|
||||
})))
|
||||
slog.Info("这是一条日志", "error", "错误", "stack", "goroutine 10 [running]:\nruntime/debug.Stack()\n\tD:/develop/Go/go1.21.4/src/runtime/debug/stack.go:24 +0x6b\njoylink.club/bj-rtsts-server/starter.customRecoveryWithSlog.func1.1()\n\tD:/GoProject/rts-sim-testing-service/starter/init.go:126 +0x36f\npanic({0x1f0cb40?, 0x226fd50?})\n\tD:/develop/Go/go1.21.4/src/runtime/panic.go:920 +0x290\njoylink.club/bj-rtsts-server/api.createByProjectId(0xc001a06100)\n\tD:/GoProject/rts-sim-testing-service/api/simulation.go:91 +0x26\ngithub.com/gin-gonic/gin.(*Context).Next(0xc001a06100)\n\tC:/Users/thesai/go/pkg/mod/github.com/gin-gonic/gin@v1.9.1/context.go:174 +0x7a\njoylink.club/bj-rtsts-server/middleware.permissionMiddleware.func1(0xc001a06100)\n\tD:/GoProject/rts-sim-testing-service/middleware/auth.go:35 +0x178\ngithub.com/gin-gonic/gin.(*Context).Next(0xc001a06100)\n\tC:/Users/thesai/go/pkg/mod/github.com/gin-gonic/gin@v1.9.1/context.go:174 +0x7a\ngithub.com/appleboy/gin-jwt/v2.(*GinJWTMiddleware).middlewareImpl(0xc000504340, 0xc001a06100)\n\tC:/Users/thesai/go/pkg/mod/github.com/appleboy/gin-jwt/v2@v2.9.1/auth_jwt.go:462 +0x579\ngithub.com/appleboy/gin-jwt/v2.(*GinJWTMiddleware).MiddlewareFunc.func1(0xc001a06100)\n\tC:/Users/thesai/go/pkg/mod/github.com/appleboy/gin-jwt/v2@v2.9.1/auth_jwt.go:415 +0x26\ngithub.com/gin-gonic/gin.(*Context).Next(0xc001a06100)\n\tC:/Users/thesai/go/pkg/mod/github.com/gin-gonic/gin@v1.9.1/context.go:174 +0x7a\njoylink.club/bj-rtsts-server/starter.customRecoveryWithSlog.func1(0xc001a06100)\n\tD:/GoProject/rts-sim-testing-service/starter/init.go:135 +0xef\ngithub.com/gin-gonic/gin.(*Context).Next(0xc001a06100)\n\tC:/Users/thesai/go/pkg/mod/github.com/gin-gonic/gin@v1.9.1/context.go:174 +0x7a\ngithub.com/samber/slog-gin.NewWithConfig.func1(0xc001a06100)\n\tC:/Users/thesai/go/pkg/mod/github.com/samber/slog-gin@v1.1.0/middleware.go:70 +0x13f\ngithub.com/gin-gonic/gin.(*Context).Next(0xc001a06100)\n\tC:/Users/thesai/go/pkg/mod/github.com/gin-gonic/gin@v1.9.1/context.go:174 +0x7a\ngithub.com/gin-gonic/gin.(*Engine).handleHTTPRequest(0xc0004d51e0, 0xc001a06100)\n\tC:/Users/thesai/go/pkg/mod/github.com/gin-gonic/gin@v1.9.1/gin.go:620 +0x427\ngithub.com/gin-gonic/gin.(*Engine).ServeHTTP(0xc0004d51e0, {0x227e480, 0xc00012aee0}, 0xc001a07100)\n\tC:/Users/thesai/go/pkg/mod/github.com/gin-gonic/gin@v1.9.1/gin.go:576 +0xbc\nnet/http.serverHandler.ServeHTTP({0xc0017f0960}, {0x227e480, 0xc00012aee0}, 0xc001a07100)\n\tD:/develop/Go/go1.21.4/src/net/http/server.go:2938 +0x257\nnet/http.(*conn).serve(0xc0019422d0, {0x2285b38, 0xc001982000})\n\tD:/develop/Go/go1.21.4/src/net/http/server.go:2009 +0x1a39\ncreated by net/http.(*Server).Serve in goroutine 1\n\tD:/develop/Go/go1.21.4/src/net/http/server.go:3086 +0xa25")
|
||||
}
|
||||
}
|
@ -3,9 +3,8 @@ package ms_api
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"time"
|
||||
|
||||
"joylink.club/bj-rtsts-server/sys_error"
|
||||
)
|
||||
|
||||
type MsgTask interface {
|
||||
@ -52,7 +51,7 @@ mainLoop:
|
||||
}
|
||||
err := t.fn()
|
||||
if err != nil {
|
||||
panic(sys_error.New(fmt.Sprintf("仿真任务【%s】状态消息收集异常", t.name), err))
|
||||
slog.Error(fmt.Sprintf("仿真任务【%s】状态消息收集异常", t.name), err)
|
||||
}
|
||||
time.Sleep(t.interval)
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ package message_server
|
||||
import (
|
||||
"fmt"
|
||||
"joylink.club/bj-rtsts-server/dto/state_proto"
|
||||
"log/slog"
|
||||
"time"
|
||||
|
||||
"joylink.club/bj-rtsts-server/dto/data_proto"
|
||||
@ -16,54 +17,62 @@ import (
|
||||
|
||||
func NewPSLMs(vs *memory.VerifySimulation, mapId int32) ms_api.MsgTask {
|
||||
mapData := memory.QueryGiData[*data_proto.RtssGraphicStorage](mapId)
|
||||
return ms_api.NewScheduleTask(fmt.Sprintf("地图[%d]综合门控箱按钮状态", mapId), func() error {
|
||||
for _, box := range mapData.GateBoxs {
|
||||
did := memory.GetMapElementId(box.Common)
|
||||
data_proto, err := collectGateBoxPSLState(vs.World, mapId, box)
|
||||
uidStructure := memory.QueryUidStructure[*memory.StationUidStructure](mapId)
|
||||
return ms_api.NewScheduleTask(fmt.Sprintf("地图[%d]PSL按钮状态", mapId), func() error {
|
||||
for _, psl := range mapData.PslBoxs {
|
||||
data, err := collectPslStates(vs.World, psl.RefPslMapCode, uidStructure.PslIds[psl.Common.Id].Uid)
|
||||
if err != nil {
|
||||
return err
|
||||
slog.Error(err.Error())
|
||||
continue
|
||||
}
|
||||
mqtt.GetMsgClient().PubPSLState(vs.SimulationId, mapId, did, data_proto)
|
||||
sendMsg(vs, mapId, psl.Common.Id, data)
|
||||
}
|
||||
for _, psl := range mapData.GarageDoors {
|
||||
data, err := collectPslStates(vs.World, psl.RefPslMapCode, uidStructure.PslIds[psl.Common.Id].Uid)
|
||||
if err != nil {
|
||||
slog.Error(err.Error())
|
||||
continue
|
||||
}
|
||||
sendMsg(vs, mapId, psl.Common.Id, data)
|
||||
}
|
||||
for _, psl := range mapData.FloodGates {
|
||||
data, err := collectPslStates(vs.World, psl.RefPslMapCode, uidStructure.PslIds[psl.Common.Id].Uid)
|
||||
if err != nil {
|
||||
slog.Error(err.Error())
|
||||
continue
|
||||
}
|
||||
sendMsg(vs, mapId, psl.Common.Id, data)
|
||||
}
|
||||
return nil
|
||||
}, 200*time.Millisecond)
|
||||
}
|
||||
|
||||
func collectGateBoxPSLState(world ecs.World, mapId int32, box *data_proto.GatedBox) (*state_proto.PushedDevicesStatus, error) {
|
||||
did := memory.GetMapElementId(box.Common)
|
||||
uidStructure := memory.QueryUidStructure[*memory.StationUidStructure](mapId)
|
||||
boxUid := uidStructure.PslIds[did].Uid
|
||||
mkxEntry, ok := entity.GetEntityByUid(world, boxUid)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("[id:%s]的门控箱实体找不到", boxUid)
|
||||
func sendMsg(vs *memory.VerifySimulation, mapId int32, pslId uint32, data *state_proto.PushedDevicesStatus) {
|
||||
err := mqtt.GetMsgClient().PubPSLState(vs.SimulationId, mapId, pslId, data)
|
||||
if err != nil {
|
||||
slog.Error(fmt.Sprintf("发送PSL状态出错:%s", err.Error()))
|
||||
}
|
||||
mkx := component.MkxType.Get(mkxEntry)
|
||||
var buttonStateArr []*state_proto.ButtonState
|
||||
if ok {
|
||||
_, pslStorage := memory.QueryGiDataByName[*data_proto.PslGraphicStorage](box.RefGatedBoxMapCode)
|
||||
btnUidMap := make(map[string]uint32, len(pslStorage.PslButtons))
|
||||
for _, button := range pslStorage.PslButtons {
|
||||
btnUidMap[boxUid+"_"+button.Code] = memory.GetMapElementId(button.Common)
|
||||
}
|
||||
btnArr := []*ecs.Entry{mkx.PCB, mkx.PCBPL, mkx.POB, mkx.POBPL, mkx.PAB, mkx.PABPL, mkx.WRZF, mkx.WRZFPL,
|
||||
mkx.QKQR, mkx.QKQRPL, mkx.MPL, mkx.JXTCPL}
|
||||
for _, btn := range btnArr {
|
||||
if btn == nil {
|
||||
continue
|
||||
}
|
||||
btnState := component.BitStateType.Get(btn)
|
||||
buttonStateArr = append(buttonStateArr, &state_proto.ButtonState{
|
||||
Id: btnUidMap[component.UidType.Get(btn).Id],
|
||||
Down: btnState.Val,
|
||||
Active: btnState.Val,
|
||||
//Bypass: btnState.BypassEnable,
|
||||
})
|
||||
}
|
||||
|
||||
func collectPslStates(world ecs.World, pslMapCode string, pslUid string) (*state_proto.PushedDevicesStatus, error) {
|
||||
var btnStates []*state_proto.ButtonState
|
||||
_, pslStorage := memory.QueryGiDataByName[*data_proto.PslGraphicStorage](pslMapCode)
|
||||
for _, button := range pslStorage.PslButtons {
|
||||
btnEntry, ok := entity.GetEntityByUid(world, memory.BuildUid(pslUid, button.Code))
|
||||
if !ok {
|
||||
slog.Error(fmt.Sprintf("[id:%s]的PSL的按钮[code:%s]对应的实体找不到", pslUid, button.Code))
|
||||
continue
|
||||
}
|
||||
btnStates = append(btnStates, &state_proto.ButtonState{
|
||||
Id: button.Common.Id,
|
||||
Down: component.BitStateType.Get(btnEntry).Val,
|
||||
Active: component.BitStateType.Get(btnEntry).Val,
|
||||
})
|
||||
}
|
||||
return &state_proto.PushedDevicesStatus{
|
||||
All: true,
|
||||
AllStatus: &state_proto.AllDevicesStatus{
|
||||
ButtonState: buttonStateArr,
|
||||
ButtonState: btnStates,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
@ -35,15 +35,15 @@ func NewSfpMs(vs *memory.VerifySimulation, mapId int32) ms_api.MsgTask {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
buttonStates, err := collectStationButtonStates(vs.World, mapId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
//buttonStates, err := collectStationButtonStates(vs.World, mapId)
|
||||
//if err != nil {
|
||||
// return err
|
||||
//}
|
||||
psdStates, err := collectPsdStates(vs.World, mapId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
sectionStates, err := collectSectionStates(vs.World, mapId)
|
||||
sectionStates, err := collectPhysicalSectionStates(vs.World, mapId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -59,17 +59,37 @@ func NewSfpMs(vs *memory.VerifySimulation, mapId int32) ms_api.MsgTask {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ckmStates, err := collectCkmStates(vs.World, mapId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fymStates, err := collectFymStates(vs.World, mapId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
xcjStates, err := collectXcjStates(vs.World, mapId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
axleCountingSectionStates, err := collectAxleCountingSectionStates(vs.World, mapId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ststes := &state_proto.PushedDevicesStatus{
|
||||
All: true,
|
||||
AllStatus: &state_proto.AllDevicesStatus{
|
||||
TrainState: trainState,
|
||||
SwitchState: turnoutStates,
|
||||
SignalState: signalStates,
|
||||
ButtonState: buttonStates,
|
||||
PsdState: psdStates,
|
||||
SectionState: sectionStates,
|
||||
PlatformState: platformStates,
|
||||
BaliseState: baliseStates,
|
||||
TrainState: trainState,
|
||||
SwitchState: turnoutStates,
|
||||
SignalState: signalStates,
|
||||
//ButtonState: buttonStates,
|
||||
PsdState: psdStates,
|
||||
SectionState: sectionStates,
|
||||
PlatformState: platformStates,
|
||||
BaliseState: baliseStates,
|
||||
CkmStates: ckmStates,
|
||||
FymStates: fymStates,
|
||||
XcjStates: xcjStates,
|
||||
AxleCountingSection: axleCountingSectionStates,
|
||||
},
|
||||
}
|
||||
err = mqtt.GetMsgClient().PubSfpState(vs.SimulationId, mapId, ststes)
|
||||
@ -80,6 +100,105 @@ func NewSfpMs(vs *memory.VerifySimulation, mapId int32) ms_api.MsgTask {
|
||||
}, 1000*time.Millisecond)
|
||||
}
|
||||
|
||||
func collectAxleCountingSectionStates(world ecs.World, mapId int32) ([]*state_proto.AxleCountingSectionState, error) {
|
||||
uidMap := memory.QueryMapUidMapByType(mapId, &data_proto.AxleCountingSection{})
|
||||
var stateArr []*state_proto.AxleCountingSectionState
|
||||
wd := entity.GetWorldData(world)
|
||||
for _, u := range uidMap {
|
||||
entry := wd.EntityMap[u.Uid]
|
||||
if entry == nil {
|
||||
continue
|
||||
}
|
||||
stateArr = append(stateArr, &state_proto.AxleCountingSectionState{
|
||||
Id: u.CommonId,
|
||||
Occupied: component.AxleCountingSectionStateType.Get(entry).Occupied,
|
||||
})
|
||||
}
|
||||
return stateArr, nil
|
||||
}
|
||||
|
||||
func collectXcjStates(world ecs.World, mapId int32) ([]*state_proto.XcjState, error) {
|
||||
uidStructure := memory.QueryUidStructure[*memory.StationUidStructure](mapId)
|
||||
var xcjStates []*state_proto.XcjState
|
||||
for _, xcj := range uidStructure.XcjIds {
|
||||
entry, ok := entity.GetEntityByUid(world, xcj.Uid)
|
||||
if ok {
|
||||
state := &state_proto.XcjState{Id: xcj.CommonId}
|
||||
xcjStates = append(xcjStates, state)
|
||||
//设置参数
|
||||
var xcjParam *request_proto.XcjParam
|
||||
if entry.HasComponent(appcomponent.XcjParamType) {
|
||||
xcjParam = appcomponent.XcjParamType.Get(entry)
|
||||
} else {
|
||||
xcjParam = &request_proto.XcjParam{}
|
||||
}
|
||||
state.Param = xcjParam
|
||||
//继电器状态
|
||||
if entry.HasComponent(component.XcjCircuitType) {
|
||||
circuit := component.XcjCircuitType.Get(entry)
|
||||
state.Xqj = component.BitStateType.Get(circuit.XQJ).Val
|
||||
for _, twj := range circuit.TWJList {
|
||||
state.TwjList = append(state.TwjList, component.BitStateType.Get(twj).Val)
|
||||
}
|
||||
state.Tgqj = component.BitStateType.Get(circuit.TGQJ).Val
|
||||
state.Xcjxj = component.BitStateType.Get(circuit.XCJXJ).Val
|
||||
state.Xcyxj = component.BitStateType.Get(circuit.XCYXJ).Val
|
||||
for _, cfj := range circuit.CFJList {
|
||||
state.CfjList = append(state.CfjList, component.BitStateType.Get(cfj).Val)
|
||||
}
|
||||
state.Jtj = component.BitStateType.Get(circuit.JTJ).Val
|
||||
state.Tgyxj = component.BitStateType.Get(circuit.TGYXJ).Val
|
||||
}
|
||||
}
|
||||
}
|
||||
return xcjStates, nil
|
||||
}
|
||||
|
||||
func collectFymStates(world ecs.World, mapId int32) ([]*state_proto.CkmState, error) {
|
||||
mapData := memory.QueryGiData[*data_proto.RtssGraphicStorage](mapId)
|
||||
return collectCkmOrFymStates(world, mapId, mapData.FloodGates)
|
||||
}
|
||||
|
||||
func collectCkmStates(world ecs.World, mapId int32) ([]*state_proto.CkmState, error) {
|
||||
mapData := memory.QueryGiData[*data_proto.RtssGraphicStorage](mapId)
|
||||
return collectCkmOrFymStates(world, mapId, mapData.GarageDoors)
|
||||
}
|
||||
|
||||
func collectCkmOrFymStates(world ecs.World, mapId int32, gates []*data_proto.GarageDoor) ([]*state_proto.CkmState, error) {
|
||||
uidStructure := memory.QueryUidStructure[*memory.StationUidStructure](mapId)
|
||||
ckmUidMap := uidStructure.CkmIds
|
||||
var ckmStates []*state_proto.CkmState
|
||||
for _, ckm := range gates {
|
||||
commonId := ckm.Common.Id
|
||||
entry, ok := entity.GetEntityByUid(world, ckmUidMap[commonId].Uid)
|
||||
if ok {
|
||||
mgj := false
|
||||
local := false
|
||||
mplj := false
|
||||
if entry.HasComponent(component.CkmCircuitType) {
|
||||
circuit := component.CkmCircuitType.Get(entry)
|
||||
mgj = component.BitStateType.Get(circuit.MGJ).Val
|
||||
local = component.BitStateType.Get(circuit.MMSJ).Val
|
||||
mplj = component.BitStateType.Get(circuit.MPLJ).Val
|
||||
}
|
||||
var ckmParam *request_proto.CkmParam
|
||||
if entry.HasComponent(appcomponent.CkmParamType) {
|
||||
ckmParam = appcomponent.CkmParamType.Get(entry)
|
||||
} else {
|
||||
ckmParam = &request_proto.CkmParam{}
|
||||
}
|
||||
ckmStates = append(ckmStates, &state_proto.CkmState{
|
||||
Id: commonId,
|
||||
Mgj: mgj,
|
||||
Param: ckmParam,
|
||||
Local: local,
|
||||
Mplj: mplj,
|
||||
})
|
||||
}
|
||||
}
|
||||
return ckmStates, nil
|
||||
}
|
||||
|
||||
// 收集应答器状态
|
||||
func collectBaliseStates(world ecs.World, mapId int32) ([]*state_proto.BaliseState, error) {
|
||||
uidStructure := memory.QueryUidStructure[*memory.StationUidStructure](mapId)
|
||||
@ -158,7 +277,7 @@ func collectPsdStates(world ecs.World, mapId int32) ([]*state_proto.PsdState, er
|
||||
}
|
||||
|
||||
// 收集区段状态
|
||||
func collectSectionStates(world ecs.World, mapId int32) ([]*state_proto.SectionState, error) {
|
||||
func collectPhysicalSectionStates(world ecs.World, mapId int32) ([]*state_proto.SectionState, error) {
|
||||
uidMap := memory.QueryMapUidMapByType(mapId, &data_proto.Section{})
|
||||
var sectionArr []*state_proto.SectionState
|
||||
for _, u := range uidMap {
|
||||
@ -178,22 +297,23 @@ func handlerSectionState(w ecs.World, uid string) *state_proto.SectionState {
|
||||
//fmt.Printf("id=%s的信号机不存在", uid)
|
||||
return nil
|
||||
}
|
||||
if entry.HasComponent(component.PhysicalSectionStateType) { //计轴区段
|
||||
sectionState := &state_proto.SectionState{}
|
||||
axleState := component.PhysicalSectionStateType.Get(entry)
|
||||
sectionState.Occupied = axleState.Occ
|
||||
sectionState.AxleFault = entry.HasComponent(component.AxleSectionFaultTag)
|
||||
|
||||
wd := entity.GetWorldData(w)
|
||||
sectionModel := wd.Repo.FindPhysicalSection(uid)
|
||||
faDcAxleDeviceEntry := entity.FindAxleManageDevice(wd, sectionModel.CentralizedStation())
|
||||
if faDcAxleDeviceEntry != nil {
|
||||
faDcAxleDevice := component.AxleManageDeviceType.Get(faDcAxleDeviceEntry)
|
||||
axleRuntime := faDcAxleDevice.FindAdr(uid)
|
||||
if axleRuntime != nil {
|
||||
sectionState.AxleDrst = axleRuntime.Drst
|
||||
sectionState.AxlePdrst = axleRuntime.Pdrst
|
||||
}
|
||||
if entry.HasComponent(component.PhysicalSectionManagerType) { //计轴区段
|
||||
axleManager := component.PhysicalSectionManagerType.Get(entry)
|
||||
sectionState := &state_proto.SectionState{
|
||||
Occupied: axleManager.Occupied,
|
||||
AxleFault: entry.HasComponent(component.PhysicalSectionForceOccupied),
|
||||
AxleDrst: false,
|
||||
AxlePdrst: axleManager.PDRST,
|
||||
}
|
||||
sectionState.Occupied = axleManager.Occupied
|
||||
sectionState.AxleFault = entry.HasComponent(component.PhysicalSectionForceOccupied)
|
||||
return sectionState
|
||||
} else if entry.HasComponent(component.TrackCircuitType) { //轨道电路
|
||||
sectionState := &state_proto.SectionState{
|
||||
Occupied: component.BitStateType.Get(component.TrackCircuitType.Get(entry).GJ).Val,
|
||||
AxleFault: false,
|
||||
AxleDrst: false,
|
||||
AxlePdrst: false,
|
||||
}
|
||||
return sectionState
|
||||
}
|
||||
@ -338,20 +458,20 @@ func collectTrainStates(vs *memory.VerifySimulation) ([]*state_proto.TrainMapSta
|
||||
|
||||
func convertTrainState(v *state_proto.TrainState) *state_proto.TrainMapState {
|
||||
t := &state_proto.TrainMapState{
|
||||
Id: v.Id,
|
||||
Up: v.Up,
|
||||
InitialSpeed: v.Speed,
|
||||
TrainLength: v.TrainLength,
|
||||
Show: v.Show,
|
||||
HeadDeviceId: v.HeadDeviceId,
|
||||
HeadOffset: v.HeadOffset,
|
||||
DevicePort: v.DevicePort,
|
||||
PointTo: v.PointTo,
|
||||
RunDirection: v.RunDirection,
|
||||
HeadDirection: v.HeadDirection,
|
||||
TrainKilometer: v.TrainKilometer,
|
||||
ControlDelayTime: v.ControlDelayTime,
|
||||
WheelDiameter: v.WheelDiameter,
|
||||
Id: v.Id,
|
||||
Up: v.Up,
|
||||
InitialSpeed: v.Speed,
|
||||
TrainLength: v.TrainLength,
|
||||
Show: v.Show,
|
||||
HeadDeviceId: v.HeadDeviceId,
|
||||
HeadOffset: v.HeadOffset,
|
||||
DevicePort: v.DevicePort,
|
||||
DriftTo: v.DriftTo,
|
||||
TrainRunUp: v.TrainRunUp,
|
||||
TrainActiveDirection: v.TrainActiveDirection,
|
||||
TrainKilometer: v.TrainKilometer,
|
||||
ControlDelayTime: v.ControlDelayTime,
|
||||
WheelDiameter: v.WheelDiameter,
|
||||
// 动力学
|
||||
DynamicHeartbeat: v.DynamicState.Heartbeat,
|
||||
HeadLinkId: v.DynamicState.HeadLinkId,
|
||||
@ -408,15 +528,17 @@ func convertTrainState(v *state_proto.TrainState) *state_proto.TrainMapState {
|
||||
TailDeviceId: v.TailDeviceId,
|
||||
TailOffset: v.TailOffset,
|
||||
TailDevicePort: v.TailDevicePort,
|
||||
BtmState: v.BtmState,
|
||||
ConnState: v.ConnState,
|
||||
//BtmState: v.BtmState,
|
||||
ConnState: v.ConnState,
|
||||
}
|
||||
convertDynamicConfig(v.TrainDynamicConfig, t.TrainDynamicConfig)
|
||||
convertDynamicConfig(v.TrainEndsA, t.TrainEndsA)
|
||||
convertDynamicConfig(v.TrainEndsB, t.TrainEndsB)
|
||||
//now := time.Now().Unix()
|
||||
|
||||
return t
|
||||
}
|
||||
|
||||
func convertDynamicConfig(config, dest interface{}) {
|
||||
configType := reflect.TypeOf(config).Elem()
|
||||
for index := 0; index < configType.NumField(); index++ {
|
||||
@ -428,17 +550,6 @@ func convertDynamicConfig(config, dest interface{}) {
|
||||
}
|
||||
}
|
||||
|
||||
/*func convertDynamicConfig(config *common_proto.TrainDynamicConfig, dest *state_proto.TrainDynamicConfigMqtt) {
|
||||
configType := reflect.TypeOf(config).Elem()
|
||||
for index := 0; index < configType.NumField(); index++ {
|
||||
field := configType.Field(index)
|
||||
if field.IsExported() {
|
||||
fieldName := field.Name
|
||||
setVal(config, dest, fieldName)
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
func setVal(source, dest interface{}, fieldName string) {
|
||||
destVal := reflect.ValueOf(dest).Elem().FieldByName(fieldName)
|
||||
sourceType := reflect.ValueOf(source).Elem().FieldByName(fieldName)
|
||||
@ -450,7 +561,10 @@ func setVal(source, dest interface{}, fieldName string) {
|
||||
debug.PrintStack()
|
||||
}
|
||||
}()
|
||||
|
||||
if destVal.Kind() == reflect.Invalid {
|
||||
//slog.Warn(fieldName, "赋值失败,源数据类型", sourceType.Kind().String(), "目标数据类型:", destVal.Kind().String())
|
||||
return
|
||||
}
|
||||
if destVal.Kind() == reflect.String {
|
||||
destVal.Set(reflect.ValueOf(fmt.Sprintf("%v", sourceData)))
|
||||
} else {
|
||||
|
@ -10,27 +10,81 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
func lowPower(power bool) bool {
|
||||
if power {
|
||||
return false
|
||||
} else {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
// 综合后备盘IBP消息服务
|
||||
func NewTrainControlMs(vs *memory.VerifySimulation, mapId int32) ms_api.MsgTask {
|
||||
/* var findMapId int32 = 0
|
||||
for _, d := range vs.MapIds {
|
||||
mapData := memory.QueryGiType(d)
|
||||
if mapData == data_proto.PictureType_TrainControlCab {
|
||||
findMapId = d
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if findMapId == 0 {
|
||||
slog.Error("未找到对应的列城控制图形")
|
||||
return nil
|
||||
}*/
|
||||
return ms_api.NewScheduleTask(fmt.Sprintf("地图[%d]列车控制", mapId), func() error {
|
||||
allTrainMap := &vs.Memory.Status.TrainStateMap
|
||||
allTrainMap.Range(func(key, value any) bool {
|
||||
trainId := fmt.Sprintf("%v", key)
|
||||
ts := value.(*state_proto.TrainState)
|
||||
err := mqtt.GetMsgClient().PubTrainControlState(vs.SimulationId, trainId, ts.Tcc)
|
||||
ttcc := ts.Tcc
|
||||
vobc := ts.VobcState
|
||||
lights := make([]*state_proto.TrainControlState_ControlLight, 0)
|
||||
for lightKey, light := range ttcc.LightMaps {
|
||||
switch lightKey {
|
||||
case memory.LIGHT_JJZD:
|
||||
state := lowPower(vobc.LightEmergencyBrakingStatus)
|
||||
lights = append(lights, &state_proto.TrainControlState_ControlLight{Id: light.Id, Val: state})
|
||||
case memory.LIGHT_QQY:
|
||||
state := lowPower(vobc.LightTractionSafetyCircuit)
|
||||
lights = append(lights, &state_proto.TrainControlState_ControlLight{Id: light.Id, Val: state})
|
||||
case memory.LIGHT_JSSJH:
|
||||
lights = append(lights, &state_proto.TrainControlState_ControlLight{Id: light.Id, Val: vobc.LightDriverActive})
|
||||
case memory.LIGHT_TFZDHJ:
|
||||
lights = append(lights, &state_proto.TrainControlState_ControlLight{Id: light.Id, Val: vobc.StopNotAllBrake})
|
||||
case memory.LIGHT_QYYX:
|
||||
lights = append(lights, &state_proto.TrainControlState_ControlLight{Id: light.Id, Val: vobc.TractionEffective})
|
||||
case memory.LIGHT_ZDYX:
|
||||
lights = append(lights, &state_proto.TrainControlState_ControlLight{Id: light.Id, Val: vobc.BrakeEffective})
|
||||
case memory.LIGHT_TFZDSJ:
|
||||
lights = append(lights, &state_proto.TrainControlState_ControlLight{Id: light.Id, Val: vobc.ParkingBrakeStatus})
|
||||
case memory.LIGHT_CYZD:
|
||||
lights = append(lights, &state_proto.TrainControlState_ControlLight{Id: light.Id, Val: vobc.MostUseBrake})
|
||||
case memory.LIGHT_ZDGL:
|
||||
lights = append(lights, &state_proto.TrainControlState_ControlLight{Id: light.Id, Val: vobc.BrakeQuarantine})
|
||||
|
||||
case memory.LIGHT_LSXH:
|
||||
lights = append(lights, &state_proto.TrainControlState_ControlLight{Id: light.Id, Val: vobc.NoSpeedSigle})
|
||||
case memory.LIGHT_ZMYX:
|
||||
lights = append(lights, &state_proto.TrainControlState_ControlLight{Id: light.Id, Val: vobc.LeftDoorState})
|
||||
case memory.LIGHT_YMYX:
|
||||
lights = append(lights, &state_proto.TrainControlState_ControlLight{Id: light.Id, Val: vobc.RightDoorState})
|
||||
case memory.LIGHT_ZFZSD:
|
||||
lights = append(lights, &state_proto.TrainControlState_ControlLight{Id: light.Id, Val: vobc.TurnbackStatus})
|
||||
case memory.LIGHT_BDATPKC:
|
||||
lights = append(lights, &state_proto.TrainControlState_ControlLight{Id: light.Id, Val: vobc.LocalAtpControl})
|
||||
case memory.LIGHT_ATOFCZSD:
|
||||
lights = append(lights, &state_proto.TrainControlState_ControlLight{Id: light.Id, Val: vobc.LightAtoSend})
|
||||
case memory.LIGHT_ATO_OPEN_LEFT_DOOR:
|
||||
lights = append(lights, &state_proto.TrainControlState_ControlLight{Id: light.Id, Val: vobc.AtoOpenLeftDoor})
|
||||
case memory.LIGHT_ATO_OPEN_RIGHT_DOOR:
|
||||
lights = append(lights, &state_proto.TrainControlState_ControlLight{Id: light.Id, Val: vobc.AtoOpenRightDoor})
|
||||
case memory.LIGHT_ATO_CLOSE_LEFT_DOOR:
|
||||
lights = append(lights, &state_proto.TrainControlState_ControlLight{Id: light.Id, Val: vobc.AtoCloseLeftDoor})
|
||||
case memory.LIGHT_ATO_CLOSE_RIGHT_DOOR:
|
||||
lights = append(lights, &state_proto.TrainControlState_ControlLight{Id: light.Id, Val: vobc.AtoCloseRightDoor})
|
||||
default:
|
||||
lights = append(lights, &state_proto.TrainControlState_ControlLight{Id: light.Id, Val: light.Val})
|
||||
}
|
||||
}
|
||||
buttons := make([]*state_proto.TrainControlState_ControlButton, 0)
|
||||
for _, button := range ttcc.Buttons {
|
||||
buttons = append(buttons, button)
|
||||
}
|
||||
sks := make([]*state_proto.TrainControlState_SwitchKeyChange, 0)
|
||||
for _, sk := range ttcc.SwitchKeyMap {
|
||||
sks = append(sks, sk)
|
||||
}
|
||||
tcc := &state_proto.TrainControlStateMsg{Buttons: buttons, DriverKey: ttcc.DriverKey, SwitchKeys: sks, PushHandler: ttcc.PushHandler, Lights: lights}
|
||||
err := mqtt.GetMsgClient().PubTrainControlState(vs.SimulationId, trainId, tcc)
|
||||
if err != nil {
|
||||
slog.Error("发送列车控制mqtt失败", err)
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ package middleware
|
||||
|
||||
import (
|
||||
"log/slog"
|
||||
"net/http"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
@ -36,12 +37,16 @@ func permissionMiddleware() gin.HandlerFunc {
|
||||
return
|
||||
}
|
||||
path, method := c.Request.URL.Path, c.Request.Method
|
||||
if method == http.MethodGet {
|
||||
c.Next()
|
||||
return
|
||||
}
|
||||
if validateUserPath(path, method, userAuth.AuthPaths) { // 用户有权限
|
||||
c.Next()
|
||||
return
|
||||
}
|
||||
slog.Error("无权限操作请求路径", "path", path, "method", method)
|
||||
panic(sys_error.New("权限不足"))
|
||||
panic(sys_error.NewCode("权限不足", 403))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -29,10 +29,15 @@ func Startup(cmc *MqttOptions) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cm, err := autopaho.NewConnection(context.Background(), *cc)
|
||||
ctx := context.Background()
|
||||
cm, err := autopaho.NewConnection(ctx, *cc)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
//添加等待连接,如果连接失败,则等待重连
|
||||
if err = cm.AwaitConnection(ctx); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
mqttClient = &MqttClient{cc: cc, cm: cm}
|
||||
return nil
|
||||
}
|
||||
@ -90,6 +95,20 @@ func (client *MqttClient) pub(topic string, data protoreflect.ProtoMessage) erro
|
||||
return err
|
||||
}
|
||||
|
||||
// 订阅mqtt服务
|
||||
/*func subHandle(p *paho.Publish) {
|
||||
fmt.Println(fmt.Sprintf("%v-%v"), hex.EncodeToString(p.Payload), len(p.Payload))
|
||||
}
|
||||
func (client *MqttClient) SubSimulationState(simulationId string) (*paho.Suback, error) {
|
||||
topIc := GetStateTopic(simulationId)
|
||||
_, err2 := client.cm.Subscribe(context.Background(), &paho.Subscribe{Subscriptions: []paho.SubscribeOptions{{Topic: topIc, QoS: 0}}})
|
||||
if err2 != nil {
|
||||
fmt.Println(err2)
|
||||
}
|
||||
client.cc.Router.RegisterHandler(topIc, subHandle)
|
||||
return nil, nil
|
||||
}*/
|
||||
|
||||
// 发送仿真状态数据
|
||||
func (client *MqttClient) PubSimulationState(simulationId string, msg *state_proto.SimulationStatus) error {
|
||||
return client.pub(GetStateTopic(simulationId), msg)
|
||||
@ -120,6 +139,6 @@ func (client *MqttClient) PubSfpState(simulationId string, mapId int32, msg *sta
|
||||
}
|
||||
|
||||
// 发送列车控制状态
|
||||
func (client *MqttClient) PubTrainControlState(simulationId string, trainId string, msg *state_proto.TrainControlState) error {
|
||||
func (client *MqttClient) PubTrainControlState(simulationId string, trainId string, msg *state_proto.TrainControlStateMsg) error {
|
||||
return client.pub(GetTrainControlTopic(simulationId, trainId), msg)
|
||||
}
|
||||
|
@ -22,6 +22,7 @@ type MqttOptions struct {
|
||||
KeepAlive uint16 // 保活时间间隔,单位s,默认为60
|
||||
ConnectRetryDelay uint16 // 连接重试延时,单位s,默认为3
|
||||
ConnectTimeout uint16 // 连接操作超时,单位s,默认为3
|
||||
route *paho.StandardRouter
|
||||
}
|
||||
|
||||
func NewMqttOptions(address, username, password string) *MqttOptions {
|
||||
@ -53,6 +54,7 @@ func (c *MqttOptions) tryInto() (*autopaho.ClientConfig, error) {
|
||||
if c.ConnectTimeout == 0 {
|
||||
c.ConnectTimeout = 3
|
||||
}
|
||||
router := paho.NewStandardRouter()
|
||||
cc := &autopaho.ClientConfig{
|
||||
BrokerUrls: []*url.URL{
|
||||
addr,
|
||||
@ -73,8 +75,15 @@ func (c *MqttOptions) tryInto() (*autopaho.ClientConfig, error) {
|
||||
OnServerDisconnect: func(d *paho.Disconnect) {
|
||||
fmt.Printf("%s 连接断开; reason code: %d,properties: %v\n", c.ClientId, d.ReasonCode, d.Properties)
|
||||
},
|
||||
//添加订阅路由功能,以支持订阅
|
||||
OnPublishReceived: []func(paho.PublishReceived) (bool, error){
|
||||
func(pr paho.PublishReceived) (bool, error) {
|
||||
router.Route(pr.Packet.Packet())
|
||||
return true, nil // we assume that the router handles all messages (todo: amend router API)
|
||||
}},
|
||||
},
|
||||
}
|
||||
cc.SetUsernamePassword(c.Username, []byte(c.Password))
|
||||
c.route = router
|
||||
return cc, nil
|
||||
}
|
||||
|
@ -15,12 +15,12 @@ var (
|
||||
basePath, _ = os.Getwd()
|
||||
protoFolder = filepath.Join(basePath, "rts-sim-testing-message", "protos")
|
||||
protocPath = filepath.Join(basePath, "rts-sim-testing-message", "protoc-23.1", "bin", "win64", "protoc")
|
||||
modulePrefix = "joylink.club/rts-sim-testing-service"
|
||||
modulePrefix = "joylink.club/bj-rtsts-server"
|
||||
)
|
||||
|
||||
func main() {
|
||||
//先安装以下插件
|
||||
//go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
|
||||
//go install google.golang.org/protobuf/cmd/protoc-gen-go@v1.33.0
|
||||
|
||||
protoFiles := getProtoFiles()
|
||||
// 编译proto文件为Go文件
|
@ -1 +1 @@
|
||||
Subproject commit 21ca336b0ce32cec941685d2d37feb9d69c92970
|
||||
Subproject commit 88b24dff6589615d68c83f19196904bc7bb64077
|
@ -1 +1 @@
|
||||
Subproject commit 0e19775ba6eae193bd5e7156d8f33ae6bc704023
|
||||
Subproject commit a6293366a132680309a62855d4c4e19546ba3aa8
|
@ -1,7 +1,10 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"joylink.club/bj-rtsts-server/config"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"joylink.club/bj-rtsts-server/db/dbquery"
|
||||
@ -33,8 +36,31 @@ func ListProjectRunConfigQuery() []*dto.ProjectRunConfigDto {
|
||||
return dto.ConvertToRunConfigFromSlice(records)
|
||||
}
|
||||
|
||||
func checkRunConfig(jsonConfigStr string) *sys_error.BusinessError {
|
||||
var configMap config.ThirdPartyConfig
|
||||
err := json.Unmarshal([]byte(jsonConfigStr), &configMap)
|
||||
if err != nil {
|
||||
return sys_error.New("运行环境序列化错误", err)
|
||||
}
|
||||
checkSameMap := make(map[string]bool)
|
||||
for _, simConfig := range configMap.PcSimConfigs {
|
||||
if simConfig.ConfigName == "" || len(strings.TrimSpace(simConfig.ConfigName)) == 0 {
|
||||
return sys_error.New(fmt.Sprintf("车载运行配置名称不能为空 "), err)
|
||||
}
|
||||
if checkSameMap[simConfig.ConfigName] {
|
||||
return sys_error.New(fmt.Sprintf("车载运行配置重复的名称:%v", simConfig.ConfigName), err)
|
||||
}
|
||||
checkSameMap[simConfig.ConfigName] = true
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// 创建项目运行环境
|
||||
func CreateProjectRunConfig(dd *dto.ProjectRunConfigReqDto) bool {
|
||||
if checkErr := checkRunConfig(dd.ConfigContent); checkErr != nil {
|
||||
panic(checkErr)
|
||||
}
|
||||
|
||||
d := model.ProjectRunConfig{
|
||||
Name: dd.Name,
|
||||
Description: dd.Description,
|
||||
@ -42,6 +68,7 @@ func CreateProjectRunConfig(dd *dto.ProjectRunConfigReqDto) bool {
|
||||
CreateTime: time.Now(),
|
||||
UpdateTime: time.Now(),
|
||||
}
|
||||
|
||||
err := dbquery.ProjectRunConfig.Save(&d)
|
||||
if err != nil {
|
||||
panic(sys_error.New("保存失败,数据库错误请联系运维人员", err))
|
||||
@ -75,6 +102,9 @@ func QueryRunConfig(id int32) *dto.ProjectRunConfigDto {
|
||||
|
||||
// 更新项目运行环境
|
||||
func UpdateProjectRunConfig(id int32, dd *dto.ProjectRunConfigReqDto) bool {
|
||||
if checkErr := checkRunConfig(dd.ConfigContent); checkErr != nil {
|
||||
panic(checkErr)
|
||||
}
|
||||
findOldQuery := dbquery.ProjectRunConfig
|
||||
oldD, err := findOldQuery.Where(findOldQuery.ID.Eq(id)).Debug().First()
|
||||
if oldD == nil || err != nil {
|
||||
|
@ -79,10 +79,15 @@ func TrainConfigToProtoConvert(t *dto.ConfigTrainData) *common_proto.TrainDynami
|
||||
// 转成列车proto
|
||||
func convertTrainDtoToProto(t *dto.TrainInfoDto) []byte {
|
||||
message := &data_proto.Train{
|
||||
TrainModel: data_proto.Train_TrainModel(t.TrainModel),
|
||||
CarriageLength: t.CarriageLength,
|
||||
TotalLength: t.TotalLength,
|
||||
TrainControlMapId: t.TrainControlMapId,
|
||||
TrainModel: data_proto.Train_TrainModel(t.TrainModel),
|
||||
CarriageLength: t.CarriageLength,
|
||||
TotalLength: t.TotalLength,
|
||||
TrainControlMapId: t.TrainControlMapId,
|
||||
TrainMaxSpeed: t.TrainMaxSpeed,
|
||||
TrainLoad: t.TrainLoad,
|
||||
TrainMaxAcc: t.TrainMaxAcc,
|
||||
TrainMaxBrake: t.TrainMaxBrake,
|
||||
TrainEmergencyBrake: t.TrainEmergencyBrake,
|
||||
//TrainControlMapCode: t.TrainControlMapCode,
|
||||
//MinDiameter: t.MinDiameter,
|
||||
//MaxDiameter: t.MaxDiameter,
|
||||
|
@ -86,7 +86,11 @@ func initServer() *gin.Engine {
|
||||
}
|
||||
}
|
||||
c.Error(be)
|
||||
c.JSON(http.StatusInternalServerError, &dto.ErrorDto{
|
||||
statusCode := http.StatusInternalServerError
|
||||
if be.ErrorCode > 0 {
|
||||
statusCode = http.StatusForbidden
|
||||
}
|
||||
c.JSON(statusCode, &dto.ErrorDto{
|
||||
Tip: be.UserMsg,
|
||||
Message: be.Error(),
|
||||
})
|
||||
@ -122,11 +126,7 @@ func customRecoveryWithSlog(logger *slog.Logger, stack bool, recovery gin.Recove
|
||||
}
|
||||
|
||||
if stack {
|
||||
logger.Error("请求处理Panic异常",
|
||||
"error", err,
|
||||
"stack", string(debug.Stack()),
|
||||
)
|
||||
debug.PrintStack()
|
||||
logger.Error("请求处理Panic异常", "error", err, "stack", string(debug.Stack()))
|
||||
} else {
|
||||
logger.Error("请求处理Panic异常",
|
||||
"error", err,
|
||||
|
@ -10,7 +10,8 @@ type BusinessError struct {
|
||||
// 用户提示信息
|
||||
UserMsg string
|
||||
// 错误信息传递(用于开发回溯定位,不给用户展示)
|
||||
Errors []string
|
||||
Errors []string
|
||||
ErrorCode int
|
||||
}
|
||||
|
||||
// 新建业务错误
|
||||
@ -34,7 +35,26 @@ func New(userMsg string, errs ...error) *BusinessError {
|
||||
// Errors: convert(errs),
|
||||
}
|
||||
}
|
||||
|
||||
func NewCode(userMsg string, errCode int, errs ...error) *BusinessError {
|
||||
if len(errs) == 1 {
|
||||
be, ok := errs[0].(*BusinessError)
|
||||
if ok {
|
||||
be.prependUserMsg(userMsg)
|
||||
return be
|
||||
} else {
|
||||
return &BusinessError{
|
||||
UserMsg: userMsg,
|
||||
ErrorCode: errCode,
|
||||
Errors: []string{errs[0].Error()},
|
||||
}
|
||||
}
|
||||
}
|
||||
return &BusinessError{
|
||||
UserMsg: userMsg,
|
||||
ErrorCode: errCode,
|
||||
// Errors: convert(errs),
|
||||
}
|
||||
}
|
||||
func IsBusinessError(err error) bool {
|
||||
_, ok := err.(*BusinessError)
|
||||
return ok
|
||||
|
304
third_party/acc/acc_server_test.go
vendored
304
third_party/acc/acc_server_test.go
vendored
@ -5,13 +5,14 @@ import (
|
||||
"fmt"
|
||||
"joylink.club/bj-rtsts-server/third_party/message"
|
||||
"joylink.club/bj-rtsts-server/third_party/udp"
|
||||
"math"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestAccUdp(t *testing.T) {
|
||||
|
||||
fmt.Println("准备启动ACC服务...")
|
||||
addr := fmt.Sprintf("%v:%v", "127.0.0.1", "8899")
|
||||
addr := fmt.Sprintf("%v:%v", "127.0.0.1", "9998")
|
||||
server := udp.NewServer(addr, handle)
|
||||
server.Listen()
|
||||
select {}
|
||||
@ -23,6 +24,305 @@ func handle(d []byte) {
|
||||
if err == nil {
|
||||
|
||||
jsonD, _ := json.Marshal(ri)
|
||||
fmt.Println(string(jsonD))
|
||||
fmt.Println("接受数据:", string(jsonD))
|
||||
}
|
||||
}
|
||||
func TestAcc(t *testing.T) {
|
||||
d2, d1, d0 := encodeAcc(6.742071875)
|
||||
a := decode2Acc(d2, d1, d0)
|
||||
fmt.Println(a)
|
||||
}
|
||||
|
||||
const G = 9.80665
|
||||
|
||||
func encodeAcc(a float32) (d2, d1, d0 byte) {
|
||||
d2 = 0
|
||||
d1 = 0
|
||||
d0 = 0
|
||||
x := a / G
|
||||
fmt.Println(x)
|
||||
v := uint32(0)
|
||||
for i := 17; i >= 0; i-- {
|
||||
t := float32(1.0 / math.Pow(2, float64(17-i)))
|
||||
if t > x {
|
||||
continue
|
||||
} else {
|
||||
v |= 1 << i
|
||||
x -= t
|
||||
}
|
||||
}
|
||||
fmt.Printf("%b, %b\n", v, v<<6)
|
||||
v <<= 6
|
||||
d0 = byte(v)
|
||||
d1 = byte(v >> 8)
|
||||
d2 = byte(v >> 16)
|
||||
fmt.Printf("%b, %b, %b\n", d2, d1, d0)
|
||||
return
|
||||
}
|
||||
|
||||
func decode2Acc(d2, d1, d0 byte) float32 {
|
||||
v := uint32(d2)<<10 | uint32(d1)<<2 | uint32(d0>>6)
|
||||
fmt.Printf("%b\n", v)
|
||||
x := float32(0)
|
||||
for i := 17; i >= 0; i-- {
|
||||
if v&(1<<i) != 0 {
|
||||
t := float32(1.0 / math.Pow(2, float64(17-i)))
|
||||
x += t
|
||||
}
|
||||
}
|
||||
fmt.Println(x)
|
||||
return x * G
|
||||
}
|
||||
|
||||
func setBit(b uint32, bitNum uint32, value uint32) uint32 {
|
||||
if value != 0 && value != 1 {
|
||||
panic("value must be 0 or 1")
|
||||
}
|
||||
mask := uint32(1 << bitNum) // 创建掩码
|
||||
b &= ^(mask) // 清除位
|
||||
b |= (mask & (value << bitNum))
|
||||
|
||||
return b
|
||||
}
|
||||
func setBit2(b byte, bitNum int, value byte) byte {
|
||||
if value != 0 && value != 1 {
|
||||
panic("value must be 0 or 1")
|
||||
}
|
||||
mask := byte(1 << uint(bitNum)) // 创建掩码
|
||||
b &= ^(mask) // 清除位
|
||||
b |= (mask & (value << uint(bitNum)))
|
||||
return b
|
||||
}
|
||||
|
||||
func encode(accSpeed float32) (d2, d1, d0 byte) {
|
||||
//var accCode uint32 = math.Float32bits(accSpeed) >> 8
|
||||
var accCode uint32 = 0
|
||||
var sum float32 = accSpeed
|
||||
for i := 17; i >= 0; i-- {
|
||||
flag := float32(1 / math.Pow(2, float64(17-i)))
|
||||
//更改对应bit位数据
|
||||
if flag > sum {
|
||||
//大于加速度,设置对应的bit为0
|
||||
accCode = setBit(accCode, uint32(i), 0)
|
||||
} else {
|
||||
//设置对应的bit位1
|
||||
accCode = setBit(accCode, uint32(i), 1)
|
||||
sum = sum - flag
|
||||
}
|
||||
fmt.Println(i, flag, fmt.Sprintf("%032b", accCode), sum)
|
||||
}
|
||||
accCode = accCode << 8 //数据向左移动8位,取后3个字节
|
||||
d2 = byte(accCode >> 16) //取出d2数据
|
||||
d1 = byte(accCode >> 8 << 8) // & 0xff //取出d1
|
||||
d0 = byte(accCode << 20) //& 0xf //取出d0高2位
|
||||
|
||||
//d2 = byte(accCode >> 16) //取出d2数据
|
||||
//d1 = byte(accCode << 8 >> 8) //取出d1
|
||||
//d0 = byte(accCode << 16) //取出d0高2位
|
||||
|
||||
return d2, d1, d0
|
||||
}
|
||||
func decode(d2, d1, d0 byte) float32 {
|
||||
accCode := uint32(d2)<<16 | uint32(d1)<<8 | uint32(d0)>>4
|
||||
|
||||
// 因为编码时进行了左移8位的操作,所以这里需要右移回来
|
||||
accCode >>= 8
|
||||
|
||||
// 重建浮点数
|
||||
var accSpeed float32
|
||||
for i := 0; i <= 17; i++ {
|
||||
bitValue := accCode & (1 << uint32(i))
|
||||
if bitValue != 0 {
|
||||
// 如果该位为1,则累加相应的值到accSpeed
|
||||
accSpeed += float32(1 / math.Pow(2, float64(17-i)))
|
||||
}
|
||||
}
|
||||
|
||||
return accSpeed
|
||||
}
|
||||
func TestAccSpeed(t *testing.T) {
|
||||
|
||||
speed := float32(0.54 / G)
|
||||
fmt.Println("输入acc加速度g", speed)
|
||||
d2, d1, d0 := encode(speed)
|
||||
decode(d2, d1, d0)
|
||||
accSpeed := decode(d2, d1, d0)
|
||||
fmt.Println("输出速度:", accSpeed*G, "加速度:", accSpeed)
|
||||
}
|
||||
|
||||
func TestCode66(t *testing.T) {
|
||||
var accSpeed float32 = 0.44 / 9.8
|
||||
fmt.Println(accSpeed)
|
||||
//生成4字节返回数据
|
||||
var accCode uint32 = 0
|
||||
var sum float32 = 0
|
||||
for i := 16; i >= 0; i-- {
|
||||
flag := float32(1 / math.Pow(2, float64(17-i)))
|
||||
//更改对应bit位数据
|
||||
if flag > accSpeed {
|
||||
//大于加速度,设置对应的bit为0
|
||||
accCode = setBit(accCode, uint32(i), 0)
|
||||
} else {
|
||||
//设置对应的bit位1
|
||||
accCode = setBit(accCode, uint32(i), 1)
|
||||
if sum+flag > accSpeed {
|
||||
//累加和超过accCode停止
|
||||
break
|
||||
}
|
||||
sum = sum + flag
|
||||
}
|
||||
fmt.Println(i, flag, fmt.Sprintf("%b", accCode), sum)
|
||||
}
|
||||
//因为获取但是18位数据,这里向左移动14位(获取之前设置的bit数据)
|
||||
fmt.Println(fmt.Sprintf("%032b", accCode))
|
||||
fmt.Println(math.Float32frombits(accCode&0xffffc000) / 0x80000000)
|
||||
accCode = accCode << 14
|
||||
fmt.Println(fmt.Sprintf("%032b", accCode))
|
||||
fmt.Println(float32(accCode) / 0x80000000)
|
||||
}
|
||||
func TestCode5(t *testing.T) {
|
||||
|
||||
var accSpeed float32 = (0.54 / 9.8) * 2
|
||||
bbc := math.Float32bits(accSpeed)
|
||||
fmt.Println(accSpeed, bbc, fmt.Sprintf("%08b", bbc), float32(bbc))
|
||||
d2 := byte(bbc) & 0xFF
|
||||
d1 := byte((bbc << 8) & 0xFF)
|
||||
d0 := byte((bbc << 16) & 0xFF)
|
||||
|
||||
fmt.Println(fmt.Sprintf("%08b", d2))
|
||||
fmt.Println(fmt.Sprintf("%08b", d1))
|
||||
fmt.Println(fmt.Sprintf("%08b", d0))
|
||||
result := (uint32(d2) << 16) | (uint32(d1) << 8) | (uint32(d0))
|
||||
|
||||
fmt.Println(fmt.Sprintf("%08b", result), float32(result)/float32(0xFFFFFF), math.Float32frombits(result))
|
||||
//arr := []byte{d2, d1, d0}
|
||||
//var sum float32 = 0
|
||||
//isBreak := false
|
||||
//index := 1
|
||||
//for i := 0; i < len(arr); i++ {
|
||||
// d := arr[i]
|
||||
//
|
||||
//}
|
||||
/* for i := 22; i >= 0; i-- {
|
||||
d := float32(1 / math.Pow(2, float64(17-i)))
|
||||
if isBreak {
|
||||
result = setBit(result, uint32(i), 1)
|
||||
|
||||
} else {
|
||||
if d > accSpeed {
|
||||
result = setBit(result, uint32(i), 0)
|
||||
} else if d < accSpeed {
|
||||
result = setBit(result, uint32(i), 1)
|
||||
if sum+d > accSpeed {
|
||||
isBreak = true
|
||||
//break
|
||||
} else {
|
||||
sum += d
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Println(i, i, d, "--------", fmt.Sprintf("%8b", result), result, sum)
|
||||
}*/
|
||||
//aas := math.Float32frombits(result)
|
||||
//fmt.Println(aas)
|
||||
result = result & 0xffffc000
|
||||
fmt.Println(result, float32(result)/dd, fmt.Sprintf("%08b", result))
|
||||
fmt.Println(math.Float32frombits(result) / dd)
|
||||
}
|
||||
func TestCode3(t *testing.T) {
|
||||
var accSpeed float32 = 0.54 / 9.8
|
||||
bbc := math.Float32bits(accSpeed)
|
||||
fmt.Println(accSpeed, bbc, fmt.Sprintf("%08b", math.Float32bits(accSpeed)))
|
||||
|
||||
d2 := byte(bbc) & 0xFF
|
||||
d1 := byte((bbc << 8) & 0xFF)
|
||||
d0 := byte((bbc << 16) & 0xFF)
|
||||
//result := (uint32(d2) << 16) | (uint32(d1) << 8) | (uint32(d0))
|
||||
|
||||
//d0 := byte(bbc & 0xFF)
|
||||
// 提取 D1 字节
|
||||
//d1 := byte((bbc >> 8) & 0xFF)
|
||||
// 提取 D2 字节
|
||||
//d2 := byte((bbc >> 16) & 0xFF)
|
||||
|
||||
fmt.Println(fmt.Sprintf("%08b", d2))
|
||||
fmt.Println(fmt.Sprintf("%08b", d1))
|
||||
fmt.Println(fmt.Sprintf("%08b", d0))
|
||||
fmt.Println("----------------------------")
|
||||
d2 = setBit2(d2, 7, 0)
|
||||
d2 = setBit2(d2, 6, 0)
|
||||
d2 = setBit2(d2, 5, 0)
|
||||
d2 = setBit2(d2, 4, 0)
|
||||
d2 = setBit2(d2, 3, 0)
|
||||
d2 = setBit2(d2, 2, 0)
|
||||
d2 = setBit2(d2, 1, 0)
|
||||
d2 = setBit2(d2, 0, 1)
|
||||
|
||||
fmt.Println(fmt.Sprintf("%08b", d2))
|
||||
fmt.Println(fmt.Sprintf("%08b", d1))
|
||||
fmt.Println(fmt.Sprintf("%08b", d0))
|
||||
var result uint32
|
||||
|
||||
//arr := []byte{d2, d1, d0}
|
||||
//buf := bytes.NewBuffer(arr)
|
||||
//binary.Read(buf, binary.BigEndian, &result)
|
||||
result = (uint32(d2) << 16) | (uint32(d1) << 8) | (uint32(d0))
|
||||
aas := math.Float32frombits(result)
|
||||
fmt.Println(aas)
|
||||
result = result & 0xffffc000
|
||||
fmt.Println(result, fmt.Sprintf("%08b", result))
|
||||
fmt.Println(aas / dd)
|
||||
|
||||
}
|
||||
|
||||
func TestCode2(t *testing.T) {
|
||||
var accSpeed float32 = 0.54 / 9.8
|
||||
fmt.Println(accSpeed)
|
||||
var acc uint32 = math.Float32bits(0)
|
||||
var sum float32 = 0
|
||||
bit := 13
|
||||
for i := 16; i >= 1; i-- {
|
||||
d := float32(1 / math.Pow(2, float64(17-i)))
|
||||
if d > accSpeed {
|
||||
acc = setBit(acc, uint32(i+bit), 0)
|
||||
} else if d < accSpeed {
|
||||
acc = setBit(acc, uint32(i+bit), 1)
|
||||
if sum+d > accSpeed {
|
||||
break
|
||||
} else {
|
||||
sum += d
|
||||
}
|
||||
}
|
||||
fmt.Println(i, i+bit, d, "--------", fmt.Sprintf("%8b", acc), acc, sum)
|
||||
}
|
||||
|
||||
//d2 := byte(acc >> 24)
|
||||
//d1 := byte((acc << 8) >> 24)
|
||||
//d0 := byte((bbc << 16) >> 24)
|
||||
//arr := []byte{d2, d1, 0, 0}
|
||||
//u3 := binary.BigEndian.Uint32(arr)
|
||||
u3 := acc & 0xffffc000
|
||||
fmt.Println(u3, sum)
|
||||
aas := math.Float32frombits(u3) / 0x80000000
|
||||
fmt.Println(aas)
|
||||
}
|
||||
|
||||
const dd = 0x80000000
|
||||
|
||||
func encodeDecimal(decimal float64) ([]byte, []byte) {
|
||||
// 将小数转换为二进制形式
|
||||
binary := fmt.Sprintf("%.15f", decimal)
|
||||
|
||||
// 提取二进制小数部分
|
||||
binaryDecimal := binary[2:]
|
||||
|
||||
// 左对齐并添加零
|
||||
paddedBinaryDecimal := fmt.Sprintf("%015s", binaryDecimal)
|
||||
|
||||
// 将二进制小数转换为字节数组
|
||||
d2 := []byte(paddedBinaryDecimal[:8])
|
||||
d1 := []byte(paddedBinaryDecimal[8:])
|
||||
|
||||
return d2, d1
|
||||
}
|
||||
|
66
third_party/acc/acc_vobc.go
vendored
66
third_party/acc/acc_vobc.go
vendored
@ -2,19 +2,25 @@ package acc
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"joylink.club/bj-rtsts-server/config"
|
||||
"joylink.club/bj-rtsts-server/dto/common_proto"
|
||||
"joylink.club/bj-rtsts-server/dto/state_proto"
|
||||
"joylink.club/bj-rtsts-server/third_party/message"
|
||||
"joylink.club/bj-rtsts-server/third_party/udp"
|
||||
"log/slog"
|
||||
"math"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// AccVobc 加速度计通信服务接口
|
||||
type AccVobc interface {
|
||||
Start(accManager AccVobcManager)
|
||||
Stop()
|
||||
SendAcc(acc *message.Accelerometer)
|
||||
TrainAccSender(info *message.DynamicsTrainInfo, trainState *state_proto.TrainState)
|
||||
}
|
||||
|
||||
type AccVobcManager interface {
|
||||
@ -27,11 +33,6 @@ var (
|
||||
singleObj *accVobcService
|
||||
)
|
||||
|
||||
const (
|
||||
accInterval = 15
|
||||
accSpeedUnit = 9.8
|
||||
)
|
||||
|
||||
func Default() AccVobc {
|
||||
defer initLock.Unlock()
|
||||
initLock.Lock()
|
||||
@ -50,15 +51,59 @@ type accVobcService struct {
|
||||
func (acc *accVobcService) Start(accManager AccVobcManager) {
|
||||
config := accManager.GetRunAccConfig()
|
||||
if config.RemoteIp == "" || config.RemotePort <= 0 || !config.Open {
|
||||
return
|
||||
}
|
||||
acc.vobcClient = udp.NewClient(fmt.Sprintf("%v:%v", config.RemoteIp, config.RemotePort))
|
||||
ctx, cancleFunc := context.WithCancel(context.Background())
|
||||
acc.controlContext = cancleFunc
|
||||
acc.radarVobcManager = accManager
|
||||
go acc.sendTask(ctx)
|
||||
}
|
||||
|
||||
func (acc *accVobcService) sendTask(ctx context.Context) {
|
||||
func (avs *accVobcService) SendAcc(acc *message.Accelerometer) {
|
||||
if avs.vobcClient != nil {
|
||||
data := acc.Encode()
|
||||
hexStr := hex.EncodeToString(data)
|
||||
slog.Info("发送 加速度消息数据:", hexStr)
|
||||
err := avs.vobcClient.Send(data)
|
||||
if err != nil {
|
||||
slog.Info("发送 加速度消息数据失败:", hexStr)
|
||||
return
|
||||
}
|
||||
}
|
||||
//avs.vobcClient.Send(acc.BuildCanData())
|
||||
}
|
||||
func (avs *accVobcService) TrainAccSender(info *message.DynamicsTrainInfo, trainState *state_proto.TrainState) {
|
||||
if trainState.VobcState.Tc1Active && trainState.TrainEndsA.AccEnable {
|
||||
s := parseAccSpeedData(info.Acceleration, trainState.TrainEndsA)
|
||||
avs.SendAcc(&message.Accelerometer{Acc: s})
|
||||
} else if trainState.VobcState.Tc2Active && trainState.TrainEndsB.AccEnable {
|
||||
s := parseAccSpeedData(info.Acceleration, trainState.TrainEndsB)
|
||||
avs.SendAcc(&message.Accelerometer{Acc: s})
|
||||
} else {
|
||||
avs.SendAcc(&message.Accelerometer{Acc: 0})
|
||||
}
|
||||
}
|
||||
|
||||
func parseAccSpeedData(accSpeed float32, trainEndState *common_proto.TrainEndsState) float32 {
|
||||
//如果差值速度和速度输出都填写,那么就以速度输出为优先
|
||||
//如果动力学速度-差值速度小于0,呢么输出的就是0(不能小于0)
|
||||
|
||||
trainEndState.AccCheckTime = int32(trainEndState.AccCheckTimeOverAt - time.Now().Unix())
|
||||
if trainEndState.AccCheckTime <= 0 {
|
||||
//判断雷达检测时间是否到期
|
||||
trainEndState.AccCheckTime = 0
|
||||
if trainEndState.AccCheckTimeOverAt != 0 {
|
||||
trainEndState.AccCheckSpeedDiff = 0
|
||||
return accSpeed
|
||||
}
|
||||
}
|
||||
if trainEndState.AccCheckSpeedDiff > 0 {
|
||||
//如果雷达检测速度差值填写,那么就以速度差值为主
|
||||
return float32(math.Abs(float64(accSpeed - trainEndState.AccCheckSpeedDiff)))
|
||||
} else {
|
||||
return accSpeed
|
||||
}
|
||||
}
|
||||
|
||||
/*func (acc *accVobcService) sendTask(ctx context.Context) {
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
@ -67,13 +112,14 @@ func (acc *accVobcService) sendTask(ctx context.Context) {
|
||||
}
|
||||
trainStatus := acc.radarVobcManager.FindAccTrain()
|
||||
if trainStatus != nil {
|
||||
// 发送加速度信息
|
||||
speedAcc := trainStatus.DynamicState.Acceleration
|
||||
t := speedAcc / accSpeedUnit
|
||||
acc.vobcClient.SendMsg(&message.Accelerometer{Acc: math.Float32bits(t)})
|
||||
}
|
||||
time.Sleep(time.Millisecond * accInterval)
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
func (acc *accVobcService) Stop() {
|
||||
if acc.controlContext != nil {
|
||||
|
61
third_party/axle_device/beijing12/msg/common.go
vendored
Normal file
61
third_party/axle_device/beijing12/msg/common.go
vendored
Normal file
@ -0,0 +1,61 @@
|
||||
package msg
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
)
|
||||
|
||||
const (
|
||||
ProtocolType_Sync = 0x01
|
||||
ProtocolType_NoSync = 0x02
|
||||
)
|
||||
|
||||
const (
|
||||
MessageType_A = 0x80
|
||||
MessageType_B = 0x81
|
||||
MessageType_SSE = 0x90
|
||||
MessageType_SSR = 0x91
|
||||
)
|
||||
|
||||
const (
|
||||
CRC_POLY_1 = 0x100D4E63 //通道1的CRC多项式
|
||||
CRC_POLY_2 = 0x8CE56011 //通道2的CRC多项式
|
||||
)
|
||||
|
||||
const (
|
||||
SCW_1 = 0xAE390B5A //通道1的SCW
|
||||
SCW_2 = 0xC103589C //通道2的SCW
|
||||
)
|
||||
|
||||
const (
|
||||
T_POLY_1 uint32 = 0x0FC22F87 //通道1的时间戳生成多项式
|
||||
T_POLY_2 uint32 = 0xC3E887E1 //通道2的时间戳生成多项式
|
||||
)
|
||||
|
||||
const Twait_sse = 3 //默认sse等待回应的周期数
|
||||
|
||||
func GetMessageType(data []byte) byte {
|
||||
return data[1]
|
||||
}
|
||||
|
||||
type MsgHeader struct {
|
||||
ProtocolType byte //协议交互类别
|
||||
MessageType byte //报文类型
|
||||
SourceAddr uint16 //源地址
|
||||
TargetAddr uint16 //目的地址
|
||||
}
|
||||
|
||||
func (m *MsgHeader) encode() []byte {
|
||||
var data []byte
|
||||
data = append(data, m.ProtocolType)
|
||||
data = append(data, m.MessageType)
|
||||
data = binary.LittleEndian.AppendUint16(data, m.SourceAddr)
|
||||
data = binary.LittleEndian.AppendUint16(data, m.TargetAddr)
|
||||
return data
|
||||
}
|
||||
|
||||
func (m *MsgHeader) decode(data []byte) error {
|
||||
buf := bytes.NewBuffer(data)
|
||||
err := binary.Read(buf, binary.LittleEndian, m)
|
||||
return err
|
||||
}
|
142
third_party/axle_device/beijing12/msg/rsd.go
vendored
Normal file
142
third_party/axle_device/beijing12/msg/rsd.go
vendored
Normal file
@ -0,0 +1,142 @@
|
||||
package msg
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"joylink.club/bj-rtsts-server/third_party/message"
|
||||
)
|
||||
|
||||
// 实时安全数据消息
|
||||
type RsdMsg struct {
|
||||
MsgHeader
|
||||
SeqNum uint32 //序列号
|
||||
UserDataLen uint16 //用户数据包字节总数+8
|
||||
Svc1 uint32 //CRC1^SID1^T1(N)^SCW1
|
||||
Svc2 uint32 //CRC2^SID2^T2(N)^SCW2
|
||||
|
||||
UserData []byte //用户数据包
|
||||
|
||||
Tail uint16 //报文尾 CRC16
|
||||
}
|
||||
|
||||
func (r *RsdMsg) Encode() []byte {
|
||||
data := r.MsgHeader.encode()
|
||||
data = binary.LittleEndian.AppendUint32(data, r.SeqNum)
|
||||
data = binary.LittleEndian.AppendUint16(data, r.UserDataLen)
|
||||
data = binary.LittleEndian.AppendUint32(data, r.Svc1)
|
||||
data = binary.LittleEndian.AppendUint32(data, r.Svc2)
|
||||
data = append(data, r.UserData...)
|
||||
r.Tail = message.Rssp_I_Crc16(data)
|
||||
data = binary.LittleEndian.AppendUint16(data, r.Tail)
|
||||
return data
|
||||
}
|
||||
|
||||
func (r *RsdMsg) Decode(data []byte) error {
|
||||
err := r.MsgHeader.decode(data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
buf := bytes.NewBuffer(data[6:]) //去掉报文头的6个字节
|
||||
fields := []any{&r.SeqNum, &r.UserDataLen, &r.Svc1, &r.Svc2}
|
||||
for _, field := range fields {
|
||||
err := binary.Read(buf, binary.LittleEndian, field)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
r.UserData = data[len(data)-buf.Len() : len(data)-2]
|
||||
r.Tail = binary.LittleEndian.Uint16(data[len(data)-2:])
|
||||
return nil
|
||||
}
|
||||
|
||||
// RsdMsgBuilder 用来构建RSD,将无需用户赋值的字段去掉了
|
||||
type RsdMsgBuilder struct {
|
||||
MsgHeader
|
||||
SeqNum uint32
|
||||
Svc1 uint32
|
||||
Svc2 uint32
|
||||
UserData []byte
|
||||
}
|
||||
|
||||
func (b *RsdMsgBuilder) Build() *RsdMsg {
|
||||
return &RsdMsg{
|
||||
MsgHeader: b.MsgHeader,
|
||||
SeqNum: b.SeqNum,
|
||||
UserDataLen: uint16(len(b.UserData) + 8),
|
||||
Svc1: b.Svc1,
|
||||
Svc2: b.Svc2,
|
||||
UserData: b.UserData,
|
||||
Tail: message.Rssp_I_Crc16(b.UserData),
|
||||
}
|
||||
}
|
||||
|
||||
func (b *RsdMsgBuilder) Encode() []byte {
|
||||
return b.Build().Encode()
|
||||
}
|
||||
|
||||
// CmdInfos 来自联锁的数据帧
|
||||
type CmdInfos []*cmdInfo
|
||||
|
||||
func (f *CmdInfos) Decode(data []byte) error {
|
||||
for _, b := range data {
|
||||
cmdInfo := &cmdInfo{}
|
||||
cmdInfo.decode(b)
|
||||
*f = append(*f, cmdInfo)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// StateInfos 发给联锁的数据帧
|
||||
type StateInfos []*StateInfo
|
||||
|
||||
func (t StateInfos) Encode() []byte {
|
||||
var data []byte
|
||||
for _, info := range t {
|
||||
data = append(data, info.encode()...)
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
type cmdInfo struct {
|
||||
PRST bool //不使用
|
||||
RST bool //不使用
|
||||
DRST bool //直接复位 位索引5
|
||||
RRST bool //不使用
|
||||
RSTR bool //不使用
|
||||
PDRST bool //预复位 位索引2
|
||||
PRRST bool //不使用
|
||||
}
|
||||
|
||||
func (c *cmdInfo) decode(data byte) {
|
||||
c.DRST = (data>>5)&1 == 1
|
||||
c.PDRST = (data>>2)&1 == 1
|
||||
}
|
||||
|
||||
type StateInfo struct {
|
||||
CLR bool //计轴出清 位索引0-7(0字节的7位)
|
||||
OCC bool //计轴占用 0-6
|
||||
RAC bool //计轴复位反馈 1-6
|
||||
RJO bool //运营原因拒绝计轴复位 1-5
|
||||
RJT bool //技术原因拒绝计轴复位 1-4
|
||||
}
|
||||
|
||||
func (s *StateInfo) encode() []byte {
|
||||
var b0 byte
|
||||
var b1 byte
|
||||
if s.CLR {
|
||||
b0 = b0 | 1<<7
|
||||
}
|
||||
if s.OCC {
|
||||
b0 = b0 | 1<<6
|
||||
}
|
||||
if s.RAC {
|
||||
b1 = b1 | 1<<6
|
||||
}
|
||||
if s.RJO {
|
||||
b1 = b1 | 1<<5
|
||||
}
|
||||
if s.RJT {
|
||||
b1 = b1 | 1<<4
|
||||
}
|
||||
return []byte{b0, b1}
|
||||
}
|
30
third_party/axle_device/beijing12/msg/sse.go
vendored
Normal file
30
third_party/axle_device/beijing12/msg/sse.go
vendored
Normal file
@ -0,0 +1,30 @@
|
||||
package msg
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"joylink.club/bj-rtsts-server/third_party/message"
|
||||
)
|
||||
|
||||
type SseMsg struct {
|
||||
MsgHeader
|
||||
SeqNum uint32 // 序列号
|
||||
SeqEnq1 uint32 //时序校正请求通道1 SID_1^T_1(NE)
|
||||
SeqEnq2 uint32 // 时序校正请求通道2 SID_2^T_2(NE)
|
||||
Tail uint16 //报文位 CRC16
|
||||
}
|
||||
|
||||
func (s *SseMsg) Encode() []byte {
|
||||
data := s.MsgHeader.encode()
|
||||
data = binary.LittleEndian.AppendUint32(data, s.SeqNum)
|
||||
data = binary.LittleEndian.AppendUint32(data, s.SeqEnq1)
|
||||
data = binary.LittleEndian.AppendUint32(data, s.SeqEnq2)
|
||||
s.Tail = message.Rssp_I_Crc16(data)
|
||||
data = binary.LittleEndian.AppendUint16(data, s.Tail)
|
||||
return data
|
||||
}
|
||||
|
||||
func (s *SseMsg) Decode(data []byte) error {
|
||||
buf := bytes.NewBuffer(data)
|
||||
return binary.Read(buf, binary.LittleEndian, s)
|
||||
}
|
34
third_party/axle_device/beijing12/msg/ssr.go
vendored
Normal file
34
third_party/axle_device/beijing12/msg/ssr.go
vendored
Normal file
@ -0,0 +1,34 @@
|
||||
package msg
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"joylink.club/bj-rtsts-server/third_party/message"
|
||||
)
|
||||
|
||||
type SsrMsg struct {
|
||||
MsgHeader
|
||||
SeqNumSsr uint32 // 应答方的序列号
|
||||
SeqNumSse uint32 // 请求方的序列号
|
||||
SeqInit1 uint32 // 时序初始化通道1 SEQENQ_1^SID_1^T_1(NR)^DATAVER_1
|
||||
SeqInit2 uint32 // 时序初始化通道2 SEQENQ_2^SID_2^T_2(NR)^DATAVER_2
|
||||
DataVer byte // 数据版本号 预留固定值0x01
|
||||
Tail uint16 // 报文位 CRC16
|
||||
}
|
||||
|
||||
func (s *SsrMsg) Encode() []byte {
|
||||
data := s.MsgHeader.encode()
|
||||
data = binary.LittleEndian.AppendUint32(data, s.SeqNumSsr)
|
||||
data = binary.LittleEndian.AppendUint32(data, s.SeqNumSse)
|
||||
data = binary.LittleEndian.AppendUint32(data, s.SeqInit1)
|
||||
data = binary.LittleEndian.AppendUint32(data, s.SeqInit2)
|
||||
data = append(data, s.DataVer)
|
||||
s.Tail = message.Rssp_I_Crc16(data)
|
||||
data = binary.LittleEndian.AppendUint16(data, s.Tail)
|
||||
return data
|
||||
}
|
||||
|
||||
func (s *SsrMsg) Decode(data []byte) error {
|
||||
buf := bytes.NewBuffer(data)
|
||||
return binary.Read(buf, binary.LittleEndian, s)
|
||||
}
|
650
third_party/axle_device/beijing12/service.go
vendored
Normal file
650
third_party/axle_device/beijing12/service.go
vendored
Normal file
@ -0,0 +1,650 @@
|
||||
package beijing12
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"joylink.club/bj-rtsts-server/config"
|
||||
"joylink.club/bj-rtsts-server/dto/state_proto"
|
||||
"joylink.club/bj-rtsts-server/sys_error"
|
||||
"joylink.club/bj-rtsts-server/third_party/axle_device/beijing12/msg"
|
||||
"joylink.club/bj-rtsts-server/third_party/message"
|
||||
"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"
|
||||
"joylink.club/rtsssimulation/component"
|
||||
"joylink.club/rtsssimulation/entity"
|
||||
"joylink.club/rtsssimulation/fi"
|
||||
"joylink.club/rtsssimulation/repository/model/proto"
|
||||
"log/slog"
|
||||
"runtime/debug"
|
||||
"strconv"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
var ( //日志
|
||||
logTag = "[北京12号线计轴通信]"
|
||||
privateLogger *slog.Logger
|
||||
loggerInit sync.Once
|
||||
)
|
||||
|
||||
// key-集中站编号
|
||||
var contextMap = make(map[string]*serviceContext)
|
||||
var mu = sync.Mutex{}
|
||||
|
||||
type serviceContext struct {
|
||||
sim *memory.VerifySimulation
|
||||
config config.RsspAxleConfig
|
||||
server udp.UdpServer
|
||||
client udp.UdpClient
|
||||
cancelFunc context.CancelFunc
|
||||
ciSectionIndexConfigs []*proto.CiSectionCodePoint
|
||||
sectionStateMap map[string]*fi.PhysicalSectionState //存储预复位/复位请求的返回结果
|
||||
state tpapi.ThirdPartyApiServiceState // 服务状态(用于展示)
|
||||
|
||||
remoteAddr uint16 //联锁地址 从配置中的16进制字符串转来的
|
||||
localAddr uint16 //计轴地址 从配置中的16进制字符串转来的
|
||||
remoteSid1 uint32 //联锁SID1 从配置中的16进制字符串转来的
|
||||
remoteSid2 uint32 //联锁SID2 从配置中的16进制字符串转来的
|
||||
localSid1 uint32 //计轴SID1 从配置中的16进制字符串转来的
|
||||
localSid2 uint32 //计轴SID2 从配置中的16进制字符串转来的
|
||||
remoteSinit1 uint32 //联锁SINIT1 从配置中的16进制字符串转来的
|
||||
remoteSinit2 uint32 //联锁SINIT2 从配置中的16进制字符串转来的
|
||||
localSinit1 uint32 //计轴SINT1 从配置中的16进制字符串转来的
|
||||
localSinit2 uint32 //计轴SINT2 从配置中的16进制字符串转来的
|
||||
remoteDataVer1 uint32 //联锁DATAVER1 从配置中的16进制字符串转来的
|
||||
remoteDataVer2 uint32 //联锁DATAVER2 从配置中的16进制字符串转来的
|
||||
localDataVer1 uint32 //计轴DATAVER1 从配置中的16进制字符串转来的
|
||||
localDataVer2 uint32 //计轴DATAVER2 从配置中的16进制字符串转来的
|
||||
msgChan <-chan []byte //消息队列
|
||||
seqNum uint32 //当前的序列号
|
||||
lastSeqParam1 uint32 //最近一次的有效时序参数 remoteSinit1~+[remoteSid1^t1(n)]
|
||||
lastSeqParam2 uint32 //最近一次的有效时序参数 remoteSinit2~+[remoteSid2^t2(n)]
|
||||
lfsr1 *lfsr //用来计算时间戳的lfsr
|
||||
lfsr2 *lfsr //用来计算时间戳的lfsr
|
||||
sseMsg *msg.SseMsg //发送出去的时序校验请求
|
||||
sseWaitTimer <-chan time.Time //sse超时定时器
|
||||
precSinit1 uint32 // 用来从SSR消息的SeqInit中提取时序参数
|
||||
precSinit2 uint32 // 用来从SSR消息的SeqInit中提取时序参数
|
||||
}
|
||||
|
||||
func CollectAllServices() []tpapi.ThirdPartyApiService {
|
||||
var services []tpapi.ThirdPartyApiService
|
||||
for _, service := range contextMap {
|
||||
services = append(services, service)
|
||||
}
|
||||
return services
|
||||
}
|
||||
|
||||
func (s *serviceContext) Type() state_proto.SimulationThirdPartyApiService_Type {
|
||||
return state_proto.SimulationThirdPartyApiService_AxleSection
|
||||
}
|
||||
|
||||
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) {
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
//检查服务启动条件
|
||||
rsspConfig := simulation.GetRunConfig().RsspAxleConfig
|
||||
if !rsspConfig.Open {
|
||||
return
|
||||
}
|
||||
if contextMap[rsspConfig.StationCode] != nil {
|
||||
panic(sys_error.New(fmt.Sprintf("%s集中站[%s]服务已启动", logTag, rsspConfig.StationCode)))
|
||||
}
|
||||
station := simulation.Repo.FindStationByStationName(rsspConfig.StationCode)
|
||||
if station == nil {
|
||||
panic(sys_error.New(fmt.Sprintf("%s集中站[%s]不存在", logTag, rsspConfig.StationCode)))
|
||||
}
|
||||
ref := simulation.Repo.GetCentralizedStationRef(station.Id())
|
||||
if ref == nil {
|
||||
panic(sys_error.New(fmt.Sprintf("%s集中站[%s]关联数据不存在", logTag, rsspConfig.StationCode)))
|
||||
}
|
||||
if len(ref.SectionCodePoints) == 0 {
|
||||
logger().Warn(fmt.Sprintf("集中站[%s]无区段编码数据,服务不启动", rsspConfig.StationCode))
|
||||
return
|
||||
}
|
||||
//初始化服务上下文
|
||||
serviceCtx := initServiceContext(rsspConfig, ref, simulation)
|
||||
//准备启动服务
|
||||
msgChan := make(chan []byte, 100)
|
||||
serviceCtx.msgChan = msgChan
|
||||
netAConfig := rsspConfig.NetAConfig
|
||||
server := udp.NewServer(fmt.Sprintf(":%d", netAConfig.LocalPort), func(b []byte) {
|
||||
//logger().Info(fmt.Sprintf("收到数据:%x", b))
|
||||
msgChan <- b
|
||||
})
|
||||
client := udp.NewClient(fmt.Sprintf("%s:%d", netAConfig.RemoteIp, netAConfig.RemotePort))
|
||||
err := server.Listen()
|
||||
if err != nil {
|
||||
panic(sys_error.New(fmt.Sprintf("%s集中站[%s]服务启动失败", logTag, rsspConfig.StationCode)))
|
||||
} else {
|
||||
logger().Info(fmt.Sprintf("监听[:%d]", netAConfig.LocalPort))
|
||||
}
|
||||
serviceCtx.server = server
|
||||
serviceCtx.client = client
|
||||
cancelCtx, cancelFunc := context.WithCancel(context.Background())
|
||||
serviceCtx.cancelFunc = cancelFunc
|
||||
serviceCtx.runCollectTask(cancelCtx)
|
||||
serviceCtx.runHandleMsgTask(cancelCtx)
|
||||
contextMap[rsspConfig.StationCode] = serviceCtx
|
||||
}
|
||||
|
||||
func initServiceContext(rsspConfig config.RsspAxleConfig, ref *proto.CentralizedStationRef, simulation *memory.VerifySimulation) *serviceContext {
|
||||
sourceAddr, err := strconv.ParseUint(rsspConfig.NetAConfig.RemoteAddr, 16, 16)
|
||||
if err != nil {
|
||||
panic(sys_error.New(fmt.Sprintf("%s集中站[%s]解析源地址[%s]出错", logTag, rsspConfig.StationCode, rsspConfig.NetAConfig.RemoteAddr)))
|
||||
}
|
||||
targetAddr, err := strconv.ParseUint(rsspConfig.NetAConfig.LocalAddr, 16, 16)
|
||||
if err != nil {
|
||||
panic(sys_error.New(fmt.Sprintf("%s集中站[%s]解析目的地址[%s]出错", logTag, rsspConfig.StationCode, rsspConfig.NetAConfig.LocalAddr)))
|
||||
}
|
||||
remoteSid1, err := strconv.ParseUint(rsspConfig.NetAConfig.RemoteSid1, 16, 32)
|
||||
if err != nil {
|
||||
panic(sys_error.New(fmt.Sprintf("%s集中站[%s]解析联锁SID1[%s]出错", logTag, rsspConfig.StationCode, rsspConfig.NetAConfig.RemoteSid1)))
|
||||
}
|
||||
remoteSid2, err := strconv.ParseUint(rsspConfig.NetAConfig.RemoteSid2, 16, 32)
|
||||
if err != nil {
|
||||
panic(sys_error.New(fmt.Sprintf("%s集中站[%s]解析联锁SID2[%s]出错", logTag, rsspConfig.StationCode, rsspConfig.NetAConfig.RemoteSid2)))
|
||||
}
|
||||
localSid1, err := strconv.ParseUint(rsspConfig.NetAConfig.LocalSid1, 16, 32)
|
||||
if err != nil {
|
||||
panic(sys_error.New(fmt.Sprintf("%s集中站[%s]解析计轴SID1[%s]出错", logTag, rsspConfig.StationCode, rsspConfig.NetAConfig.LocalSid1)))
|
||||
}
|
||||
localSid2, err := strconv.ParseUint(rsspConfig.NetAConfig.LocalSid2, 16, 32)
|
||||
if err != nil {
|
||||
panic(sys_error.New(fmt.Sprintf("%s集中站[%s]解析计轴SID2[%s]出错", logTag, rsspConfig.StationCode, rsspConfig.NetAConfig.LocalSid2)))
|
||||
}
|
||||
remoteSinit1, err := strconv.ParseUint(rsspConfig.NetAConfig.RemoteSinit1, 16, 32)
|
||||
if err != nil {
|
||||
panic(sys_error.New(fmt.Sprintf("%s集中站[%s]解析联锁SINT1[%s]出错", logTag, rsspConfig.StationCode, rsspConfig.NetAConfig.RemoteSinit1)))
|
||||
}
|
||||
remoteSinit2, err := strconv.ParseUint(rsspConfig.NetAConfig.RemoteSinit2, 16, 32)
|
||||
if err != nil {
|
||||
panic(sys_error.New(fmt.Sprintf("%s集中站[%s]解析联锁SINT2[%s]出错", logTag, rsspConfig.StationCode, rsspConfig.NetAConfig.RemoteSinit2)))
|
||||
}
|
||||
localSinit1, err := strconv.ParseUint(rsspConfig.NetAConfig.LocalSinit1, 16, 32)
|
||||
if err != nil {
|
||||
panic(sys_error.New(fmt.Sprintf("%s集中站[%s]解析计轴SINT1[%s]出错", logTag, rsspConfig.StationCode, rsspConfig.NetAConfig.LocalSinit1)))
|
||||
}
|
||||
localSinit2, err := strconv.ParseUint(rsspConfig.NetAConfig.LocalSinit2, 16, 32)
|
||||
if err != nil {
|
||||
panic(sys_error.New(fmt.Sprintf("%s集中站[%s]解析计轴SINT2[%s]出错", logTag, rsspConfig.StationCode, rsspConfig.NetAConfig.LocalSinit2)))
|
||||
}
|
||||
remoteDataVer1, err := strconv.ParseUint(rsspConfig.NetAConfig.RemoteDataVer1, 16, 32)
|
||||
if err != nil {
|
||||
panic(sys_error.New(fmt.Sprintf("%s集中站[%s]解析联锁DATAVER_1[%s]出错", logTag, rsspConfig.StationCode, rsspConfig.NetAConfig.RemoteDataVer1)))
|
||||
}
|
||||
remoteDataVer2, err := strconv.ParseUint(rsspConfig.NetAConfig.RemoteDataVer2, 16, 32)
|
||||
if err != nil {
|
||||
panic(sys_error.New(fmt.Sprintf("%s集中站[%s]解析联锁DATAVER_2[%s]出错", logTag, rsspConfig.StationCode, rsspConfig.NetAConfig.RemoteDataVer2)))
|
||||
}
|
||||
localDataVer1, err := strconv.ParseUint(rsspConfig.NetAConfig.LocalDataVer1, 16, 32)
|
||||
if err != nil {
|
||||
panic(sys_error.New(fmt.Sprintf("%s集中站[%s]解析计轴DATAVER_1[%s]出错", logTag, rsspConfig.StationCode, rsspConfig.NetAConfig.LocalDataVer1)))
|
||||
}
|
||||
localDataVer2, err := strconv.ParseUint(rsspConfig.NetAConfig.LocalDataVer2, 16, 32)
|
||||
if err != nil {
|
||||
panic(sys_error.New(fmt.Sprintf("%s集中站[%s]解析计轴DATAVER_2[%s]出错", logTag, rsspConfig.StationCode, rsspConfig.NetAConfig.LocalDataVer2)))
|
||||
}
|
||||
//服务初始化及启动
|
||||
serviceCtx := &serviceContext{
|
||||
sim: simulation,
|
||||
config: rsspConfig,
|
||||
ciSectionIndexConfigs: ref.SectionCodePoints,
|
||||
sectionStateMap: make(map[string]*fi.PhysicalSectionState),
|
||||
|
||||
remoteAddr: uint16(sourceAddr),
|
||||
localAddr: uint16(targetAddr),
|
||||
|
||||
remoteSid1: uint32(remoteSid1),
|
||||
remoteSid2: uint32(remoteSid2),
|
||||
localSid1: uint32(localSid1),
|
||||
localSid2: uint32(localSid2),
|
||||
|
||||
remoteSinit1: uint32(remoteSinit1),
|
||||
remoteSinit2: uint32(remoteSinit2),
|
||||
localSinit1: uint32(localSinit1),
|
||||
localSinit2: uint32(localSinit2),
|
||||
|
||||
remoteDataVer1: uint32(remoteDataVer1),
|
||||
remoteDataVer2: uint32(remoteDataVer2),
|
||||
localDataVer1: uint32(localDataVer1),
|
||||
localDataVer2: uint32(localDataVer2),
|
||||
|
||||
lfsr1: &lfsr{value: uint32(localSid1), poly: msg.T_POLY_1},
|
||||
lfsr2: &lfsr{value: uint32(localSid2), poly: msg.T_POLY_2},
|
||||
|
||||
precSinit1: calculatePrecSinit(uint32(remoteSinit1), uint32(localSid1), uint32(remoteDataVer1)),
|
||||
precSinit2: calculatePrecSinit(uint32(remoteSinit2), uint32(localSid2), uint32(remoteDataVer2)),
|
||||
}
|
||||
return serviceCtx
|
||||
}
|
||||
|
||||
func calculatePrecSinit(remoteSinit uint32, localSid uint32, remoteDataVer uint32) uint32 {
|
||||
l1 := lfsr{value: remoteSinit, poly: msg.T_POLY_1}
|
||||
l1.add(localSid ^ remoteDataVer)
|
||||
valueTmp := l1.value
|
||||
l1.load(0).post(valueTmp)
|
||||
return l1.value
|
||||
}
|
||||
|
||||
func Stop(simulation *memory.VerifySimulation) {
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
rsspConfig := simulation.GetRunConfig().RsspAxleConfig
|
||||
serviceCtx := contextMap[rsspConfig.StationCode]
|
||||
if serviceCtx == nil {
|
||||
return
|
||||
}
|
||||
serviceCtx.stop()
|
||||
delete(contextMap, rsspConfig.StationCode)
|
||||
}
|
||||
|
||||
func (s *serviceContext) stop() {
|
||||
if s.server != nil {
|
||||
s.server.Close()
|
||||
}
|
||||
if s.client != nil {
|
||||
s.client.Close()
|
||||
}
|
||||
s.cancelFunc()
|
||||
}
|
||||
|
||||
func (s *serviceContext) runCollectTask(ctx context.Context) {
|
||||
go func() {
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
logger().Error("状态收集任务出错,记录后重启", "error", err, "stack", string(debug.Stack()))
|
||||
s.runCollectTask(ctx)
|
||||
}
|
||||
}()
|
||||
for range time.Tick(time.Duration(s.config.NetAConfig.Period) * time.Millisecond) {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
default:
|
||||
frame := s.collect()
|
||||
data := frame.Encode()
|
||||
err := s.client.Send(data)
|
||||
if err != nil {
|
||||
s.state = tpapi.ThirdPartyState_Broken
|
||||
logger().Error("发送状态数据失败", "error", err)
|
||||
} else {
|
||||
s.state = tpapi.ThirdPartyState_Normal
|
||||
//logger().Info(fmt.Sprintf("发送数据:%x", data))
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func (s *serviceContext) collect() *msg.RsdMsgBuilder {
|
||||
worldData := entity.GetWorldData(s.sim.World)
|
||||
stateInfos := msg.StateInfos{}
|
||||
for _, cfg := range s.ciSectionIndexConfigs {
|
||||
entry := worldData.EntityMap[cfg.SectionId]
|
||||
if entry == nil {
|
||||
logger().Error(fmt.Sprintf("没有id[%s]的区段实体", cfg.SectionId))
|
||||
}
|
||||
am := component.PhysicalSectionManagerType.Get(entry)
|
||||
stateInfo := &msg.StateInfo{
|
||||
CLR: !am.Occupied,
|
||||
OCC: am.Occupied,
|
||||
}
|
||||
stateInfos = append(stateInfos, stateInfo)
|
||||
//预复位/复位驱动反馈状态
|
||||
state := s.sectionStateMap[cfg.SectionId]
|
||||
delete(s.sectionStateMap, cfg.SectionId)
|
||||
if state != nil {
|
||||
stateInfo.RAC = state.Rac
|
||||
stateInfo.RJO = state.Rjo
|
||||
stateInfo.RJT = state.Rjt
|
||||
}
|
||||
}
|
||||
userData := []byte{0x00} //检查字节
|
||||
userData = append(userData, stateInfos.Encode()...)
|
||||
//更新序列号及时间戳
|
||||
s.seqNum++
|
||||
s.lfsr1.add(0)
|
||||
s.lfsr2.add(0)
|
||||
//构建消息
|
||||
builder := &msg.RsdMsgBuilder{
|
||||
MsgHeader: msg.MsgHeader{
|
||||
ProtocolType: msg.ProtocolType_Sync,
|
||||
MessageType: msg.MessageType_B, //从抓包数据里看到的
|
||||
SourceAddr: s.localAddr,
|
||||
TargetAddr: s.remoteAddr,
|
||||
},
|
||||
SeqNum: s.seqNum,
|
||||
Svc1: s.calculateSvc1(userData),
|
||||
Svc2: s.calculateSvc2(userData),
|
||||
UserData: userData,
|
||||
}
|
||||
return builder
|
||||
}
|
||||
|
||||
func (s *serviceContext) calculateSvc1(userData []byte) uint32 {
|
||||
return message.Rssp_I_Crc32C1(userData) ^ s.localSid1 ^ s.lfsr1.value ^ msg.SCW_1
|
||||
}
|
||||
|
||||
func (s *serviceContext) calculateSvc2(userData []byte) uint32 {
|
||||
return message.Rssp_I_Crc32C2(userData) ^ s.localSid2 ^ s.lfsr2.value ^ msg.SCW_2
|
||||
}
|
||||
|
||||
func (s *serviceContext) runHandleMsgTask(ctx context.Context) {
|
||||
go func() {
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
logger().Error("消息处理任务出错,记录后重启", "error", err, "stack", string(debug.Stack()))
|
||||
s.runHandleMsgTask(ctx)
|
||||
}
|
||||
}()
|
||||
for {
|
||||
if len(s.msgChan) > 10 {
|
||||
logger().Warn(fmt.Sprintf("消息处理耗时过长,消息队列长度:%d", len(s.msgChan)))
|
||||
}
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
case <-s.sseWaitTimer: //SSE消息等待超时
|
||||
s.sseMsg = nil
|
||||
case data := <-s.msgChan:
|
||||
messageType := msg.GetMessageType(data)
|
||||
switch messageType {
|
||||
case msg.MessageType_A, msg.MessageType_B:
|
||||
s.handleRsdMsg(data)
|
||||
case msg.MessageType_SSE:
|
||||
s.handleSseMsg(data)
|
||||
case msg.MessageType_SSR:
|
||||
s.handleSsrMsg(data)
|
||||
default:
|
||||
logger().Warn(fmt.Sprintf("未知的消息类型[%x]", messageType))
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func (s *serviceContext) handleRsdMsg(data []byte) {
|
||||
if s.sseMsg != nil { //正在时序校正过程中
|
||||
return
|
||||
}
|
||||
rsdMsg := &msg.RsdMsg{}
|
||||
err := rsdMsg.Decode(data)
|
||||
if err != nil {
|
||||
logger().Error("解析RSD数据出错", "error", err)
|
||||
return
|
||||
}
|
||||
//校验
|
||||
validateResult := s.validateRsdMsg(rsdMsg, data)
|
||||
if validateResult == 0 {
|
||||
return
|
||||
} else if validateResult == 2 {
|
||||
//开启时序校正流程
|
||||
logger().Error("时序校验失败,开始时序校正")
|
||||
s.startSeeProgress()
|
||||
return
|
||||
}
|
||||
//流程处理
|
||||
cmdInfos := msg.CmdInfos{}
|
||||
err = cmdInfos.Decode(rsdMsg.UserData[1:]) //用户数据第一个字节是[检查字节]
|
||||
if err != nil {
|
||||
logger().Error("解析命令信息出错", "error", err)
|
||||
return
|
||||
}
|
||||
//驱动
|
||||
for i, cmdInfo := range cmdInfos {
|
||||
sectionIndexConfig := s.ciSectionIndexConfigs[i]
|
||||
if cmdInfo.PDRST {
|
||||
state, err := fi.PhysicalSectionPdrstDrive(s.sim.World, sectionIndexConfig.SectionId)
|
||||
if err != nil {
|
||||
logger().Error("计轴预复位驱动出错", "error", err)
|
||||
}
|
||||
s.sectionStateMap[sectionIndexConfig.SectionId] = state
|
||||
}
|
||||
if cmdInfo.DRST {
|
||||
state, err := fi.PhysicalSectionDrstDrive(s.sim.World, sectionIndexConfig.SectionId)
|
||||
if err != nil {
|
||||
logger().Error("计轴复位驱动出错", "error", err)
|
||||
}
|
||||
s.sectionStateMap[sectionIndexConfig.SectionId] = state
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *serviceContext) handleSseMsg(data []byte) {
|
||||
sseMsg := &msg.SseMsg{}
|
||||
err := sseMsg.Decode(data)
|
||||
if err != nil {
|
||||
logger().Error("解析SSE数据出错", "error", err)
|
||||
return
|
||||
}
|
||||
//校验
|
||||
if !s.validateSseMsg(sseMsg) {
|
||||
logger().Error("SSE数据校验失败")
|
||||
return
|
||||
}
|
||||
logger().Info(fmt.Sprintf("SSE数据通过校验:%x", data))
|
||||
//回复
|
||||
ssrMsg := msg.SsrMsg{
|
||||
MsgHeader: msg.MsgHeader{
|
||||
ProtocolType: msg.ProtocolType_Sync,
|
||||
MessageType: msg.MessageType_SSR,
|
||||
SourceAddr: s.localAddr,
|
||||
TargetAddr: s.remoteAddr,
|
||||
},
|
||||
SeqNumSsr: s.seqNum,
|
||||
SeqNumSse: sseMsg.SeqNum,
|
||||
SeqInit1: s.calculateSeqInit1(sseMsg.SeqEnq1),
|
||||
SeqInit2: s.calculateSeqInit2(sseMsg.SeqEnq2),
|
||||
DataVer: 0x01,
|
||||
}
|
||||
ssrBytes := ssrMsg.Encode()
|
||||
err = s.client.Send(ssrBytes)
|
||||
if err != nil {
|
||||
s.state = tpapi.ThirdPartyState_Broken
|
||||
logger().Error("发送SSR数据失败", "error", err)
|
||||
} else {
|
||||
s.state = tpapi.ThirdPartyState_Normal
|
||||
logger().Info(fmt.Sprintf("发送SSR数据:%x", ssrBytes))
|
||||
//更新本地数据
|
||||
s.lastSeqParam1 = (&lfsr{value: s.remoteSinit1, poly: msg.T_POLY_1}).add(sseMsg.SeqEnq1)
|
||||
s.lastSeqParam2 = (&lfsr{value: s.remoteSinit2, poly: msg.T_POLY_2}).add(sseMsg.SeqEnq2)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *serviceContext) handleSsrMsg(data []byte) {
|
||||
if s.sseMsg == nil { //不在时序校正过程中
|
||||
logger().Warn("不在时序校正流程中,丢弃SSR数据")
|
||||
return
|
||||
}
|
||||
ssrMsg := &msg.SsrMsg{}
|
||||
err := ssrMsg.Decode(data)
|
||||
if err != nil {
|
||||
logger().Error("解析SSR数据出错", "error", err)
|
||||
return
|
||||
}
|
||||
//校验
|
||||
if !s.validateSsrMsg(ssrMsg) {
|
||||
logger().Error("SSR数据校验失败")
|
||||
return
|
||||
}
|
||||
logger().Info("SSR数据通过校验")
|
||||
//完成校正时序(precSinit~+t_e == Sinit_r~+(sid_r^t_r))
|
||||
s.lastSeqParam1 = (&lfsr{value: s.precSinit1, poly: msg.T_POLY_1}).add(ssrMsg.SeqInit1 ^ (s.sseMsg.SeqEnq1 ^ s.localSid1))
|
||||
s.lastSeqParam2 = (&lfsr{value: s.precSinit2, poly: msg.T_POLY_2}).add(ssrMsg.SeqInit2 ^ (s.sseMsg.SeqEnq2 ^ s.localSid2))
|
||||
s.sseMsg = nil
|
||||
}
|
||||
|
||||
// 启动SSE流程
|
||||
func (s *serviceContext) startSeeProgress() {
|
||||
sseMsg := &msg.SseMsg{
|
||||
MsgHeader: msg.MsgHeader{
|
||||
ProtocolType: msg.ProtocolType_Sync,
|
||||
MessageType: msg.MessageType_SSE,
|
||||
SourceAddr: s.localAddr,
|
||||
TargetAddr: s.remoteAddr,
|
||||
},
|
||||
SeqNum: s.seqNum,
|
||||
SeqEnq1: s.calculateSeqEnq1(),
|
||||
SeqEnq2: s.calculateSeqEnq2(),
|
||||
}
|
||||
sseBytes := sseMsg.Encode()
|
||||
err := s.client.Send(sseBytes)
|
||||
if err != nil {
|
||||
s.state = tpapi.ThirdPartyState_Broken
|
||||
logger().Error("发送SSE数据失败", "error", err)
|
||||
} else {
|
||||
s.state = tpapi.ThirdPartyState_Normal
|
||||
logger().Info(fmt.Sprintf("发送SSE数据:%x", sseBytes))
|
||||
s.sseMsg = sseMsg
|
||||
s.sseWaitTimer = time.After(time.Duration(s.config.NetAConfig.Period*msg.Twait_sse) * time.Millisecond)
|
||||
}
|
||||
}
|
||||
|
||||
// 校验RSD消息
|
||||
// return 0-时序校验之外的失败 1-成功 2-时序异常
|
||||
func (s *serviceContext) validateRsdMsg(rsdMsg *msg.RsdMsg, data []byte) int {
|
||||
if rsdMsg.SourceAddr != s.remoteAddr {
|
||||
logger().Error(fmt.Sprintf("源地址[%x]不正确[%s]", rsdMsg.SourceAddr, s.config.NetAConfig.RemoteAddr))
|
||||
return 0
|
||||
}
|
||||
if rsdMsg.TargetAddr != s.localAddr {
|
||||
logger().Error(fmt.Sprintf("目的地址[%x]不正确[%s]", rsdMsg.TargetAddr, s.config.NetAConfig.LocalAddr))
|
||||
return 0
|
||||
}
|
||||
if len(rsdMsg.UserData)-1 != len(s.ciSectionIndexConfigs) { //用户数据第一个字节是[检查字节]
|
||||
logger().Error(fmt.Sprintf("命令数据长度[%d]与配置长度[%d]不符", len(rsdMsg.UserData), len(s.ciSectionIndexConfigs)))
|
||||
return 0
|
||||
}
|
||||
if message.Rssp_I_Crc16(data[:len(data)-2]) != rsdMsg.Tail {
|
||||
logger().Error(fmt.Sprintf("报文尾验证失败"))
|
||||
return 0
|
||||
}
|
||||
if !s.validateSvc1(rsdMsg.Svc1, rsdMsg.UserData) {
|
||||
logger().Error(fmt.Sprintf("SVC1[%x]校验未通过", rsdMsg.Svc1))
|
||||
return 2
|
||||
}
|
||||
if !s.validateSvc2(rsdMsg.Svc2, rsdMsg.UserData) {
|
||||
logger().Error(fmt.Sprintf("SVC2[%x]校验未通过", rsdMsg.Svc2))
|
||||
return 2
|
||||
}
|
||||
return 1
|
||||
}
|
||||
|
||||
func (s *serviceContext) validateSseMsg(sseMsg *msg.SseMsg) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (s *serviceContext) validateSsrMsg(ssrMsg *msg.SsrMsg) bool {
|
||||
if s.sseMsg.SeqNum != ssrMsg.SeqNumSse {
|
||||
logger().Error(fmt.Sprintf("SSR的时序号[%d]与请求方不符[%d]", ssrMsg.SeqNumSse, s.sseMsg.SeqNum))
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (s *serviceContext) validateSvc1(svc uint32, userData []byte) bool {
|
||||
return s.validateSvc(svc, msg.SCW_1, message.Rssp_I_Crc32C1(userData), s.remoteSid1, s.remoteSinit1, &s.lastSeqParam1, msg.T_POLY_1)
|
||||
}
|
||||
|
||||
func (s *serviceContext) validateSvc2(svc uint32, userData []byte) bool {
|
||||
return s.validateSvc(svc, msg.SCW_2, message.Rssp_I_Crc32C2(userData), s.remoteSid2, s.remoteSinit2, &s.lastSeqParam2, msg.T_POLY_2)
|
||||
}
|
||||
|
||||
func (s *serviceContext) validateSvc(svc uint32, scw uint32, crc1 uint32, sid uint32, sinit uint32, lastSeqParam *uint32, tPoly uint32) bool {
|
||||
seqParam := crc1 ^ svc ^ scw
|
||||
for i := 0; i <= s.config.NetAConfig.MaxDeviation; i++ {
|
||||
seqLfsr := lfsr{value: *lastSeqParam, poly: tPoly}
|
||||
constLfsr := lfsr{value: sinit, poly: tPoly}
|
||||
constLfsr.add(sid)
|
||||
for j := 0; j < i; j++ {
|
||||
seqLfsr.add(0)
|
||||
constLfsr.add(0)
|
||||
}
|
||||
if seqLfsr.add(seqParam) == constLfsr.add(sid) {
|
||||
*lastSeqParam = seqLfsr.load(sinit).add(seqParam)
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (s *serviceContext) calculateSeqEnq1() uint32 {
|
||||
return s.localSid1 ^ s.lfsr1.value
|
||||
}
|
||||
|
||||
func (s *serviceContext) calculateSeqEnq2() uint32 {
|
||||
return s.localSid2 ^ s.lfsr2.value
|
||||
}
|
||||
|
||||
func (s *serviceContext) calculateSeqInit1(seqEnq1 uint32) uint32 {
|
||||
return seqEnq1 ^ s.localSid1 ^ s.localDataVer1 ^ s.lfsr1.value
|
||||
}
|
||||
|
||||
func (s *serviceContext) calculateSeqInit2(seqEnq2 uint32) uint32 {
|
||||
return seqEnq2 ^ s.localSid2 ^ s.localDataVer2 ^ s.lfsr2.value
|
||||
}
|
||||
|
||||
type lfsr struct {
|
||||
value uint32
|
||||
poly uint32 //时间戳生成多项式
|
||||
}
|
||||
|
||||
func (l *lfsr) load(value uint32) *lfsr {
|
||||
l.value = value
|
||||
return l
|
||||
}
|
||||
|
||||
func (l *lfsr) add(x uint32) uint32 {
|
||||
l.value = l.value ^ x
|
||||
var carry bool
|
||||
for i := 0; i < 32; i++ {
|
||||
carry = l.value&0x80000000 != 0
|
||||
l.value = l.value << 1
|
||||
if carry {
|
||||
l.value ^= l.poly
|
||||
}
|
||||
}
|
||||
return l.value
|
||||
}
|
||||
|
||||
func (l *lfsr) post(x uint32) uint32 {
|
||||
|
||||
var carry bool
|
||||
for i := 0; i < 32; i++ {
|
||||
carry = x&1 == 1
|
||||
if carry {
|
||||
x ^= l.poly
|
||||
}
|
||||
x = x >> 1
|
||||
if carry {
|
||||
x |= 0x80000000
|
||||
}
|
||||
}
|
||||
l.value ^= x
|
||||
return l.value
|
||||
}
|
||||
|
||||
func logger() *slog.Logger {
|
||||
loggerInit.Do(func() {
|
||||
privateLogger = slog.Default().With("tag", logTag)
|
||||
})
|
||||
return privateLogger
|
||||
}
|
19
third_party/axle_device/beijing12/service_test.go
vendored
Normal file
19
third_party/axle_device/beijing12/service_test.go
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
package beijing12
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"joylink.club/bj-rtsts-server/third_party/axle_device/beijing12/msg"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func Test_serviceContext_calculateSvc1(t *testing.T) {
|
||||
serviceCtx := &serviceContext{
|
||||
localSid1: 0x7665986c,
|
||||
localSid2: 0x67da286e,
|
||||
lfsr1: &lfsr{value: uint32(0x7665986c), poly: msg.T_POLY_1},
|
||||
lfsr2: &lfsr{value: uint32(0x67da286e), poly: msg.T_POLY_2},
|
||||
}
|
||||
userData := []byte{00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00}
|
||||
fmt.Printf("%x\n", serviceCtx.calculateSvc1(userData))
|
||||
fmt.Printf("%x\n", serviceCtx.calculateSvc2(userData))
|
||||
}
|
129
third_party/axle_device/rssp_axle.go
vendored
129
third_party/axle_device/rssp_axle.go
vendored
@ -1,129 +0,0 @@
|
||||
package axle_device
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"joylink.club/bj-rtsts-server/config"
|
||||
"joylink.club/bj-rtsts-server/third_party/message"
|
||||
"log/slog"
|
||||
"runtime/debug"
|
||||
"time"
|
||||
)
|
||||
|
||||
//计轴设备与联锁系统安全通信应用层实现
|
||||
|
||||
type RsspAxle interface {
|
||||
//Start 启动计轴设备与联锁系统安全通信服务
|
||||
Start(amm AxleMessageManager) error
|
||||
//Stop 停止计轴设备与联锁系统安全通信服务
|
||||
Stop()
|
||||
}
|
||||
|
||||
type rsspAxle struct {
|
||||
//所属城市
|
||||
city string
|
||||
//所属线路
|
||||
lineId string
|
||||
//所属集中站
|
||||
centralizedStation string
|
||||
//接收方每个安全通信会话对应的发送周期值,单位ms
|
||||
sendingPeriod uint32
|
||||
//主安全通道
|
||||
rsspChannel *RsspChannel
|
||||
//收到应用层消息回调
|
||||
messageManager AxleMessageManager
|
||||
//发送区段状态任务
|
||||
cancelSendStatus context.CancelFunc
|
||||
}
|
||||
|
||||
func InitRsspAxle(cfg *config.RsspAxleConfig) RsspAxle {
|
||||
ra := &rsspAxle{}
|
||||
//
|
||||
ra.city = cfg.City
|
||||
ra.lineId = cfg.LineId
|
||||
ra.centralizedStation = cfg.CentralizedStation
|
||||
ra.sendingPeriod = cfg.RsspCfg.SendingPeriod
|
||||
//
|
||||
mrc := &RsspChannel{}
|
||||
ra.rsspChannel = mrc.Init(&cfg.RsspCfg)
|
||||
//
|
||||
return ra
|
||||
}
|
||||
|
||||
// rssp 安全层执行
|
||||
func (s *rsspAxle) rcvCmdMsg(data []byte) {
|
||||
msg := &message.SectionCmdMsgPack{}
|
||||
msg.Decode(data)
|
||||
s.messageManager.HandleSectionCmdMsg(s.city, s.lineId, s.centralizedStation, msg)
|
||||
}
|
||||
func (s *rsspAxle) Start(amm AxleMessageManager) error {
|
||||
s.messageManager = amm
|
||||
//设置安全通道层
|
||||
if s.rsspChannel != nil {
|
||||
s.rsspChannel.handleUserData = s.rcvCmdMsg
|
||||
s.rsspChannel.Start()
|
||||
}
|
||||
//
|
||||
sendContext, sendCancel := context.WithCancel(context.Background())
|
||||
go s.periodRun(sendContext)
|
||||
s.cancelSendStatus = sendCancel
|
||||
//
|
||||
return nil
|
||||
}
|
||||
func (s *rsspAxle) Stop() {
|
||||
if s.rsspChannel != nil {
|
||||
s.rsspChannel.Stop()
|
||||
}
|
||||
//
|
||||
if s.cancelSendStatus != nil {
|
||||
s.cancelSendStatus()
|
||||
}
|
||||
s.messageManager = nil
|
||||
}
|
||||
func (s *rsspAxle) periodRun(runContext context.Context) {
|
||||
time.Sleep(2 * time.Second)
|
||||
defer func() {
|
||||
if e := recover(); e != nil {
|
||||
slog.Error(fmt.Sprintf("[%s-%s-%s]定时发送计轴区段状态任务异常", s.city, s.lineId, s.centralizedStation), "error", e, "stack", string(debug.Stack()))
|
||||
debug.PrintStack()
|
||||
}
|
||||
}()
|
||||
for {
|
||||
select {
|
||||
case <-runContext.Done():
|
||||
return
|
||||
default:
|
||||
}
|
||||
//slog.Debug("计轴设备periodRun")
|
||||
if s.messageManager == nil {
|
||||
slog.Warn(fmt.Sprintf("[%s-%s-%s]定时发送计轴区段状态任务因messageManager不存在退出", s.city, s.lineId, s.centralizedStation))
|
||||
return
|
||||
}
|
||||
//收集区段状态
|
||||
sectionStatusMsg, e := s.messageManager.CollectSectionStatus(s.city, s.lineId, s.centralizedStation)
|
||||
if e == nil {
|
||||
if sectionStatusMsg != nil {
|
||||
msgPack := &message.SectionStatusMsgPack{}
|
||||
msgPack.Ck = 0 //暂时无用
|
||||
msgPack.Sms = sectionStatusMsg
|
||||
s.sendStatusMsg(msgPack)
|
||||
}
|
||||
} else {
|
||||
slog.Warn(e.Error())
|
||||
}
|
||||
//
|
||||
time.Sleep(time.Duration(s.sendingPeriod) * time.Millisecond)
|
||||
//更新周期性参数
|
||||
s.rsspChannel.NextPeriod()
|
||||
}
|
||||
}
|
||||
|
||||
// 发送计轴区段状态给联锁
|
||||
func (s *rsspAxle) sendStatusMsg(msg *message.SectionStatusMsgPack) {
|
||||
data := msg.Encode()
|
||||
//向主通道发送
|
||||
if s.rsspChannel != nil {
|
||||
//slog.Debug("计轴设备发送SectionStatusMsgPack", "区段状态个数", len(msg.Sms), "packLen", len(data))
|
||||
s.rsspChannel.SendUserData(data)
|
||||
}
|
||||
}
|
65
third_party/axle_device/rssp_axle_manage.go
vendored
65
third_party/axle_device/rssp_axle_manage.go
vendored
@ -1,65 +0,0 @@
|
||||
package axle_device
|
||||
|
||||
import (
|
||||
"joylink.club/bj-rtsts-server/config"
|
||||
"joylink.club/bj-rtsts-server/third_party/message"
|
||||
"log/slog"
|
||||
)
|
||||
|
||||
//联锁集中站计轴与联锁通信管理
|
||||
|
||||
type AxleMessageManager interface {
|
||||
GetLineAllRsspAxleCfgs() []config.RsspAxleConfig
|
||||
//HandleSectionCmdMsg 计轴设备接收到联锁发送来的控制命令
|
||||
HandleSectionCmdMsg(city string, lineId string, centralizedStation string, msg *message.SectionCmdMsgPack)
|
||||
//CollectSectionStatus 收集仿真中计轴区段状态
|
||||
CollectSectionStatus(city string, lineId string, centralizedStation string) ([]*message.SectionStatusMsg, error)
|
||||
}
|
||||
|
||||
var allRsspAxleServices []RsspAxle
|
||||
|
||||
func StartLineAllRsspAxleServices(ram AxleMessageManager) {
|
||||
allRsspAxleServices = nil
|
||||
cfgs := ram.GetLineAllRsspAxleCfgs()
|
||||
//cfgs = getTestConfig() //测试用
|
||||
for _, cfg := range cfgs {
|
||||
if cfg.Open {
|
||||
as := InitRsspAxle(&cfg)
|
||||
allRsspAxleServices = append(allRsspAxleServices, as)
|
||||
as.Start(ram)
|
||||
slog.Debug("启动计轴设备", "city", cfg.City, "lineId", cfg.LineId, "CentralizedStation", cfg.CentralizedStation)
|
||||
}
|
||||
}
|
||||
}
|
||||
func StopLineAllRsspAxleServices() {
|
||||
for _, as := range allRsspAxleServices {
|
||||
as.Stop()
|
||||
}
|
||||
}
|
||||
|
||||
// 测试用
|
||||
func getTestConfig() []config.RsspAxleConfig {
|
||||
cfg := &config.RsspAxleConfig{}
|
||||
cfg.Open = true
|
||||
cfg.City = "北京"
|
||||
cfg.LineId = "12"
|
||||
cfg.CentralizedStation = "酒仙桥"
|
||||
cfg.RsspCfg.SrcAddr = 0x02
|
||||
cfg.RsspCfg.DstAddr = 0x01
|
||||
cfg.RsspCfg.DataVer1 = 0x0011
|
||||
cfg.RsspCfg.DataVer2 = 0x0012
|
||||
cfg.RsspCfg.SID1 = 0x10000000
|
||||
cfg.RsspCfg.SID2 = 0x00000001
|
||||
cfg.RsspCfg.SINIT1 = 0x01
|
||||
cfg.RsspCfg.SINIT2 = 0x02
|
||||
cfg.RsspCfg.SendingPeriod = 500
|
||||
cfg.RsspCfg.SsrRsspTimeout = 3
|
||||
cfg.RsspCfg.Mtv = 3
|
||||
cfg.RsspCfg.DeviceA = false
|
||||
cfg.RsspCfg.PicType = message.PIC_MASTER
|
||||
cfg.RsspCfg.RemoteIp = "192.168.3.5"
|
||||
cfg.RsspCfg.RemoteUdpPort = 7777
|
||||
cfg.RsspCfg.LocalUdpPort = 6666
|
||||
//
|
||||
return []config.RsspAxleConfig{*cfg}
|
||||
}
|
315
third_party/axle_device/rssp_channel.go
vendored
315
third_party/axle_device/rssp_channel.go
vendored
@ -1,315 +0,0 @@
|
||||
package axle_device
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log/slog"
|
||||
|
||||
"joylink.club/bj-rtsts-server/config"
|
||||
"joylink.club/bj-rtsts-server/third_party/message"
|
||||
"joylink.club/bj-rtsts-server/third_party/udp"
|
||||
)
|
||||
|
||||
// 铁路信号安全通信协议实现
|
||||
|
||||
// HandleUserData 回调应用层
|
||||
type HandleUserData func([]byte)
|
||||
|
||||
// RsspChannel 实现rssp通信
|
||||
type RsspChannel struct {
|
||||
//udp
|
||||
udpServer udp.UdpServer
|
||||
//udp
|
||||
udpClient udp.UdpClient
|
||||
//回调应用层
|
||||
handleUserData HandleUserData
|
||||
//rssp安全通信配置
|
||||
config *config.RsspConfig
|
||||
//rssp时钟
|
||||
rsspTimer *RsspTimer
|
||||
//批次编号,发送序列号
|
||||
sn *message.RsspSn
|
||||
//安全通道1时间戳
|
||||
ch1Ts *message.RsspLFSR
|
||||
//安全通道2时间戳
|
||||
ch2Ts *message.RsspLFSR
|
||||
//最近一次接收到的报文的安全通道1时间戳
|
||||
rcvCh1Ts uint32
|
||||
//最近一次接收到的报文的安全通道2时间戳
|
||||
rcvCh2Ts uint32
|
||||
//最近一次接收到的报文的序列号
|
||||
rcvSn uint32
|
||||
//时序校验请求发送记录
|
||||
sendSseRecord *SseFireRecord
|
||||
}
|
||||
|
||||
func (s *RsspChannel) SetRcvUserDataCallback(handleUserData HandleUserData) {
|
||||
s.handleUserData = handleUserData
|
||||
}
|
||||
func (s *RsspChannel) Init(config *config.RsspConfig) *RsspChannel {
|
||||
s.config = config
|
||||
s.rsspTimer = &RsspTimer{t: 0}
|
||||
s.sn = message.NewRsspSn(1)
|
||||
s.ch1Ts = message.NewRsspLFSR(message.RSSP_I_C1_TS, 32, s.config.SID1, false)
|
||||
s.ch2Ts = message.NewRsspLFSR(message.RSSP_I_C2_TS, 32, s.config.SID2, false)
|
||||
return s
|
||||
}
|
||||
|
||||
// Start 启动安全通道
|
||||
func (s *RsspChannel) Start() {
|
||||
//
|
||||
s.udpServer = udp.NewServer(fmt.Sprintf(":%d", s.config.LocalUdpPort), s.handleRsspMsg)
|
||||
s.udpServer.Listen()
|
||||
//
|
||||
s.udpClient = udp.NewClient(fmt.Sprintf("%s:%d", s.config.RemoteIp, s.config.RemoteUdpPort))
|
||||
}
|
||||
|
||||
// Stop 关闭安全通道
|
||||
func (s *RsspChannel) Stop() {
|
||||
if s.udpServer != nil {
|
||||
s.udpServer.Close()
|
||||
}
|
||||
if s.udpClient != nil {
|
||||
s.udpClient.Close()
|
||||
}
|
||||
}
|
||||
|
||||
// RsspTimer rssp时钟,每一个tick周期为config.SendingPeriod
|
||||
type RsspTimer struct {
|
||||
t uint64
|
||||
}
|
||||
|
||||
func (s *RsspTimer) tick() {
|
||||
s.t++
|
||||
}
|
||||
func (s *RsspTimer) now() uint64 {
|
||||
return s.t
|
||||
}
|
||||
|
||||
// SseFireRecord 发送时序校验请求的记录
|
||||
type SseFireRecord struct {
|
||||
send *message.RsspSse //已经发送的时序校验请求
|
||||
rsspTime uint64 //发送时序校验请求时的rssp时间
|
||||
}
|
||||
|
||||
func (s *SseFireRecord) record(send *message.RsspSse, rsspTime uint64) {
|
||||
s.send = send
|
||||
s.rsspTime = rsspTime
|
||||
}
|
||||
func (s *SseFireRecord) clear() {
|
||||
s.send = nil
|
||||
s.rsspTime = 0
|
||||
}
|
||||
func (s *SseFireRecord) hasRecord() bool {
|
||||
return s.send != nil
|
||||
}
|
||||
|
||||
// 处理接收到的rssp报文
|
||||
// 注意本函数由udp socket 协程执行
|
||||
func (s *RsspChannel) handleRsspMsg(pack []byte) {
|
||||
slog.Debug("接收到RSSP报文", "len", len(pack))
|
||||
//报文头校验
|
||||
head := &message.RsspHead{}
|
||||
if !head.Parse(pack) { //解析报文头失败
|
||||
slog.Debug("丢弃接收的RSSP报文:解析报文头失败")
|
||||
return
|
||||
}
|
||||
if !message.RsspHeadMcCheck(head) { //报文类别检测未通过
|
||||
slog.Debug("丢弃接收的RSSP报文:报文类别检测未通过")
|
||||
return
|
||||
}
|
||||
if !message.RsspHeadPicCheck(head) { //协议交互类别检测未通过
|
||||
slog.Debug("丢弃接收的RSSP报文:协议交互类别检测未通过")
|
||||
return
|
||||
}
|
||||
if !s.config.CheckAddress(head.Sa, head.Da) { //校验报文头中源地址和目的地址是否包含在已配置列表中
|
||||
slog.Debug("丢弃接收的RSSP报文:报文头中源地址或目的地址不在在已配置列表中")
|
||||
return
|
||||
}
|
||||
//报文尾校验
|
||||
if !message.RsspPackCrc16Check(pack) { //整个报文crc16校验未通过
|
||||
slog.Debug("丢弃接收的RSSP报文:报文尾CRC16校验未通过")
|
||||
return
|
||||
}
|
||||
//解析得到RSD、SSE或SRE
|
||||
rssp := message.ParseRsspPack(head, pack)
|
||||
if rssp == nil { //解析具体rssp包失败
|
||||
slog.Debug("丢弃接收的RSSP报文:解析具体类别包失败")
|
||||
return
|
||||
}
|
||||
//处理接收到的具体类别RSSP包
|
||||
switch rssp.Type() {
|
||||
case message.RSD_A:
|
||||
fallthrough
|
||||
case message.RSD_B:
|
||||
s.handleRsspRsd(rssp.(*message.RsspRsd))
|
||||
case message.SSE:
|
||||
s.handleRsspSse(rssp.(*message.RsspSse))
|
||||
case message.SSR:
|
||||
s.handleRsspSsr(rssp.(*message.RsspSsr))
|
||||
}
|
||||
}
|
||||
|
||||
// 处理接收到的实时安全数据 RSD
|
||||
func (s *RsspChannel) handleRsspRsd(rsd *message.RsspRsd) {
|
||||
//slog.Debug("接收到的实时安全数据 RSD")
|
||||
//如果为备机发送来的安全数据
|
||||
if s.config.PicType == message.PIC_SLAVE { //备安全通道
|
||||
slog.Debug("丢弃接收的RSSP-RSD报文:舍弃在备安全通道中接收到的安全数据")
|
||||
//备安全通道收到安全数据,表示该物理通道连接正常
|
||||
return
|
||||
}
|
||||
//如果为主机发送来的安全数据
|
||||
if s.config.PicType == message.PIC_MASTER { //主安全通道
|
||||
if !rsd.IsMaster() {
|
||||
slog.Debug("丢弃接收的RSSP-RSD报文:舍弃在主安全通道中收到的非主机发送来的安全数据")
|
||||
return
|
||||
}
|
||||
}
|
||||
//序列号校验
|
||||
//接收的序列号小于最近一次有效序列号,则触发SSE时序校验
|
||||
if rsd.Sn < s.rcvSn {
|
||||
slog.Debug("丢弃接收的RSSP-RSD报文:当前接收RSD的序列号小于最近一次接收的RSD的序列号,触发SSE")
|
||||
s.fireSse(rsd)
|
||||
return
|
||||
}
|
||||
dSn := rsd.Sn - s.rcvSn
|
||||
if dSn > s.config.Mtv {
|
||||
slog.Debug("丢弃接收的RSSP-RSD报文:当前接收RSD的序列号与最近一次接收的RSD的序列号差值过大,触发SSE")
|
||||
s.fireSse(rsd)
|
||||
return
|
||||
}
|
||||
//SVC校验
|
||||
c1Crc32 := message.Rssp_I_Crc32C1(rsd.Sad)
|
||||
c1SidTs := c1Crc32 ^ rsd.Svc1 ^ message.RSSP_I_C1_SCW //T(n)
|
||||
//
|
||||
c2Crc32 := message.Rssp_I_Crc32C2(rsd.Sad)
|
||||
c2SidTs := c2Crc32 ^ rsd.Svc2 ^ message.RSSP_I_C2_SCW //T(n)
|
||||
//todo ... SVC校验待完善
|
||||
_ = c1SidTs
|
||||
_ = c2SidTs
|
||||
//校验通过
|
||||
//记录本次接收RSD的序列号和安全校验通道时间戳
|
||||
s.rcvSn = rsd.Sn
|
||||
s.rcvCh1Ts = c1SidTs ^ s.config.SID1
|
||||
s.rcvCh2Ts = c2SidTs ^ s.config.SID2
|
||||
//通知应用层接收应用数据
|
||||
s.handleUserData(rsd.Sad)
|
||||
}
|
||||
|
||||
// 触发时序校正请求
|
||||
func (s *RsspChannel) fireSse(rsd *message.RsspRsd) {
|
||||
s.sendSseRecord = &SseFireRecord{send: s.sendSse(), rsspTime: s.rsspTimer.now()}
|
||||
}
|
||||
|
||||
// 接收到时序校正请求
|
||||
func (s *RsspChannel) handleRsspSse(sse *message.RsspSse) {
|
||||
if s.config.PicType != message.PIC_MASTER {
|
||||
slog.Debug("丢弃接收的RSSP-SSE报文:在非主安全通道中收到时序校正请求SSE")
|
||||
return
|
||||
}
|
||||
//发送时序校正响应
|
||||
s.sendSsr(sse)
|
||||
}
|
||||
|
||||
// 接收到时序校正应答
|
||||
func (s *RsspChannel) handleRsspSsr(ssr *message.RsspSsr) {
|
||||
//SSR校验
|
||||
if !s.sendSseRecord.hasRecord() {
|
||||
slog.Debug("丢弃接收的RSSP-SSR报文:未发起过SSE时序校正请求")
|
||||
return
|
||||
}
|
||||
if s.rsspTimer.t-s.sendSseRecord.rsspTime > uint64(s.config.SsrRsspTimeout) {
|
||||
slog.Debug("丢弃接收的RSSP-SSR报文:等待SSE响应超时")
|
||||
return
|
||||
}
|
||||
if ssr.SeSn != s.sendSseRecord.send.Sn {
|
||||
slog.Debug("丢弃接收的RSSP-SSR报文:SSR与SSE不对应")
|
||||
return
|
||||
}
|
||||
//恢复时序?
|
||||
s.rcvSn = ssr.SrSn
|
||||
s.rcvCh1Ts = ssr.Tic1 ^ s.sendSseRecord.send.SeqEnq1 ^ s.config.SID1 ^ s.config.DataVer1
|
||||
s.rcvCh2Ts = ssr.Tic2 ^ s.sendSseRecord.send.SeqEnq2 ^ s.config.SID2 ^ s.config.DataVer2
|
||||
}
|
||||
|
||||
// NextPeriod 刷新与周期有关的:将序列号和时间戳更新到下一个值
|
||||
func (s *RsspChannel) NextPeriod() {
|
||||
s.sn.GetAndAdd()
|
||||
s.ch1Ts.GetAndMove()
|
||||
s.ch2Ts.GetAndMove()
|
||||
s.rsspTimer.tick()
|
||||
}
|
||||
|
||||
// 发送时序校正应答
|
||||
func (s *RsspChannel) sendSsr(sse *message.RsspSse) {
|
||||
ssr := &message.RsspSsr{}
|
||||
//
|
||||
ssr.Pic = message.PIC_MASTER
|
||||
ssr.Mc = message.SSR
|
||||
ssr.Sa = s.config.SrcAddr
|
||||
ssr.Da = s.config.DstAddr
|
||||
ssr.SrSn = s.sn.Get() //当前序列号
|
||||
ssr.SeSn = sse.Sn
|
||||
ssr.Tic1 = sse.SeqEnq1 ^ s.config.SID1 ^ s.ch1Ts.Get() ^ s.config.DataVer1
|
||||
ssr.Tic2 = sse.SeqEnq2 ^ s.config.SID2 ^ s.ch2Ts.Get() ^ s.config.DataVer2
|
||||
ssr.Dvn = 0x01 //预留固定值
|
||||
//
|
||||
rsspPack := ssr.Encode()
|
||||
s.sendPack(rsspPack)
|
||||
}
|
||||
|
||||
// 发送时序校正请求
|
||||
func (s *RsspChannel) sendSse() *message.RsspSse {
|
||||
sse := &message.RsspSse{}
|
||||
//
|
||||
sse.Pic = message.PIC_MASTER
|
||||
sse.Mc = message.SSE
|
||||
sse.Sa = s.config.SrcAddr
|
||||
sse.Da = s.config.DstAddr
|
||||
//时序校正请求,把最近一次接收到的报文中的序列号时间戳发送给发送方
|
||||
sse.Sn = s.rcvSn
|
||||
sse.SeqEnq1 = s.createSeqNeq(s.config.SID1, s.rcvCh1Ts)
|
||||
sse.SeqEnq2 = s.createSeqNeq(s.config.SID2, s.rcvCh2Ts)
|
||||
//
|
||||
rsspPack := sse.Encode()
|
||||
s.sendPack(rsspPack)
|
||||
//
|
||||
return sse
|
||||
}
|
||||
|
||||
// SendUserData 发送用户数据,即通过rssp安全通道发送应用层数据,通过rssp的RSD报文发送
|
||||
func (s *RsspChannel) SendUserData(userData []byte) {
|
||||
rsd := &message.RsspRsd{}
|
||||
rsd.Pic = s.config.PicType
|
||||
if s.config.DeviceA {
|
||||
rsd.Mc = message.RSD_A
|
||||
} else {
|
||||
rsd.Mc = message.RSD_B
|
||||
}
|
||||
rsd.Sa = s.config.SrcAddr
|
||||
rsd.Da = s.config.DstAddr
|
||||
//
|
||||
rsd.Sn = s.sn.Get()
|
||||
rsd.Sdl = uint16(len(userData) + 8)
|
||||
//安全校验通道SVC_1
|
||||
crc_c1 := message.Rssp_I_Crc32C1(userData)
|
||||
rsd.Svc1 = s.createSvcCode(crc_c1, s.config.SID1, s.ch1Ts.Get(), message.RSSP_I_C1_SCW)
|
||||
//安全校验通道SVC_2
|
||||
crc_c2 := message.Rssp_I_Crc32C2(userData)
|
||||
rsd.Svc1 = s.createSvcCode(crc_c2, s.config.SID2, s.ch2Ts.Get(), message.RSSP_I_C2_SCW)
|
||||
rsd.Sad = userData
|
||||
//
|
||||
rsspPack := rsd.Encode()
|
||||
s.sendPack(rsspPack)
|
||||
}
|
||||
func (s *RsspChannel) createSvcCode(crc32 uint32, sid uint32, ts uint32, scw uint32) uint32 {
|
||||
return crc32 ^ sid ^ ts ^ scw
|
||||
}
|
||||
func (s *RsspChannel) createSeqNeq(sid uint32, ts uint32) uint32 {
|
||||
return sid ^ ts
|
||||
}
|
||||
|
||||
// 通过网络发送数据
|
||||
func (s *RsspChannel) sendPack(rsspPack []byte) {
|
||||
s.udpClient.Send(rsspPack)
|
||||
}
|
29
third_party/balise/codec.go
vendored
29
third_party/balise/codec.go
vendored
@ -1,29 +0,0 @@
|
||||
package balise
|
||||
|
||||
import "fmt"
|
||||
|
||||
// 应答器数据编解码器
|
||||
type Codec interface {
|
||||
}
|
||||
|
||||
const (
|
||||
Bytes1023 = 128
|
||||
Bytes341 = 43
|
||||
)
|
||||
|
||||
// 解码应答器数据,1023/341位解码
|
||||
// bys - 128/43字节数据
|
||||
// return - 830/210位数据
|
||||
func Decode(bys []byte) ([]int, error) {
|
||||
size := len(bys)
|
||||
|
||||
if size == Bytes1023 {
|
||||
// 1023应答器解码
|
||||
return nil, nil
|
||||
} else if size == Bytes341 {
|
||||
// 341应答器解码
|
||||
return nil, nil
|
||||
} else {
|
||||
return nil, fmt.Errorf("不支持的应答器类型")
|
||||
}
|
||||
}
|
177
third_party/balisecodec/codec.go
vendored
Normal file
177
third_party/balisecodec/codec.go
vendored
Normal file
@ -0,0 +1,177 @@
|
||||
package balisecodec
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
)
|
||||
|
||||
// 应答器数据编解码器
|
||||
type Codec interface {
|
||||
}
|
||||
|
||||
const (
|
||||
Bytes1023 = 128
|
||||
)
|
||||
|
||||
func Decode(byte128 []byte) ([]byte, error) {
|
||||
if len(byte128) != Bytes1023 {
|
||||
return nil, buildError("应答器报文长度错误, 期望长度为128字节")
|
||||
}
|
||||
bits1023 := convertTo1023Bits(byte128)
|
||||
// 检查控制位
|
||||
err := checkCb(bits1023)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// 检查913位数据,并将913位数据转换为830位数据
|
||||
data913 := getRange(bits1023, 1022, 110)
|
||||
scrambled830, err := convert913To830(data913)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// 根据加扰位计算得到加扰器初始值S
|
||||
S := calculateS(getRange(bits1023, 106, 95))
|
||||
// 解扰
|
||||
descrambled830 := descrambling(scrambled830, S)
|
||||
// 还原第一个10位值
|
||||
reverted830 := revertFirst10Bits(descrambled830)
|
||||
// 转换为左边为最高有效位(MSB)的字节数组
|
||||
byte104 := toBytes(reverted830)
|
||||
return byte104, nil
|
||||
}
|
||||
|
||||
// 解码应答器数据,1023/341位解码
|
||||
// msg - 128字节数据
|
||||
// return - 830/210位数据
|
||||
func DecodeByteString(msg string) ([]byte, error) {
|
||||
length := len(msg)
|
||||
if length != 256 {
|
||||
panic("invalid length")
|
||||
}
|
||||
// 字节转换为字节数组
|
||||
slog.Debug("待解码的1023应答器报文", "msg", msg)
|
||||
bytes := ConvertByteStringToBytes(msg)
|
||||
return Decode(bytes)
|
||||
}
|
||||
|
||||
// 转换字节(16进制)字符串为字节数组
|
||||
func ConvertByteStringToBytes(msg string) []byte {
|
||||
bytes, err := hex.DecodeString(msg)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return bytes
|
||||
}
|
||||
|
||||
// 将830位补末尾补1到832位,然后转换为左边为最高有效位(MSB)的字节数组
|
||||
func toBytes(reverted830 []byte) []byte {
|
||||
reverted830 = append(reverted830, 1, 1)
|
||||
byte104 := make([]byte, 104)
|
||||
for i := 0; i < 104; i++ {
|
||||
byte104[i] = byte(ToValLeftMsb(reverted830[i*8 : i*8+8]))
|
||||
}
|
||||
return byte104
|
||||
}
|
||||
|
||||
func buildError(msg string) error {
|
||||
return fmt.Errorf("应答器1023解码错误:%s", msg)
|
||||
}
|
||||
|
||||
// 检查控制位
|
||||
func checkCb(bits1023 []byte) error {
|
||||
cb := getRange(bits1023, 109, 107)
|
||||
for i, v := range cb {
|
||||
n := 109 - i
|
||||
//name := "b" + strconv.Itoa(109-i)
|
||||
//slog.Info("cb", name, v)
|
||||
if n == 109 && v == 1 {
|
||||
return buildError("控制位cb错误,b109应该为0,实际为1")
|
||||
} else if n == 108 && v == 1 {
|
||||
return buildError("控制位cb错误,b108应该为0,实际为1")
|
||||
} else if n == 107 && v == 0 {
|
||||
return buildError("控制位cb错误,b107应该为1,实际为0")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func getRange(bits []byte, start, end int) []byte {
|
||||
if start < 0 || end < 0 || start < end || start > 1022 {
|
||||
panic("invalid range")
|
||||
}
|
||||
return bits[1022-start : 1022-(end-1)]
|
||||
}
|
||||
|
||||
// 转换128字节数据为1023位数据
|
||||
func convertTo1023Bits(byte128 []byte) []byte {
|
||||
if len(byte128) != 128 {
|
||||
panic("invalid length")
|
||||
}
|
||||
// 字节转换为bit数组
|
||||
bits := make([]byte, 1024)
|
||||
for i, bt := range byte128 {
|
||||
for j := 0; j < 8; j++ {
|
||||
move := 7 - j
|
||||
idx := i*8 + j
|
||||
bits[idx] = (bt >> move) & 1
|
||||
}
|
||||
}
|
||||
bits1023 := bits[0:1023]
|
||||
return bits1023
|
||||
}
|
||||
|
||||
// 由加扰位计算得到的S,作为初始状态为S的32位线性反馈移位寄存器对数据进行加扰
|
||||
func calculateS(sb []byte) uint32 {
|
||||
// 由加扰计算得到的S,作为初始状态为S的32位线性反馈移位寄存器对数据进行加扰
|
||||
if len(sb) != 12 {
|
||||
panic("invalid length")
|
||||
}
|
||||
B := ToValLeftMsb(sb)
|
||||
const A uint64 = 2801775573
|
||||
S := uint32((A * uint64(B)) % (1 << 32))
|
||||
//slog.Info("由12位加扰位计算得到整数S", "B", B, "S", S, "Sb", fmt.Sprintf("%032b", S))
|
||||
return S
|
||||
}
|
||||
|
||||
// 以11位为一组比较两个913位的报文
|
||||
func compare913(b913 []byte, compare []byte) {
|
||||
if len(b913) != 913 {
|
||||
panic("invalid length")
|
||||
}
|
||||
for i := 0; i < 913; i += 11 {
|
||||
for j := 0; j < 11; j++ {
|
||||
print(b913[i+j])
|
||||
}
|
||||
println()
|
||||
for j := 0; j < 11; j++ {
|
||||
print(compare[i+j])
|
||||
}
|
||||
println()
|
||||
println()
|
||||
}
|
||||
}
|
||||
|
||||
// 比较830位数据
|
||||
func compare830(b830 []byte, compare830 []byte) {
|
||||
if len(b830) != 830 {
|
||||
panic("invalid length")
|
||||
}
|
||||
for i := 0; i < 83; i++ {
|
||||
for j := 0; j < 10; j++ {
|
||||
fmt.Printf("%01b", b830[i*10+j])
|
||||
}
|
||||
println()
|
||||
for j := 0; j < 10; j++ {
|
||||
fmt.Printf("%01b", compare830[i*10+j])
|
||||
}
|
||||
println()
|
||||
println()
|
||||
}
|
||||
for i := 0; i < 830; i++ {
|
||||
if b830[i] != compare830[i] {
|
||||
slog.Info("error", "index", i, "b830", b830[i], "compare", compare830[i])
|
||||
panic("830 bit compare error")
|
||||
}
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package balise
|
||||
package balisecodec
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
@ -113,14 +113,83 @@ var ConvWords = []uint16{
|
||||
var convWordMap = make(map[uint16]int, 1024)
|
||||
|
||||
func init() {
|
||||
if len(ConvWords) != 1024 {
|
||||
panic(fmt.Errorf("ConvWords长度不是1024, len=%d", len(ConvWords)))
|
||||
}
|
||||
// 检查,前512个字的累加和为267528,所有1024个字的累加和为1048064
|
||||
sum1 := 0
|
||||
sum2 := 0
|
||||
for i := 0; i < 1024; i++ {
|
||||
if i < 512 {
|
||||
sum1 += int(ConvWords[i])
|
||||
}
|
||||
sum2 += int(ConvWords[i])
|
||||
}
|
||||
if sum1 != 267528 {
|
||||
panic(fmt.Errorf("前512个字的累加和不是267528, sum1=%d", sum1))
|
||||
}
|
||||
if sum2 != 1048064 {
|
||||
panic(fmt.Errorf("所有1024个字的累加和不是1048064, sum2=%d", sum2))
|
||||
}
|
||||
// 检查,后一个字比前一个字大
|
||||
for i := 1; i < 1024; i++ {
|
||||
if ConvWords[i] <= ConvWords[i-1] {
|
||||
panic(fmt.Errorf("第%d个字比第%d个字小, %04o <= %04o", i, i-1, ConvWords[i], ConvWords[i-1]))
|
||||
}
|
||||
}
|
||||
for i, v := range ConvWords {
|
||||
convWordMap[v] = i
|
||||
//fmt.Printf("%04o: %d\n", v, i)
|
||||
// slog.Info("构建10位到11位转换置换字", "i", i, "v", v)
|
||||
}
|
||||
// 检查:翻转有效字的所有位能形成另一个有效字
|
||||
for _, v := range ConvWords {
|
||||
rv := revertBits(v)
|
||||
_, ok := convWordMap[rv]
|
||||
if !ok {
|
||||
panic(fmt.Errorf("构建10位到11位转换置换字失败, v=%04o, rv=%04o", v, rv))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 翻转11位bit数组
|
||||
func revertBits(word uint16) uint16 {
|
||||
bits11 := ToBitsLeftMsb(int(word), 11)
|
||||
revert := make([]byte, 11)
|
||||
for i := 0; i < 11; i++ {
|
||||
if bits11[i] == 1 {
|
||||
revert[i] = 0
|
||||
} else {
|
||||
revert[i] = 1
|
||||
}
|
||||
}
|
||||
rw := ToValLeftMsb(revert)
|
||||
// slog.Info("反转11位bit数组", "word", fmt.Sprintf("%04o", word), "bits11", bits11, "revert", revert, "revertWord", fmt.Sprintf("%04o", rw))
|
||||
return rw
|
||||
}
|
||||
|
||||
// bit数组转换为数字,左边为最高有效位
|
||||
// v - 0/1数组,数组的每个值都只能是0或1
|
||||
func ToVal(v []int) uint16 {
|
||||
func ToValLeftMsb(v []byte) uint16 {
|
||||
if len(v) > 15 {
|
||||
panic(fmt.Errorf("不支持15位以上"))
|
||||
}
|
||||
val := uint16(0)
|
||||
l := len(v)
|
||||
//elems := make([]string, l)
|
||||
for i := 0; i < l; i++ {
|
||||
//elems[i] = fmt.Sprintf("%d", v[i])
|
||||
if v[i] == 1 {
|
||||
val += (1 << (l - i - 1))
|
||||
}
|
||||
}
|
||||
// slog.Info("ToValLeftMsb", "len", l, "v", strings.Join(elems, ""), "val", fmt.Sprintf("%04o", val))
|
||||
return val
|
||||
}
|
||||
|
||||
// bit数组转换为数字,右边为最高有效位
|
||||
// v - 0/1数组,数组的每个值都只能是0或1
|
||||
func ToValRightMsb(v []byte) uint16 {
|
||||
if len(v) > 15 {
|
||||
panic(fmt.Errorf("不支持15位以上"))
|
||||
}
|
||||
@ -128,15 +197,15 @@ func ToVal(v []int) uint16 {
|
||||
l := len(v)
|
||||
for i := 0; i < l; i++ {
|
||||
if v[i] == 1 {
|
||||
val += (1 << (l - i - 1))
|
||||
val += (1 << i)
|
||||
}
|
||||
}
|
||||
return val
|
||||
}
|
||||
|
||||
// 数字转换为bit数组,左边为最高有效位
|
||||
func ToBits(val int, count int) []int {
|
||||
bs := make([]int, count)
|
||||
func ToBitsLeftMsb(val int, count int) []byte {
|
||||
bs := make([]byte, count)
|
||||
for i := 0; i < count; i++ {
|
||||
tmp := 1 << (count - 1 - i)
|
||||
if (val & (tmp)) == (tmp) {
|
||||
@ -148,26 +217,42 @@ func ToBits(val int, count int) []int {
|
||||
return bs
|
||||
}
|
||||
|
||||
// 数字转换为bit数组,右边为最高有效位
|
||||
func ToBitsRightMsb(val int, count int) []byte {
|
||||
bs := make([]byte, count)
|
||||
for i := 0; i < count; i++ {
|
||||
tmp := 1 << i
|
||||
if (val & (tmp)) == (tmp) {
|
||||
bs[i] = 1
|
||||
} else {
|
||||
bs[i] = 0
|
||||
}
|
||||
}
|
||||
return bs
|
||||
}
|
||||
|
||||
// 11位字转换回10位字
|
||||
func From11(b11 []int) ([]int, error) {
|
||||
v11 := ToVal(b11)
|
||||
v10, ok := convWordMap[uint16(v11)]
|
||||
func From11(b11 []byte) ([]byte, error) {
|
||||
v11 := ToValLeftMsb(b11)
|
||||
v10, ok := convWordMap[v11]
|
||||
// slog.Info("11位字转换回10位字", "v11", fmt.Sprintf("%04o", v11), "v11b", fmt.Sprintf("%011b", v11), "v10", v10, "ok", ok, "v10bits", fmt.Sprintf("%010b", v10), "to10Bits", ToBitsLeftMsb(v10, 10))
|
||||
if ok {
|
||||
return ToBits(v10, 10), nil
|
||||
return ToBitsLeftMsb(v10, 10), nil
|
||||
} else {
|
||||
return nil, fmt.Errorf("错误的11位字")
|
||||
return nil, fmt.Errorf("错误的11位字,word11=%04o", v11)
|
||||
}
|
||||
}
|
||||
|
||||
// 10位字转换为11位字
|
||||
func To11(b10 []int) []int {
|
||||
func To11(b10 []byte) []byte {
|
||||
if len(b10) != 10 {
|
||||
panic(fmt.Errorf("应答器编码10位字转换为11位字参数异常: 位数不是10, len=%d", len(b10)))
|
||||
}
|
||||
v10 := ToVal(b10)
|
||||
v10 := ToValLeftMsb(b10)
|
||||
if v10 > 1023 {
|
||||
panic(fmt.Errorf("应答器编码10位字转换为11位字参数异常: 10位字转为整数不能大于1023, v10=%d", v10))
|
||||
}
|
||||
v11 := ConvWords[v10]
|
||||
return ToBits(int(v11), 11)
|
||||
// slog.Info("10位字转换为11位字", "v10", v10, "v10b", fmt.Sprintf("%010b", v10), "v11", fmt.Sprintf("%04o", v11), "v11bits", fmt.Sprintf("%011b", v11), "to11Bits", ToBitsLeftMsb(int(v11), 11))
|
||||
return ToBitsLeftMsb(int(v11), 11)
|
||||
}
|
@ -1,10 +1,10 @@
|
||||
package balise_test
|
||||
package balisecodec_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"joylink.club/bj-rtsts-server/third_party/balise"
|
||||
"joylink.club/bj-rtsts-server/third_party/balisecodec"
|
||||
)
|
||||
|
||||
var by830 = []int{
|
||||
@ -18,27 +18,27 @@ var by830 = []int{
|
||||
|
||||
func TestConvList(t *testing.T) {
|
||||
// 数量
|
||||
assert.Equal(t, 1024, len(balise.ConvWords))
|
||||
assert.Equal(t, 1024, len(balisecodec.ConvWords))
|
||||
// 后面的数比前面大
|
||||
for i := 0; i < 1023; i++ {
|
||||
assert.Less(t, balise.ConvWords[i], balise.ConvWords[i+1])
|
||||
assert.Less(t, balisecodec.ConvWords[i], balisecodec.ConvWords[i+1])
|
||||
}
|
||||
// 前512项和
|
||||
sum_q := 0
|
||||
for i := 0; i < 512; i++ {
|
||||
sum_q += int(balise.ConvWords[i])
|
||||
sum_q += int(balisecodec.ConvWords[i])
|
||||
}
|
||||
assert.Equal(t, 267528, sum_q)
|
||||
// 后512项和
|
||||
sum_b := 0
|
||||
for i := 512; i < 1024; i++ {
|
||||
sum_b += int(balise.ConvWords[i])
|
||||
sum_b += int(balisecodec.ConvWords[i])
|
||||
}
|
||||
// 总1024项和
|
||||
assert.Equal(t, 1048064, sum_q+sum_b)
|
||||
}
|
||||
|
||||
func TestTo11(t *testing.T) {
|
||||
b10 := []int{0, 0, 0, 0, 0, 0, 0, 0, 0, 1}
|
||||
balise.To11(b10)
|
||||
b10 := []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 1}
|
||||
balisecodec.To11(b10)
|
||||
}
|
83
third_party/balisecodec/decode.go
vendored
Normal file
83
third_party/balisecodec/decode.go
vendored
Normal file
@ -0,0 +1,83 @@
|
||||
package balisecodec
|
||||
|
||||
// 将830位的二进制数组以10位为单位组成一个左边为最高有效位(MSB)的无符号整数数组,除了第一个10位值,其余值求和,然后循环2的10次方次与其他值求和结果相加后模2的10次方,若结果和第一个10位值相同,则结束,此值即为原始的第一个10位值,将此值替换为第一个10位二进制数组,依然是左边为MSB
|
||||
func revertFirst10Bits(b []byte) []byte {
|
||||
if len(b) != 830 {
|
||||
panic("invalid length")
|
||||
}
|
||||
// 将830位的二进制数组以10位为单位组成一个左边为最高有效位(MSB)的无符号整数数组
|
||||
w10s := make([]uint16, 83)
|
||||
for i := 0; i < 83; i++ {
|
||||
w10s[i] = uint16(ToValLeftMsb(b[i*10 : i*10+10]))
|
||||
//// 打印输出
|
||||
//for j := 0; j < 10; j++ {
|
||||
// fmt.Printf("%01b", b[i*10+j])
|
||||
//}
|
||||
//print(" ")
|
||||
//if i != 0 && i%10 == 9 {
|
||||
// println()
|
||||
//}
|
||||
}
|
||||
//println()
|
||||
// 将除了第一个10位字整数求和
|
||||
sum := uint64(0)
|
||||
for i := 1; i < 83; i++ {
|
||||
sum += uint64(w10s[i])
|
||||
}
|
||||
// 循环2的10次方次与其他值求和结果相加后模2的10次方
|
||||
for i := 0; i < 1024; i++ {
|
||||
test := sum + uint64(i)
|
||||
if test%1024 == uint64(w10s[0]) {
|
||||
w10s[0] = uint16(i)
|
||||
break
|
||||
}
|
||||
}
|
||||
//slog.Info("还原第一个10位值", "sum", sum, "bits[0]", w10s[0], "bits[0]b", fmt.Sprintf("%010b", w10s[0]))
|
||||
bits := make([]byte, 830)
|
||||
// 将整个10位数组转换为二进制数组,依然是MSB
|
||||
u0bits := ToBitsLeftMsb(int(w10s[0]), 10)
|
||||
for i := 0; i < 10; i++ {
|
||||
bits[i] = u0bits[i]
|
||||
}
|
||||
for i := 10; i < 830; i++ {
|
||||
bits[i] = b[i]
|
||||
}
|
||||
return bits
|
||||
}
|
||||
|
||||
// 解扰,由加扰计算得到的S,作为初始状态为S的32位线性反馈移位寄存器对数据进行解扰
|
||||
func descrambling(dn []byte, S uint32) []byte {
|
||||
if len(dn) != 830 {
|
||||
panic("invalid length")
|
||||
}
|
||||
// const Polynomial = 0x000000AF
|
||||
out := make([]byte, len(dn))
|
||||
t := S // 寄存器初始值
|
||||
for i := 0; i < len(dn); i++ {
|
||||
msb := (t >> 31) & 1
|
||||
out[i] = (dn[i] ^ byte(msb)) & 1
|
||||
// fmt.Printf("i=%d, t=%032b, msb=%d, dn=%d, out=%d\n", i, t, msb, dn[i], out[i])
|
||||
xor := uint32(dn[i])
|
||||
t = (xor << 30) ^ (xor << 29) ^ (xor << 28) ^ (xor << 26) ^ (xor << 24) ^ t
|
||||
t = (t << 1) | xor
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// 转换913位数据为830位数据
|
||||
func convert913To830(b913 []byte) ([]byte, error) {
|
||||
if len(b913) != 913 {
|
||||
panic("invalid length")
|
||||
}
|
||||
b830 := make([]byte, 830)
|
||||
for i := 0; i < 83; i++ {
|
||||
b10, err := From11(b913[i*11 : i*11+11])
|
||||
if err != nil {
|
||||
return nil, buildError(err.Error())
|
||||
}
|
||||
for j := 0; j < 10; j++ {
|
||||
b830[i*10+j] = b10[j]
|
||||
}
|
||||
}
|
||||
return b830, nil
|
||||
}
|
86
third_party/balisecodec/encode.go
vendored
Normal file
86
third_party/balisecodec/encode.go
vendored
Normal file
@ -0,0 +1,86 @@
|
||||
package balisecodec
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log/slog"
|
||||
)
|
||||
|
||||
// 将830位的二进制数组以10位为单位组成一个左边为最高有效位(MSB)的无符号整数数组,然后求和后模2的10次方,得到的结果覆盖第一个10位值,然后将整个10位数组转换为二进制数组,依然是左边为MSB
|
||||
func replaceFirst10Bits(b []byte) []byte {
|
||||
if len(b) != 830 {
|
||||
panic("invalid length")
|
||||
}
|
||||
// 将830位的二进制数组以10位为单位组成一个左边为最高有效位(MSB)的无符号整数数组
|
||||
bits := make([]uint16, 83)
|
||||
for i := 0; i < 83; i++ {
|
||||
bits[i] = uint16(ToValLeftMsb(b[i*10 : i*10+10]))
|
||||
// 打印输出
|
||||
for j := 0; j < 10; j++ {
|
||||
fmt.Printf("%01b", b[i*10+j])
|
||||
}
|
||||
print(" ")
|
||||
if i != 0 && i%10 == 9 {
|
||||
println()
|
||||
}
|
||||
}
|
||||
println()
|
||||
// 将每一个10位字整数求和后模2的10次方,得到的结果覆盖第一个10位值
|
||||
sum := uint64(0)
|
||||
for i := 0; i < 83; i++ {
|
||||
sum += uint64(bits[i])
|
||||
// fmt.Printf("i=%d, v10=%d, v10b=%010b\n", i, bits[i], bits[i])
|
||||
}
|
||||
bits[0] = uint16(sum % 1024)
|
||||
slog.Info("替换第一个10位值", "sum", sum, "bits[0]", bits[0], "bits[0]b", fmt.Sprintf("%010b", bits[0]))
|
||||
rbits := make([]byte, 830)
|
||||
// 将整个10位数组转换为二进制数组,依然是MSB
|
||||
u0bits := ToBitsLeftMsb(int(bits[0]), 10)
|
||||
for i := 0; i < 10; i++ {
|
||||
rbits[i] = u0bits[i]
|
||||
}
|
||||
for i := 10; i < 830; i++ {
|
||||
rbits[i] = b[i]
|
||||
}
|
||||
// compare830(b, rbits)
|
||||
return rbits
|
||||
}
|
||||
|
||||
// 由加扰计算得到的S,作为初始状态为S的32位线性反馈移位寄存器对数据进行加扰
|
||||
// 1. 生成一个32位的线性反馈移位寄存器,其初始状态为S(左边为MSB)
|
||||
// 2. 系数h31,h30,h29,h27,h25和h0等于1(表示连接),所有其他系数都为0(表示不连接)
|
||||
// 3. 然后电路被时钟驱动m-1次,其中m是数据位的数量,同时输入dn的每一位dn(m-1),dn(m-2),...,dn(0),便生成加扰后的码位(在第一个时钟之前读取第一个输出out(m-1))
|
||||
// 4. 生成的加扰码位是dn的每一位与S的最高位的异或值
|
||||
// 5. 生成的加扰码位会根据系数进行异或反馈回S的最低位
|
||||
// 几种可能性:
|
||||
func scrambling(dn []byte, S uint32) []byte {
|
||||
if len(dn) != 830 {
|
||||
panic("invalid length")
|
||||
}
|
||||
// const Polynomial = 0x000000AF
|
||||
out := make([]byte, len(dn))
|
||||
t := S // 寄存器初始值
|
||||
for i := 0; i < len(dn); i++ {
|
||||
msb := (t >> 31) & 1
|
||||
out[i] = (dn[i] ^ byte(msb)) & 1
|
||||
// fmt.Printf("i=%d, t=%032b, msb=%d, dn=%d, out=%d\n", i, t, msb, dn[i], out[i])
|
||||
xor := uint32(out[i])
|
||||
t = (xor << 30) ^ (xor << 29) ^ (xor << 28) ^ (xor << 26) ^ (xor << 24) ^ t
|
||||
t = (t << 1) | xor
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// 将830位的二进制数组先以10位为一组分别转换为11位并组合
|
||||
func convert830To913(b830 []byte) []byte {
|
||||
if len(b830) != 830 {
|
||||
panic("invalid length")
|
||||
}
|
||||
b913 := make([]byte, 913)
|
||||
for i := 0; i < 83; i++ {
|
||||
b11 := To11(b830[i*10 : i*10+10])
|
||||
for j := 0; j < 11; j++ {
|
||||
b913[i*11+j] = b11[j]
|
||||
}
|
||||
}
|
||||
return b913
|
||||
}
|
311
third_party/btm_vobc/beijing11/msg.go
vendored
Normal file
311
third_party/btm_vobc/beijing11/msg.go
vendored
Normal file
@ -0,0 +1,311 @@
|
||||
package beijing11
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"github.com/snksoft/crc"
|
||||
"joylink.club/bj-rtsts-server/util/myreader"
|
||||
)
|
||||
|
||||
const ( //数据帧外层增加用于区分ID命令帧和请求帧的帧类型字段
|
||||
id_Tupe = 0xF8
|
||||
rqst_Type = 0xE6
|
||||
)
|
||||
|
||||
type frameType = byte
|
||||
|
||||
const (
|
||||
frameType_ID frameType = 0x90 //ID命令帧
|
||||
frameType_Req frameType = 0x91 //请求帧
|
||||
frameType_Telegram frameType = 0x92 //报文帧
|
||||
frameType_Free frameType = 0x94 //空闲帧
|
||||
)
|
||||
|
||||
type frameState = byte //帧正确/不正确(不好起名字,就这么着吧)
|
||||
const ( //以下以外的值无效
|
||||
fs_Correct frameState = 0x06 //帧正确
|
||||
fs_Error frameState = 0x15 //不正确
|
||||
fs_Init frameState = 0x00 //开机
|
||||
)
|
||||
|
||||
type telegramState = byte //空闲/报文数据
|
||||
const ( //以下以外的值无效
|
||||
telegram_free = 0x05 //空闲
|
||||
telegram_on = 0x0A //报文数据
|
||||
telegram_init = 0x00 //开机
|
||||
)
|
||||
|
||||
type btmWorkState byte
|
||||
|
||||
const (
|
||||
bws_normal btmWorkState = 0x00 //正常工作
|
||||
bws_minorFault btmWorkState = 0x04 //警告BTM有轻微故障(单套故障,整机能工作),向 BDMS 汇报;
|
||||
bws_fault btmWorkState = 0xFF //BTM故障,不能正常工作;在MMI进行故障提示并向 ATS 汇报。
|
||||
)
|
||||
|
||||
type ivState uint16 //电流/电压的状态
|
||||
const (
|
||||
iv_under ivState = 1 //欠流/欠压状态
|
||||
iv_over ivState = 2 //过流/过压状态
|
||||
iv_normal ivState = 3 //正常
|
||||
)
|
||||
|
||||
type cableState uint16 //线缆状态
|
||||
const (
|
||||
cable_open cableState = 1 //开路
|
||||
cable_short cableState = 2 //短路
|
||||
cable_normal cableState = 3 //正常
|
||||
)
|
||||
|
||||
type receiverBoardState uint16
|
||||
|
||||
const (
|
||||
rbs_doubleFault receiverBoardState = 0 //双通道故障
|
||||
rbs_singleFault receiverBoardState = 1 //单通道故障
|
||||
rbs_normal receiverBoardState = 3 //正常
|
||||
)
|
||||
|
||||
type powerBoardState uint16
|
||||
|
||||
const (
|
||||
pbs_fault powerBoardState = 0 //故障
|
||||
pbs_singleFault powerBoardState = 1 //单通道故障
|
||||
pbs_doubleFault powerBoardState = 2 //双通道故障(文档写的单通道,应该是写错了)
|
||||
pbs_normal powerBoardState = 3 //正常
|
||||
)
|
||||
|
||||
type cpuWorkTemperatureState uint16
|
||||
|
||||
const (
|
||||
wts_tooHigh cpuWorkTemperatureState = 0 //过高
|
||||
wts_alarm cpuWorkTemperatureState = 1 //温度报警
|
||||
wts_unknown cpuWorkTemperatureState = 2 //未知
|
||||
wts_normal cpuWorkTemperatureState = 3 //正常
|
||||
)
|
||||
|
||||
type baseFrame struct {
|
||||
frameType frameType //帧类型
|
||||
len byte //帧长
|
||||
id byte //递增的ID码
|
||||
}
|
||||
|
||||
func (b *baseFrame) decode(data []byte) error {
|
||||
b.frameType = data[0]
|
||||
b.len = data[1]
|
||||
b.id = data[2]
|
||||
return nil
|
||||
}
|
||||
|
||||
type idFrame struct {
|
||||
baseFrame
|
||||
btmId uint16 //BTM的ID
|
||||
vobcId uint16 //VOBC的ID
|
||||
vobcCycle uint32 //VOBC周期号(1~0xFFFFFFFF,0不使用)
|
||||
reservedBytes []byte //预留字节(4字节)
|
||||
}
|
||||
|
||||
func (i *idFrame) decode(data []byte) error {
|
||||
err := i.baseFrame.decode(data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
reader := myreader.NewReader(data[3:])
|
||||
i.btmId = binary.BigEndian.Uint16(reader.ReadBytes(2))
|
||||
i.vobcId = binary.BigEndian.Uint16(reader.ReadBytes(2))
|
||||
i.vobcCycle = binary.BigEndian.Uint32(reader.ReadBytes(4))
|
||||
return reader.Err
|
||||
}
|
||||
|
||||
type reqFrame struct {
|
||||
baseFrame
|
||||
frameState frameState //帧正确/不正确
|
||||
telegramState telegramState //空闲/报文数据
|
||||
sn byte //报文序列号。1-255;开机时使用 0
|
||||
reservedBytes []byte //预留字节(10字节)
|
||||
vobcCycle uint32 //VOBC周期号(1~0xFFFFFFFF,0不使用)
|
||||
time []byte //年月日时分秒各占一个字节
|
||||
speed uint16 //速度 单位:cm/s
|
||||
vobcCycleDistance uint16 //周期走行距离 单位:cm
|
||||
}
|
||||
|
||||
func (r *reqFrame) decode(data []byte) error {
|
||||
err := r.baseFrame.decode(data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
reader := myreader.NewReader(data[3:])
|
||||
r.frameState = reader.ReadByte()
|
||||
r.telegramState = reader.ReadByte()
|
||||
r.sn = reader.ReadByte()
|
||||
r.reservedBytes = reader.ReadBytes(10)
|
||||
r.vobcCycle = binary.BigEndian.Uint32(reader.ReadBytes(4))
|
||||
r.time = reader.ReadBytes(6)
|
||||
r.speed = binary.BigEndian.Uint16(reader.ReadBytes(2))
|
||||
r.vobcCycleDistance = binary.BigEndian.Uint16(reader.ReadBytes(2))
|
||||
return reader.Err
|
||||
}
|
||||
|
||||
type telegramFrame struct {
|
||||
id byte //递增的id码
|
||||
headTTLTime uint16 //前沿TTL时间。单位为ms,溢出为0xffff,高字节在前;车体及应答器地面环境理想情况下误差小于5ms
|
||||
sn byte //报文序列号。1-255,不使用0
|
||||
btmWorkState btmWorkState //BTM工作状态
|
||||
channel1GoodBitRate byte //1通道好码率(0~100)
|
||||
channel2GoodBitRate byte //2通道好码率(0~100)
|
||||
decodeDuration uint16 //解码时间。从包络前沿到解码成功的时间(没写单位,猜测是ms)
|
||||
//reservedBytes []byte //预留(9字节)
|
||||
tailTTLTime uint16 //后沿TTL时间。单位为ms,溢出为0xffff,高字节在前;车体及应答器地面环境理想情况下误差小于5ms
|
||||
telegram []byte //应答器报文(104字节)
|
||||
responseDuration byte //响应时间。0~150,其他非法,单位0.1ms,误差小于3ms
|
||||
vobcReqCycle uint32 //VOBC请求帧周期号。(1~0xFFFFFFFF,不使用0)
|
||||
}
|
||||
|
||||
func (t *telegramFrame) encode() []byte {
|
||||
data := make([]byte, 0, 150)
|
||||
data = append(data, frameType_Telegram)
|
||||
data = append(data, 0) //帧长度,占位
|
||||
data = append(data, t.id)
|
||||
data = binary.BigEndian.AppendUint16(data, t.headTTLTime)
|
||||
data = append(data, t.sn)
|
||||
data = append(data, byte(t.btmWorkState))
|
||||
data = append(data, t.channel1GoodBitRate)
|
||||
data = append(data, t.channel2GoodBitRate)
|
||||
data = binary.BigEndian.AppendUint16(data, t.decodeDuration)
|
||||
data = append(data, make([]byte, 9)...)
|
||||
data = binary.BigEndian.AppendUint16(data, t.tailTTLTime)
|
||||
data = append(data, t.telegram...)
|
||||
data = append(data, t.responseDuration)
|
||||
data = binary.BigEndian.AppendUint32(data, t.vobcReqCycle)
|
||||
data[1] = byte(len(data) + 4) //帧长度赋值
|
||||
data = binary.BigEndian.AppendUint32(data, crc32(data))
|
||||
return data
|
||||
}
|
||||
|
||||
func (t *telegramFrame) getSn() byte {
|
||||
if t == nil {
|
||||
return 0
|
||||
} else {
|
||||
return t.sn
|
||||
}
|
||||
}
|
||||
|
||||
type freeFrame struct {
|
||||
id byte //递增的id码
|
||||
//reservedBytes []byte //保留(2个1字节)
|
||||
sn byte //报文序列号。1-255,开机时为0
|
||||
btmWorkState btmWorkState //BTM工作状态
|
||||
workTemperature byte //工作温度。单位℃
|
||||
//reservedBytes []byte //预留(6字节)
|
||||
//14-15字节。功放板、天线状态
|
||||
amp1CurrentState ivState //功放1电流状态(0-1位)
|
||||
amp1VoltageState ivState //功放1电压状态(2-3位)
|
||||
amp2CurrentState ivState //功放2电流状态(4-5位)
|
||||
amp2VoltageState ivState //功放2电压状态(6-7位)
|
||||
antenna1Fault bool //天线1状态(8-9位)(1故障,3正常)
|
||||
cable1State cableState //线缆1状态(10-11位)
|
||||
antenna2Fault bool //天线1状态(12-13位)(1故障,3正常)
|
||||
cable2State cableState //线缆2状态(14-15位)
|
||||
//16-17字节。接收板状态
|
||||
selfCheckChannel1Fault bool //上行自检码检测通道1(0位)(1正常,0故障)
|
||||
selfCheckChannel2Fault bool //上行自检码检测通道2(1位)(1正常,0故障)
|
||||
fskChannel1Fault bool //FSK连接线状态通道1(2位)(1正常,0故障)
|
||||
fskChannel2Fault bool //FSK连接线状态通道2(3位)(1正常,0故障)
|
||||
receiverBoardState receiverBoardState //接收板状态(6-7位)
|
||||
//18-19字节。电源板状态
|
||||
channel1_24v ivState //通道1 24V状态(0-1位)
|
||||
channel2_24v ivState //通道2 24V状态(2-3位)
|
||||
channel1_23vFault bool //通道1 23V状态(4位)(1正常,0故障)
|
||||
channel2_23vFault bool //通道2 23V状态(5位)(1正常,0故障)
|
||||
powerBoardState powerBoardState //电源板状态(14-15位)
|
||||
//20-21字节。处理器板
|
||||
cpuBoardId byte //板卡ID槽位号(0-1位)(0-1号板卡,1-2号板卡,类推)
|
||||
cpuWorkTemperatureState cpuWorkTemperatureState //工作温度状态(2-3位)
|
||||
|
||||
//freeData []byte //空闲数据(104字节,全填0)
|
||||
responseDuration byte //相应时间,0-150,其他非法,单位0.1ms
|
||||
vobcCycle uint32 //VOBC请求帧周期号
|
||||
}
|
||||
|
||||
func (f *freeFrame) encode() []byte {
|
||||
data := make([]byte, 0, 150)
|
||||
data = append(data, frameType_Free)
|
||||
data = append(data, 0) //帧长度,占位
|
||||
data = append(data, f.id)
|
||||
data = append(data, 0, 0)
|
||||
data = append(data, f.sn)
|
||||
data = append(data, byte(f.btmWorkState))
|
||||
data = append(data, f.workTemperature)
|
||||
data = append(data, make([]byte, 6)...)
|
||||
//功放板、天线状态
|
||||
var ampState uint16
|
||||
ampState += uint16(f.amp1CurrentState) << 15
|
||||
ampState += uint16(f.amp1VoltageState) << 13
|
||||
ampState += uint16(f.amp2CurrentState) << 11
|
||||
ampState += uint16(f.amp2VoltageState) << 9
|
||||
if f.antenna1Fault {
|
||||
ampState += uint16(1) << 7
|
||||
} else {
|
||||
ampState += uint16(3) << 7
|
||||
}
|
||||
ampState += uint16(f.cable1State) << 5
|
||||
if f.antenna2Fault {
|
||||
ampState += uint16(1) << 3
|
||||
} else {
|
||||
ampState += uint16(3) << 3
|
||||
}
|
||||
ampState += uint16(f.cable2State)
|
||||
data = binary.BigEndian.AppendUint16(data, ampState)
|
||||
//接收板状态
|
||||
var receiverState uint16
|
||||
if !f.selfCheckChannel1Fault {
|
||||
receiverState += uint16(1) << 15
|
||||
}
|
||||
if !f.selfCheckChannel2Fault {
|
||||
receiverState += uint16(1) << 14
|
||||
}
|
||||
if !f.fskChannel1Fault {
|
||||
receiverState += uint16(1) << 13
|
||||
}
|
||||
if !f.fskChannel2Fault {
|
||||
receiverState += uint16(1) << 12
|
||||
}
|
||||
receiverState += uint16(f.receiverBoardState) << 9
|
||||
data = binary.BigEndian.AppendUint16(data, receiverState)
|
||||
//电源板状态
|
||||
var powerState uint16
|
||||
powerState += uint16(f.channel1_24v) << 15
|
||||
powerState += uint16(f.channel2_24v) << 13
|
||||
if !f.channel1_23vFault {
|
||||
powerState += uint16(1) << 11
|
||||
}
|
||||
if !f.channel2_23vFault {
|
||||
powerState += uint16(1) << 10
|
||||
}
|
||||
powerState += uint16(f.powerBoardState)
|
||||
data = binary.BigEndian.AppendUint16(data, powerState)
|
||||
//处理器板
|
||||
var cpuState uint16
|
||||
cpuState += uint16(f.cpuBoardId) << 15
|
||||
cpuState += uint16(f.cpuWorkTemperatureState) << 13
|
||||
data = binary.BigEndian.AppendUint16(data, cpuState)
|
||||
//其它
|
||||
data = append(data, make([]byte, 104)...)
|
||||
data = append(data, f.responseDuration)
|
||||
data = binary.BigEndian.AppendUint32(data, f.vobcCycle)
|
||||
|
||||
data[1] = byte(len(data) + 4) //帧长度赋值
|
||||
data = binary.BigEndian.AppendUint32(data, crc32(data))
|
||||
return data
|
||||
}
|
||||
|
||||
var crcHash = crc.NewHash(&crc.Parameters{
|
||||
Width: 32,
|
||||
Polynomial: crc.CRC32.Polynomial,
|
||||
ReflectIn: false,
|
||||
ReflectOut: false,
|
||||
Init: 0xFFFFFFFF,
|
||||
FinalXor: 0,
|
||||
})
|
||||
|
||||
func crc32(data []byte) uint32 {
|
||||
return uint32(crcHash.CalculateCRC(data))
|
||||
}
|
247
third_party/btm_vobc/beijing11/service.go
vendored
Normal file
247
third_party/btm_vobc/beijing11/service.go
vendored
Normal file
@ -0,0 +1,247 @@
|
||||
package beijing11
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"joylink.club/bj-rtsts-server/dto/state_proto"
|
||||
"joylink.club/bj-rtsts-server/third_party/message"
|
||||
"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/slog"
|
||||
"sync"
|
||||
)
|
||||
|
||||
var (
|
||||
logTag = "[北京11号线BTM-VOBC通信]"
|
||||
privateLogger *slog.Logger
|
||||
loggerInit sync.Once
|
||||
)
|
||||
|
||||
var (
|
||||
mu = sync.Mutex{} //启动任务时使用,避免重复启动任务
|
||||
serviceCtx *serviceContext //当前正在运行的服务
|
||||
)
|
||||
|
||||
type serviceContext struct {
|
||||
simulation *memory.VerifySimulation
|
||||
client udp.UdpClient
|
||||
server udp.UdpServer
|
||||
|
||||
id byte //无论何时传输数据,该数都在 1-255 范围内递增,在错误重传时也是递增的,255 之后是 1,不使用 0
|
||||
sn byte //报文序列号。1-255;开机时使用0
|
||||
lastTelFrame *telegramFrame //最后发送的报文帧
|
||||
sendSuccess bool //如果报文帧后第一个请求帧的报文序列号与BTM刚发送的报文帧中的报文序列号不一样,BTM就认为发送是不成功的,BTM会将同样的数据再发一次;
|
||||
|
||||
lastCmdId byte //vobc ID命令帧最新的ID码(1~255,不使用0)
|
||||
lastReqId byte //vobc 请求帧最新的ID码(1~255,不使用0)
|
||||
btmId uint16 //BTM的id
|
||||
vobcId uint16 //VOBC的id
|
||||
|
||||
state tpapi.ThirdPartyApiServiceState
|
||||
}
|
||||
|
||||
func Start(sim *memory.VerifySimulation) {
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
if serviceCtx != nil {
|
||||
logger().Warn("重复启动服务")
|
||||
return
|
||||
}
|
||||
config := sim.GetRunConfig().BtmVobc
|
||||
client := udp.NewClient(fmt.Sprintf("%s:%d", config.RemoteIp, config.RemoteUdpPort))
|
||||
serviceCtx = &serviceContext{
|
||||
simulation: sim,
|
||||
client: client,
|
||||
sendSuccess: true,
|
||||
}
|
||||
server := udp.NewServer(fmt.Sprintf(":%d", config.LocalUdpPort), serviceCtx.handle)
|
||||
err := server.Listen()
|
||||
if err != nil {
|
||||
Stop(sim)
|
||||
return
|
||||
}
|
||||
serviceCtx.server = server
|
||||
serviceCtx.state = tpapi.ThirdPartyState_Normal
|
||||
}
|
||||
|
||||
func Stop(sim *memory.VerifySimulation) {
|
||||
if serviceCtx != nil && serviceCtx.simulation == sim {
|
||||
if serviceCtx.server != nil {
|
||||
serviceCtx.server.Close()
|
||||
}
|
||||
if serviceCtx.client != nil {
|
||||
serviceCtx.client.Close()
|
||||
}
|
||||
serviceCtx = nil
|
||||
logger().Info("服务停止")
|
||||
}
|
||||
}
|
||||
|
||||
func (s *serviceContext) handle(data []byte) {
|
||||
logger().Info(fmt.Sprintf("收到数据:%x", data))
|
||||
if !bytes.HasPrefix(data, []byte{0xFF, 0xFE}) || !bytes.HasSuffix(data, []byte{0xFF, 0xFD}) {
|
||||
logger().Error("帧头/帧尾不对,丢弃数据")
|
||||
return
|
||||
}
|
||||
decodeBytes, _ := message.TranslateFromFFFE(data[3 : len(data)-2])
|
||||
switch data[2] {
|
||||
case id_Tupe:
|
||||
frame := idFrame{}
|
||||
err := frame.decode(decodeBytes)
|
||||
if err != nil {
|
||||
logger().Error(fmt.Sprintf("id命令帧解析出错:%s", err))
|
||||
return
|
||||
}
|
||||
s.handleIdFrame(&frame)
|
||||
case rqst_Type:
|
||||
frame := reqFrame{}
|
||||
err := frame.decode(decodeBytes[20 : len(decodeBytes)-2]) //RSSP-I协议部分不做校验,直接忽略
|
||||
if err != nil {
|
||||
logger().Error(fmt.Sprintf("请求帧解析出错:%s", err))
|
||||
return
|
||||
}
|
||||
s.handleReqFrame(&frame)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *serviceContext) handleIdFrame(frame *idFrame) {
|
||||
if frame.id < s.lastCmdId && s.lastCmdId-frame.id < 250 { //第二个条件保证id没有突破255
|
||||
logger().Error(fmt.Sprintf("vobc ID命令帧id倒退[%d:%d]", s.lastCmdId, frame.id))
|
||||
}
|
||||
s.lastCmdId = frame.id
|
||||
s.btmId = frame.btmId
|
||||
s.vobcId = frame.vobcId
|
||||
}
|
||||
|
||||
func (s *serviceContext) handleReqFrame(frame *reqFrame) {
|
||||
if frame.frameState == fs_Error {
|
||||
logger().Info("上一帧的报文序列或者CRC检查不正确")
|
||||
}
|
||||
if frame.id <= s.lastReqId && s.lastReqId-frame.id < 250 { //第二个条件保证id没有突破255
|
||||
logger().Info(fmt.Sprintf("vobc请求帧id倒退【%d:%d】", s.lastReqId, frame.id))
|
||||
return
|
||||
}
|
||||
s.lastReqId = frame.id
|
||||
if !s.sendSuccess {
|
||||
if frame.sn != s.lastTelFrame.getSn() { //报文帧后第一条请求帧的报文序列号对应不上
|
||||
logger().Error(fmt.Sprintf("报文帧后第一条请求帧的报文序列号对应不上[%s:%s]", frame.sn, s.lastTelFrame.getSn()))
|
||||
err := s.client.Send(s.lastTelFrame.encode())
|
||||
if err != nil {
|
||||
logger().Error(fmt.Sprintf("发送上一次的报文帧出错:%s", err))
|
||||
} else {
|
||||
logger().Error("BTM重发上一次的报文帧成功")
|
||||
}
|
||||
return
|
||||
}
|
||||
s.sendSuccess = true
|
||||
}
|
||||
|
||||
var telegram []byte
|
||||
s.simulation.Memory.Status.TrainStateMap.Range(func(_, value any) bool {
|
||||
trainState := value.(*state_proto.TrainState)
|
||||
if trainState.ConnState.Conn && trainState.ConnState.ConnType == state_proto.TrainConnState_PC_SIM {
|
||||
for _, bs := range trainState.BtmBaliseCacheA.BaliseList {
|
||||
if !bs.IsSend {
|
||||
bs.IsSend = true
|
||||
tel, err := hex.DecodeString(bs.Telegram)
|
||||
if err != nil {
|
||||
logger().Error(fmt.Sprintf("用户报文解码出错:%s", err))
|
||||
break
|
||||
}
|
||||
telegram = tel
|
||||
break
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
return true
|
||||
})
|
||||
if len(telegram) != 0 { //有未发送报文,发送报文帧
|
||||
telFrame := telegramFrame{}
|
||||
telFrame.id = s.nextId()
|
||||
telFrame.headTTLTime = 1
|
||||
telFrame.sn = s.nextSn()
|
||||
telFrame.btmWorkState = bws_normal
|
||||
telFrame.channel1GoodBitRate = 100
|
||||
telFrame.channel2GoodBitRate = 100
|
||||
telFrame.decodeDuration = 1
|
||||
telFrame.tailTTLTime = 1
|
||||
telFrame.telegram = telegram
|
||||
telFrame.responseDuration = 10
|
||||
telFrame.vobcReqCycle = frame.vobcCycle
|
||||
|
||||
encode := telFrame.encode()
|
||||
err := s.client.Send(encode)
|
||||
if err != nil {
|
||||
logger().Error("发送数据失败")
|
||||
} else {
|
||||
logger().Info(fmt.Sprintf("发送报文帧:%x", encode))
|
||||
s.sendSuccess = false
|
||||
}
|
||||
} else { //否则发送空闲帧
|
||||
freeFrame := freeFrame{
|
||||
id: s.nextId(),
|
||||
sn: s.lastTelFrame.getSn(),
|
||||
btmWorkState: bws_normal,
|
||||
workTemperature: 20,
|
||||
amp1CurrentState: iv_normal,
|
||||
amp1VoltageState: iv_normal,
|
||||
amp2CurrentState: iv_normal,
|
||||
amp2VoltageState: iv_normal,
|
||||
antenna1Fault: false,
|
||||
cable1State: cable_normal,
|
||||
antenna2Fault: false,
|
||||
cable2State: cable_normal,
|
||||
selfCheckChannel1Fault: false,
|
||||
selfCheckChannel2Fault: false,
|
||||
fskChannel1Fault: false,
|
||||
fskChannel2Fault: false,
|
||||
receiverBoardState: rbs_normal,
|
||||
channel1_24v: iv_normal,
|
||||
channel2_24v: iv_normal,
|
||||
channel1_23vFault: false,
|
||||
channel2_23vFault: false,
|
||||
powerBoardState: pbs_normal,
|
||||
cpuBoardId: 0,
|
||||
cpuWorkTemperatureState: wts_normal,
|
||||
responseDuration: 0,
|
||||
vobcCycle: frame.vobcCycle,
|
||||
}
|
||||
encode := freeFrame.encode()
|
||||
err := s.client.Send(encode)
|
||||
if err != nil {
|
||||
logger().Error("发送数据失败")
|
||||
} else {
|
||||
logger().Info(fmt.Sprintf("发送空闲帧:%x", encode))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *serviceContext) nextId() byte {
|
||||
s.id++
|
||||
if s.id == 0 {
|
||||
s.id++
|
||||
}
|
||||
return s.id
|
||||
}
|
||||
|
||||
func (s *serviceContext) nextSn() byte {
|
||||
if s.sn == 0 {
|
||||
return 0
|
||||
}
|
||||
tmp := s.sn
|
||||
s.sn++
|
||||
if s.sn == 0 {
|
||||
s.sn++
|
||||
}
|
||||
return tmp
|
||||
}
|
||||
|
||||
func logger() *slog.Logger {
|
||||
loggerInit.Do(func() {
|
||||
privateLogger = slog.Default().With("tag", logTag)
|
||||
})
|
||||
return privateLogger
|
||||
}
|
76
third_party/can_btm/balise_btm.go
vendored
76
third_party/can_btm/balise_btm.go
vendored
@ -3,7 +3,6 @@ package can_btm
|
||||
import (
|
||||
"fmt"
|
||||
"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/message"
|
||||
"joylink.club/bj-rtsts-server/third_party/udp"
|
||||
@ -11,7 +10,6 @@ import (
|
||||
"joylink.club/rtsssimulation/entity"
|
||||
"log/slog"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
@ -46,7 +44,7 @@ type btmCanetClient struct {
|
||||
//最近一次车载ATP系统查询帧CRC16校验结果,true-校验通过
|
||||
atpReqCrc16Check bool
|
||||
//btm系统时间,每次接收到ATP查询请求帧时同步一次时间
|
||||
btmTime btmClock
|
||||
btmTime BtmClock
|
||||
//数据流水号
|
||||
dsn byte
|
||||
//重发的数据
|
||||
@ -55,31 +53,14 @@ type btmCanetClient struct {
|
||||
baliseDetector *BaliseDetector
|
||||
}
|
||||
|
||||
func (s *btmCanetClient) GetState() state_proto.BTMState {
|
||||
detector := s.baliseDetector
|
||||
var telegram string
|
||||
info := detector.eq[len(detector.eq)-1]
|
||||
if /*detector.aboveBalise &&*/ info != nil && len(info.telegram) != 0 {
|
||||
telegram = fmt.Sprintf("%X", info.telegram)
|
||||
} else {
|
||||
telegram = strings.Repeat("00", balise_const.UserTelegramByteLen)
|
||||
}
|
||||
return state_proto.BTMState{
|
||||
DataSerialNumber: uint32(s.dsn),
|
||||
BaliseCount: uint32(detector.baliseCounter),
|
||||
MessageCounter: uint32(detector.messageCounter),
|
||||
Telegram: telegram,
|
||||
}
|
||||
}
|
||||
|
||||
type btmClock struct {
|
||||
btmTk uint32 //与ATP系统同步的时间ms
|
||||
sysTk time.Time //本地系统时间
|
||||
type BtmClock struct {
|
||||
BtmTk uint32 //与ATP系统同步的时间ms
|
||||
SysTk time.Time //本地系统时间
|
||||
}
|
||||
|
||||
// 获取以btmTk为基准的当前时间ms
|
||||
func (c *btmClock) tkNow() uint32 {
|
||||
return c.btmTk + uint32(time.Now().UnixMilli()-c.sysTk.UnixMilli())
|
||||
func (c *BtmClock) TkNow() uint32 {
|
||||
return c.BtmTk + uint32(time.Now().UnixMilli()-c.SysTk.UnixMilli())
|
||||
}
|
||||
|
||||
type BtmCanetClient interface {
|
||||
@ -87,8 +68,7 @@ type BtmCanetClient interface {
|
||||
Stop()
|
||||
//HandleTrainHeadPositionInfo 处理收到列车位置信息
|
||||
HandleTrainHeadPositionInfo(w ecs.World, h *TrainHeadPositionInfo)
|
||||
//获取BTM显示状态
|
||||
GetState() state_proto.BTMState
|
||||
HandleTrainHeadPositionInfoForTrain(w ecs.World, train *state_proto.TrainState, h *TrainHeadPositionInfo)
|
||||
}
|
||||
|
||||
var (
|
||||
@ -97,13 +77,33 @@ var (
|
||||
)
|
||||
|
||||
func Default() BtmCanetClient {
|
||||
btmClientLocker.Lock()
|
||||
defer btmClientLocker.Unlock()
|
||||
btmClientLocker.Lock()
|
||||
if btmClient == nil {
|
||||
btmClient = &btmCanetClient{baliseDetector: &BaliseDetector{}}
|
||||
}
|
||||
return btmClient
|
||||
}
|
||||
func (s *btmCanetClient) HandleTrainHeadPositionInfoForTrain(w ecs.World, train *state_proto.TrainState, h *TrainHeadPositionInfo) {
|
||||
wd := entity.GetWorldData(w)
|
||||
repo := wd.Repo
|
||||
h2 := &TrainHeadPositionInfo{
|
||||
TrainId: h.TrainId,
|
||||
Up: h.Up,
|
||||
Link: h.OldLink,
|
||||
LinkOffset: h.OldLinkOffset,
|
||||
Speed: h.Speed,
|
||||
Acceleration: h.Acceleration}
|
||||
s.baliseDetector.newDetect(wd, repo, h, h2, train.BtmBaliseCacheA, h.IsLine12, false, "11")
|
||||
h.Up = h.TailUp
|
||||
h.Link = h.TailLink
|
||||
h.LinkOffset = h.TailLinkOffset
|
||||
h2.Up = h.TailUp
|
||||
h2.Link = h.OldTailLink
|
||||
h2.LinkOffset = h.OldTailLinkOffset
|
||||
s.baliseDetector.newDetect(wd, repo, h, h2, train.BtmBaliseCacheB, h.IsLine12, false, "222")
|
||||
|
||||
}
|
||||
|
||||
// HandleTrainHeadPositionInfo 处理来自动力学的列车位置信息
|
||||
func (s *btmCanetClient) HandleTrainHeadPositionInfo(w ecs.World, h *TrainHeadPositionInfo) {
|
||||
@ -143,6 +143,9 @@ func (s *btmCanetClient) Stop() {
|
||||
s.udpClient.Close()
|
||||
s.udpClient = nil
|
||||
}
|
||||
for d := range s.baliseDetector.eq {
|
||||
s.baliseDetector.eq[d] = nil
|
||||
}
|
||||
}
|
||||
func (s *btmCanetClient) handleCanetFrames(cfs []byte) {
|
||||
defer func() {
|
||||
@ -206,8 +209,8 @@ func (s *btmCanetClient) dealWithAptReq(f *message.CanetFrame) {
|
||||
//处理查询请求
|
||||
//slog.Debug(fmt.Sprintf("处理查询请求:%s", atpReq.String()))
|
||||
//
|
||||
s.btmTime.btmTk = atpReq.Time
|
||||
s.btmTime.sysTk = now
|
||||
s.btmTime.BtmTk = atpReq.Time
|
||||
s.btmTime.SysTk = now
|
||||
s.atpReqSn = atpReq.FId.ID4
|
||||
s.atpReqCrc16Check = atpReq.Crc16CheckOk
|
||||
s.baliseDetector.powerAmplifierSwitch = atpReq.PowerAmplifierTurnOn
|
||||
@ -263,7 +266,7 @@ func (s *btmCanetClient) rspToAtp(sb *BtmAntennaScanningBaliseInfo) {
|
||||
statusF.BaliseCounter = byte(btmStatus.BaliseCounter)
|
||||
statusF.MessageCounter = byte(btmStatus.MessageCounter)
|
||||
statusF.PowerAmplifierOn = btmStatus.PowerAmplifierOn
|
||||
statusF.TkTimeA = s.btmTime.tkNow()
|
||||
statusF.TkTimeA = s.btmTime.TkNow()
|
||||
statusF.PowerAmplifierFailure = btmStatus.PowerAmplifierFault
|
||||
statusF.DetailedCode = 0
|
||||
if btmStatus.AboveBalise {
|
||||
@ -271,16 +274,17 @@ func (s *btmCanetClient) rspToAtp(sb *BtmAntennaScanningBaliseInfo) {
|
||||
}
|
||||
statusF.AtpReqCrcCheckWrong = !s.atpReqCrc16Check
|
||||
statusF.Dsn = s.dsn
|
||||
|
||||
s.dsnAdd1()
|
||||
//
|
||||
//true-收到应答器报文
|
||||
isRcvTelegram := sb != nil && len(sb.telegram) > 0
|
||||
if isRcvTelegram { //当收到应答器报文时响应:时间同步帧、状态应答帧、数据帧
|
||||
statusDataCf, statusDataCfOk := message.CreateBtmRspFramesData(statusF, sb.telegram, false, s.btmTime.tkNow(), s.btmTime.tkNow(), s.btmTime.tkNow())
|
||||
statusDataCf, statusDataCfOk := message.CreateBtmRspFramesData(statusF, sb.telegram, false, s.btmTime.TkNow(), s.btmTime.TkNow(), s.btmTime.TkNow())
|
||||
if statusDataCfOk {
|
||||
timeSyncF := message.NewBtmTimeSyncCheckFrame(s.atpReqSn)
|
||||
timeSyncF.T2 = s.btmTime.btmTk
|
||||
timeSyncF.T3 = s.btmTime.tkNow()
|
||||
timeSyncF.T2 = s.btmTime.BtmTk
|
||||
timeSyncF.T3 = s.btmTime.TkNow()
|
||||
s.sendCanetFrame(timeSyncF.Encode().Encode())
|
||||
//
|
||||
s.resendData = newResendData(statusDataCf)
|
||||
@ -290,8 +294,8 @@ func (s *btmCanetClient) rspToAtp(sb *BtmAntennaScanningBaliseInfo) {
|
||||
}
|
||||
} else { //当未收到应答器报文时响应:时间同步帧、状态应答帧
|
||||
timeSyncF := message.NewBtmTimeSyncCheckFrame(s.atpReqSn)
|
||||
timeSyncF.T2 = s.btmTime.btmTk
|
||||
timeSyncF.T3 = s.btmTime.tkNow()
|
||||
timeSyncF.T2 = s.btmTime.BtmTk
|
||||
timeSyncF.T3 = s.btmTime.TkNow()
|
||||
s.sendCanetFrame(timeSyncF.Encode().Encode())
|
||||
//
|
||||
statusCf := statusF.Encode().Encode()
|
||||
|
254
third_party/can_btm/balise_detection.go
vendored
254
third_party/can_btm/balise_detection.go
vendored
@ -2,6 +2,7 @@ package can_btm
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"joylink.club/bj-rtsts-server/dto/state_proto"
|
||||
"joylink.club/rtsssimulation/component"
|
||||
"joylink.club/rtsssimulation/fi"
|
||||
"joylink.club/rtsssimulation/repository"
|
||||
@ -27,20 +28,26 @@ type BtmAntennaRunningInfo struct {
|
||||
}
|
||||
|
||||
const (
|
||||
BtmAntennaOffsetHead = int64(1000) //车载BTM天线距车头端点的距离,mm
|
||||
//BtmAntennaOffsetHead = int64(1000) //车载BTM天线距车头端点的距离,mm
|
||||
BtmAntennaOffsetHead = int64(0) //车载BTM天线距车头端点的距离,mm
|
||||
)
|
||||
|
||||
// TrainHeadPositionInfo 列车车头运行位置信息
|
||||
type TrainHeadPositionInfo = fi.TrainHeadPositionInfo
|
||||
type BtmAntennaToBaliseInfo struct {
|
||||
Distance int64 //BTM天线中心到应答器的距离,mm
|
||||
BaliseId string //应答器id
|
||||
Distance int64 //BTM天线中心到应答器的距离,mm
|
||||
BaliseId string //应答器id
|
||||
BaliseType proto.Transponder_Type //应答器类型
|
||||
}
|
||||
type BtmAntennaScanningBaliseInfo struct {
|
||||
BaliseId string //应答器id
|
||||
Time time.Time //应答器预计被BTM天线激活的时刻
|
||||
active bool //true-激活过,即列车扫过
|
||||
telegram []byte //应答器报文
|
||||
BaliseId string //应答器id
|
||||
Time time.Time //应答器预计被BTM天线激活的时刻
|
||||
active bool //true-激活过,即列车扫过
|
||||
telegram []byte //应答器用户报文
|
||||
telegram128 []byte //应答器报文
|
||||
Distance int64 //BTM天线中心到应答器的距离,mm
|
||||
BaliseType proto.Transponder_Type //应答器类型
|
||||
IsSend bool
|
||||
}
|
||||
|
||||
// BaliseDetector 车载BTM天线,应答器探测器
|
||||
@ -67,9 +74,47 @@ func (t *BaliseDetector) tryRebind(th *TrainHeadPositionInfo) {
|
||||
t.clearExpectedBalise()
|
||||
t.baliseCounter = 0
|
||||
t.messageCounter = 0
|
||||
slog.Debug(fmt.Sprintf("列车[%s]与CAN-BTM绑定", t.trianId))
|
||||
//slog.Debug(fmt.Sprintf("列车[%s]与CAN-BTM绑定", t.trianId))
|
||||
}
|
||||
}
|
||||
func (t *BaliseDetector) newDetect(wd *component.WorldData, repo *repository.Repository, th, th2 *TrainHeadPositionInfo, btmCache *state_proto.TrainBtmCache, isLine12, show bool, id string) {
|
||||
//BTM天线中心点运行信息
|
||||
curAntennaRi := t.createBtmAntennaRunningInfo(wd, repo, th) //目前车头
|
||||
curAntennaRi2 := t.createBtmAntennaRunningInfo(wd, repo, th2) //上次车头
|
||||
var startBalises []*repository.Transponder
|
||||
if th.Link != th2.Link {
|
||||
startBalises = t.searchBalisesFromBetweenLinkPosition(repo, th.Up, curAntennaRi.LinkId, curAntennaRi.LinkOffset, curAntennaRi.LinkOffset)
|
||||
} else {
|
||||
startBalises = t.searchBalisesFromBetweenLinkPosition(repo, th.Up, curAntennaRi.LinkId, curAntennaRi2.LinkOffset, curAntennaRi.LinkOffset)
|
||||
}
|
||||
balises := make([]*repository.Transponder, 0)
|
||||
for _, balise := range startBalises {
|
||||
find := false
|
||||
for _, transponder := range balises {
|
||||
if transponder.Id() == balise.Id() {
|
||||
find = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !find {
|
||||
balises = append(balises, balise)
|
||||
}
|
||||
}
|
||||
if len(balises) > 0 {
|
||||
balise := balises[0]
|
||||
telegram, utel := t.rcvTelegram(wd, balise.Id())
|
||||
/*if show {
|
||||
slog.Info(fmt.Sprintf("%v --------------------id:%v ,offset:%v, up: %v,linkeId:%v ,headoffset:%v,tailOffset:%v", id, balise.Id(), balise.LinkPosition().Offset(), th.Up, curAntennaRi.LinkId, curAntennaRi.LinkOffset, curAntennaRi2.LinkOffset))
|
||||
}*/
|
||||
if AddNewExpectedBalise(balise, btmCache, telegram, utel, isLine12) {
|
||||
if show {
|
||||
slog.Info(fmt.Sprintf("%v +++++++++++++id:%v ,offset:%v, up: %v,linkeId:%v ,headoffset:%v,tailOffset:%v", id, balise.Id(), balise.LinkPosition().Offset(), th.Up, curAntennaRi.LinkId, curAntennaRi.LinkOffset, curAntennaRi2.LinkOffset))
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (t *BaliseDetector) detect(wd *component.WorldData, repo *repository.Repository, th *TrainHeadPositionInfo) {
|
||||
t.tryRebind(th)
|
||||
@ -81,14 +126,17 @@ func (t *BaliseDetector) detect(wd *component.WorldData, repo *repository.Reposi
|
||||
curAntennaRi := t.createBtmAntennaRunningInfo(wd, repo, th)
|
||||
//预测BTM天线到最近一个应答器的时刻
|
||||
curExpect := t.timeScanNearestBalise(curTime, wd, repo, curAntennaRi)
|
||||
|
||||
if curExpect != nil && curExpect.Time.UnixMilli()-curTime.UnixMilli() < 20 { //20ms
|
||||
//slog.Debug("将要激活应答器", "BaliseId", curExpect.BaliseId, "ActiveTime", dt)
|
||||
|
||||
telegram, utel := t.rcvTelegram(wd, curExpect.BaliseId)
|
||||
|
||||
//记录即将经过的应答器
|
||||
if t.addExpectedBalise(curExpect) {
|
||||
t.baliseCounterAdd1() //应答器计数器
|
||||
telegram := t.rcvTelegram(wd, curExpect.BaliseId)
|
||||
if len(telegram) > 0 {
|
||||
curExpect.telegram = telegram
|
||||
curExpect.telegram = utel
|
||||
curExpect.telegram128 = telegram
|
||||
t.baliseMessageCounterAdd1() //报文计数器
|
||||
}
|
||||
}
|
||||
@ -97,6 +145,7 @@ func (t *BaliseDetector) detect(wd *component.WorldData, repo *repository.Reposi
|
||||
} else {
|
||||
t.aboveBalise = false
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// 应答器计数器加1,[0,255]
|
||||
@ -115,15 +164,43 @@ func (t *BaliseDetector) baliseMessageCounterAdd1() {
|
||||
}
|
||||
}
|
||||
|
||||
// BTM天线接收应答器报文(线程不安全)
|
||||
func (t *BaliseDetector) rcvTelegram(wd *component.WorldData, baliseId string) []byte {
|
||||
entry, ok := wd.EntityMap[baliseId]
|
||||
if ok {
|
||||
if component.BaliseWorkStateType.Get(entry).Work {
|
||||
return component.BaliseFixedTelegramType.Get(entry).Telegram
|
||||
func BaliseCounterAdd(counter uint32, isLine12 bool) byte {
|
||||
c := byte(counter)
|
||||
c++
|
||||
if c > 255 {
|
||||
c = 1
|
||||
if isLine12 {
|
||||
c = 0
|
||||
}
|
||||
}
|
||||
return nil
|
||||
return c
|
||||
}
|
||||
|
||||
// BTM天线接收应答器报文(线程不安全)
|
||||
func (t *BaliseDetector) rcvTelegram(wd *component.WorldData, baliseId string) ([]byte, []byte) {
|
||||
|
||||
if entry, ok := wd.EntityMap[baliseId]; ok {
|
||||
workedState := component.BaliseWorkStateType.Get(entry)
|
||||
fixBalise := component.BaliseFixedTelegramType.Get(entry)
|
||||
if entry.HasComponent(component.BaliseVariableTelegramType) {
|
||||
baliseVar := component.BaliseVariableTelegramType.Get(entry)
|
||||
if !workedState.Work {
|
||||
return fixBalise.Telegram, fixBalise.UserTelegram
|
||||
} else if baliseVar.UserTelegram == nil || len(baliseVar.UserTelegram) == 0 {
|
||||
//slog.Warn(fmt.Sprintf("BTM天线未接受到应答器可变报文,即将使用对应的固定报文, baliseId: %v", baliseId))
|
||||
return fixBalise.Telegram, fixBalise.UserTelegram
|
||||
} else {
|
||||
return baliseVar.Telegram, baliseVar.UserTelegram
|
||||
}
|
||||
} else if workedState.Work {
|
||||
return fixBalise.Telegram, fixBalise.UserTelegram
|
||||
} else {
|
||||
//slog.Warn(fmt.Sprintf("BTM天线未接受到应答器报文,应答器未工作 baliseId: %v", baliseId))
|
||||
}
|
||||
} else {
|
||||
//slog.Warn(fmt.Sprintf("BTM天线接收应答器报文,未找到 baliseId: %v", baliseId))
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// true-新增;false-更新
|
||||
@ -139,10 +216,17 @@ func (t *BaliseDetector) addExpectedBalise(curExpect *BtmAntennaScanningBaliseIn
|
||||
// }
|
||||
//}
|
||||
//检查是否已经记录过
|
||||
eq := t.eq[len(t.eq)-1]
|
||||
if eq != nil && eq.BaliseId == curExpect.BaliseId {
|
||||
return false
|
||||
|
||||
for _, tt := range t.eq {
|
||||
if tt != nil && tt.BaliseId == curExpect.BaliseId {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
/* eq := t.eq[len(t.eq)-1]
|
||||
if eq != nil && eq.BaliseId == curExpect.BaliseId {
|
||||
return false
|
||||
}*/
|
||||
//左移
|
||||
for i := 1; i < len(t.eq); i++ {
|
||||
t.eq[i-1] = t.eq[i]
|
||||
@ -184,8 +268,10 @@ func (t *BaliseDetector) timeScanNearestBalise(curTime time.Time, wd *component.
|
||||
curAc := float64(ba.Acceleration)
|
||||
s := float64(expectedBalise.Distance) / 1000
|
||||
st, ok := t.calculateBtmAntennaScanNextBaliseTime(curTime, curV, curAc, s)
|
||||
|
||||
if ok {
|
||||
return &BtmAntennaScanningBaliseInfo{BaliseId: expectedBalise.BaliseId, Time: st}
|
||||
|
||||
return &BtmAntennaScanningBaliseInfo{BaliseId: expectedBalise.BaliseId, Time: st, Distance: expectedBalise.Distance, BaliseType: expectedBalise.BaliseType}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
@ -208,25 +294,29 @@ func (t *BaliseDetector) calculateBtmAntennaScanNextBaliseTime(curTime time.Time
|
||||
}
|
||||
|
||||
// 获取车载BTM天线中心点运行方向最近的1个应答器
|
||||
func (t *BaliseDetector) findBaliseWillScanByBtmAntenna(wd *component.WorldData, repo *repository.Repository, ba *BtmAntennaRunningInfo) *BtmAntennaToBaliseInfo {
|
||||
func (t *BaliseDetector) findBaliseWillScanByBtmAntenna2(wd *component.WorldData, repo *repository.Repository, ba *BtmAntennaRunningInfo) *BtmAntennaToBaliseInfo {
|
||||
//BTM天线中心点所在轨道
|
||||
baLink := repo.FindLink(ba.LinkId)
|
||||
rs1 := t.searchBalisesFromLinkPosition(repo, ba.LinkId, ba.Up, ba.LinkOffset)
|
||||
if ba.Up {
|
||||
if len(rs1) > 0 {
|
||||
return &BtmAntennaToBaliseInfo{BaliseId: rs1[0].Id(), Distance: rs1[0].LinkPosition().Offset() - ba.LinkOffset}
|
||||
rs := rs1[0]
|
||||
return &BtmAntennaToBaliseInfo{BaliseId: rs.Id(), Distance: rs.LinkPosition().Offset() - ba.LinkOffset, BaliseType: rs.BaliseType()}
|
||||
} else {
|
||||
nextLinkPort := t.getNextLink(wd, repo, ba.LinkId, ba.Up)
|
||||
if nextLinkPort != nil {
|
||||
if nextLinkPort.IsPortA() {
|
||||
rs2 := t.searchBalisesFromLinkPosition(repo, nextLinkPort.Link().Id(), true, 0)
|
||||
if len(rs2) > 0 {
|
||||
return &BtmAntennaToBaliseInfo{BaliseId: rs2[0].Id(), Distance: baLink.Length() - ba.LinkOffset + rs2[0].LinkPosition().Offset()}
|
||||
rs := rs2[0]
|
||||
|
||||
return &BtmAntennaToBaliseInfo{BaliseId: rs.Id(), Distance: baLink.Length() - ba.LinkOffset + rs.LinkPosition().Offset(), BaliseType: rs.BaliseType()}
|
||||
}
|
||||
} else {
|
||||
rs2 := t.searchBalisesFromLinkPosition(repo, nextLinkPort.Link().Id(), false, nextLinkPort.Link().Length())
|
||||
if len(rs2) > 0 {
|
||||
return &BtmAntennaToBaliseInfo{BaliseId: rs2[0].Id(), Distance: baLink.Length() - ba.LinkOffset + nextLinkPort.Link().Length() - rs2[0].LinkPosition().Offset()}
|
||||
rs := rs2[0]
|
||||
return &BtmAntennaToBaliseInfo{BaliseId: rs.Id(), Distance: baLink.Length() - ba.LinkOffset + nextLinkPort.Link().Length() - rs.LinkPosition().Offset(), BaliseType: rs.BaliseType()}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -254,6 +344,90 @@ func (t *BaliseDetector) findBaliseWillScanByBtmAntenna(wd *component.WorldData,
|
||||
return nil
|
||||
}
|
||||
|
||||
// 获取车载BTM天线中心点运行方向最近的1个应答器
|
||||
func (t *BaliseDetector) findBaliseWillScanByBtmAntenna(wd *component.WorldData, repo *repository.Repository, ba *BtmAntennaRunningInfo) *BtmAntennaToBaliseInfo {
|
||||
//BTM天线中心点所在轨道
|
||||
baLink := repo.FindLink(ba.LinkId)
|
||||
rs1 := t.searchBalisesFromLinkPosition(repo, ba.LinkId, ba.Up, ba.LinkOffset)
|
||||
if ba.Up {
|
||||
if len(rs1) > 0 {
|
||||
rs := rs1[0]
|
||||
return &BtmAntennaToBaliseInfo{BaliseId: rs.Id(), Distance: rs.LinkPosition().Offset() - ba.LinkOffset, BaliseType: rs.BaliseType()}
|
||||
} else {
|
||||
nextLinkPort := t.getNextLink(wd, repo, ba.LinkId, ba.Up)
|
||||
if nextLinkPort != nil {
|
||||
if nextLinkPort.IsPortA() {
|
||||
rs2 := t.searchBalisesFromLinkPosition(repo, nextLinkPort.Link().Id(), true, 0)
|
||||
if len(rs2) > 0 {
|
||||
rs := rs2[0]
|
||||
|
||||
return &BtmAntennaToBaliseInfo{BaliseId: rs.Id(), Distance: baLink.Length() - ba.LinkOffset + rs.LinkPosition().Offset(), BaliseType: rs.BaliseType()}
|
||||
}
|
||||
} else {
|
||||
rs2 := t.searchBalisesFromLinkPosition(repo, nextLinkPort.Link().Id(), false, nextLinkPort.Link().Length())
|
||||
if len(rs2) > 0 {
|
||||
rs := rs2[0]
|
||||
return &BtmAntennaToBaliseInfo{BaliseId: rs.Id(), Distance: baLink.Length() - ba.LinkOffset + nextLinkPort.Link().Length() - rs.LinkPosition().Offset(), BaliseType: rs.BaliseType()}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if len(rs1) > 0 {
|
||||
return &BtmAntennaToBaliseInfo{BaliseId: rs1[0].Id(), Distance: ba.LinkOffset - rs1[0].LinkPosition().Offset()}
|
||||
} else {
|
||||
nextLinkPort := t.getNextLink(wd, repo, ba.LinkId, ba.Up)
|
||||
if nextLinkPort != nil {
|
||||
if nextLinkPort.IsPortA() {
|
||||
rs2 := t.searchBalisesFromLinkPosition(repo, nextLinkPort.Link().Id(), true, 0)
|
||||
if len(rs2) > 0 {
|
||||
return &BtmAntennaToBaliseInfo{BaliseId: rs2[0].Id(), Distance: ba.LinkOffset + rs2[0].LinkPosition().Offset()}
|
||||
}
|
||||
} else {
|
||||
rs2 := t.searchBalisesFromLinkPosition(repo, nextLinkPort.Link().Id(), false, nextLinkPort.Link().Length())
|
||||
if len(rs2) > 0 {
|
||||
return &BtmAntennaToBaliseInfo{BaliseId: rs2[0].Id(), Distance: ba.LinkOffset + nextLinkPort.Link().Length() - rs2[0].LinkPosition().Offset()}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *BaliseDetector) searchBalisesFromBetweenLinkPosition(repo *repository.Repository, up bool, linkId string, fromOffset int64, toOffset int64) []*repository.Transponder {
|
||||
rs := repo.ResponderListByLink(linkId)
|
||||
balises := make([]*repository.Transponder, 0)
|
||||
if up {
|
||||
sort.SliceStable(rs, func(i, j int) bool {
|
||||
return rs[i].LinkPosition().Offset() < rs[j].LinkPosition().Offset()
|
||||
})
|
||||
for _, r := range rs {
|
||||
|
||||
if r.LinkPosition().Offset() >= fromOffset && r.LinkPosition().Offset() <= toOffset {
|
||||
//slog.Info(fmt.Sprintf("up id:%v,offset:%v,from:%v,to:%v", r.Id(), r.LinkPosition().Offset(), fromOffset, toOffset))
|
||||
balises = append(balises, r)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
sort.SliceStable(rs, func(i, j int) bool {
|
||||
return rs[j].LinkPosition().Offset() < rs[i].LinkPosition().Offset()
|
||||
})
|
||||
for _, r := range rs {
|
||||
//slog.Info(fmt.Sprintf("down id:%v,offset:%v,from:%v,to:%v", r.Id(), r.LinkPosition().Offset(), fromOffset, toOffset))
|
||||
if r.LinkPosition().Offset() <= toOffset {
|
||||
cha := int64(math.Abs(float64(toOffset - fromOffset)))
|
||||
cha2 := int64(math.Abs(float64(toOffset - r.LinkPosition().Offset())))
|
||||
if cha2 <= cha {
|
||||
balises = append(balises, r)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
return balises
|
||||
}
|
||||
|
||||
// up-在轨道上的搜索方向
|
||||
func (t *BaliseDetector) searchBalisesFromLinkPosition(repo *repository.Repository, linkId string, up bool, fromOffset int64) []*repository.Transponder {
|
||||
rs := repo.ResponderListByLink(linkId)
|
||||
@ -281,6 +455,34 @@ func (t *BaliseDetector) searchBalisesFromLinkPosition(repo *repository.Reposito
|
||||
}
|
||||
|
||||
// 列车车头端点运行信息转换为车载BTM天线中心点运行信息
|
||||
func (t *BaliseDetector) createBtmAntennaRunningInfo2(wd *component.WorldData, isSameLink bool, repo *repository.Repository, head *TrainHeadPositionInfo) *BtmAntennaRunningInfo {
|
||||
headLink := repo.FindLink(head.Link)
|
||||
if head.Up {
|
||||
if isSameLink { //车头与BTM天线在同一个轨道上
|
||||
return &BtmAntennaRunningInfo{Up: head.Up, LinkId: head.Link, LinkOffset: head.LinkOffset + int64(math.Abs(float64(BtmAntennaOffsetHead))), Speed: head.Speed, Acceleration: head.Acceleration}
|
||||
} else { //车头与BTM天线在同一个轨道上
|
||||
nextLinkPort := t.getNextLink(wd, repo, head.Link, !head.Up)
|
||||
nextLink := nextLinkPort.Link()
|
||||
if nextLinkPort.IsPortA() {
|
||||
return &BtmAntennaRunningInfo{Up: false, LinkId: nextLink.Id(), LinkOffset: BtmAntennaOffsetHead - head.LinkOffset, Speed: head.Speed, Acceleration: head.Acceleration}
|
||||
} else {
|
||||
return &BtmAntennaRunningInfo{Up: true, LinkId: nextLink.Id(), LinkOffset: nextLink.Length() - (BtmAntennaOffsetHead - head.LinkOffset), Speed: head.Speed, Acceleration: head.Acceleration}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if isSameLink { //车头与BTM天线在同一个轨道上
|
||||
return &BtmAntennaRunningInfo{Up: head.Up, LinkId: head.Link, LinkOffset: head.LinkOffset + BtmAntennaOffsetHead, Speed: head.Speed, Acceleration: head.Acceleration}
|
||||
} else {
|
||||
nextLinkPort := t.getNextLink(wd, repo, head.Link, !head.Up)
|
||||
nextLink := nextLinkPort.Link()
|
||||
if nextLinkPort.IsPortA() {
|
||||
return &BtmAntennaRunningInfo{Up: false, LinkId: nextLink.Id(), LinkOffset: BtmAntennaOffsetHead - headLink.Length() + head.LinkOffset, Speed: head.Speed, Acceleration: head.Acceleration}
|
||||
} else {
|
||||
return &BtmAntennaRunningInfo{Up: true, LinkId: nextLink.Id(), LinkOffset: nextLink.Length() - (BtmAntennaOffsetHead - headLink.Length() + head.LinkOffset), Speed: head.Speed, Acceleration: head.Acceleration}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
func (t *BaliseDetector) createBtmAntennaRunningInfo(wd *component.WorldData, repo *repository.Repository, head *TrainHeadPositionInfo) *BtmAntennaRunningInfo {
|
||||
headLink := repo.FindLink(head.Link)
|
||||
if head.Up {
|
||||
|
121
third_party/can_btm/train_balise_cache.go
vendored
Normal file
121
third_party/can_btm/train_balise_cache.go
vendored
Normal file
@ -0,0 +1,121 @@
|
||||
package can_btm
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"joylink.club/bj-rtsts-server/const/balise_const"
|
||||
"joylink.club/bj-rtsts-server/dto/state_proto"
|
||||
"joylink.club/rtsssimulation/repository"
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
|
||||
var baliseLock = &sync.Mutex{}
|
||||
|
||||
func IsLine12(train *state_proto.TrainState) bool {
|
||||
if strings.Contains(train.ProjectCode, "12") {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
func AddNewExpectedBalise(balise *repository.Transponder, btmCache *state_proto.TrainBtmCache, telegram, userTelegram []byte, isLine12 bool) bool {
|
||||
baliseLock.Lock()
|
||||
defer baliseLock.Unlock()
|
||||
bl := btmCache.BaliseList
|
||||
for _, tt := range bl {
|
||||
if tt != nil && tt.BaliseId == balise.Id() {
|
||||
return false
|
||||
}
|
||||
}
|
||||
for i := 1; i < len(bl); i++ {
|
||||
bl[i-1] = bl[i]
|
||||
}
|
||||
unpack := false
|
||||
bc := BaliseCounterAdd(btmCache.BaliseCount, isLine12)
|
||||
mc := btmCache.MessageCounter
|
||||
if userTelegram != nil && len(userTelegram) > 0 {
|
||||
mc = uint32(BaliseCounterAdd(mc, isLine12))
|
||||
unpack = true
|
||||
}
|
||||
|
||||
btmCache.BaliseCount = uint32(bc)
|
||||
btmCache.MessageCounter = mc
|
||||
btmS := &state_proto.BTMState{BaliseId: balise.Id(),
|
||||
Telegram: fmt.Sprintf("%x", userTelegram),
|
||||
Telegram128: fmt.Sprintf("%X", telegram),
|
||||
Unpack: unpack,
|
||||
BaliseType: int32(balise.BaliseType().Number()),
|
||||
AboveBalise: true, HasData: true}
|
||||
|
||||
if userTelegram == nil || len(userTelegram) == 0 {
|
||||
btmS.Telegram = strings.Repeat("00", balise_const.UserTelegramByteLen)
|
||||
btmS.Telegram128 = strings.Repeat("00", balise_const.TelegramByteLen)
|
||||
btmS.HasData = false
|
||||
}
|
||||
//存入队尾
|
||||
bl[len(bl)-1] = btmS
|
||||
return true
|
||||
}
|
||||
|
||||
// HandleTrainHeadPositionInfoForTrain 处理列车位置信息
|
||||
// 参数1: 参数2:发送序列号,参数3:应答器计数(每过一个应答器加一,在同一个应答器内不变),参数4:报文计数器 (每解出一个报文加一)(0~255)
|
||||
func FindBaliseResend(cache *state_proto.TrainBtmCache, isLine12 bool) (*state_proto.BTMState, byte, byte, byte) {
|
||||
baliseLock.Lock()
|
||||
defer baliseLock.Unlock()
|
||||
for _, balise := range cache.BaliseList {
|
||||
if balise != nil && balise.BaliseId == cache.ResendBaliseId && balise.ResendCount < 3 {
|
||||
balise.ResendCount++
|
||||
ndsn := BaliseCounterAdd(cache.Dsn, isLine12)
|
||||
cache.Dsn = uint32(ndsn)
|
||||
return balise, ndsn, byte(cache.BaliseCount), byte(cache.MessageCounter)
|
||||
}
|
||||
}
|
||||
ndsn := BaliseCounterAdd(cache.Dsn, isLine12)
|
||||
return nil, ndsn, 0, 0
|
||||
}
|
||||
|
||||
func FindBaliseByNotSend(cache *state_proto.TrainBtmCache, isLine12 bool) (*state_proto.BTMState, byte, byte, byte) {
|
||||
baliseLock.Lock()
|
||||
defer baliseLock.Unlock()
|
||||
|
||||
for _, btmCache := range cache.BaliseList {
|
||||
if btmCache != nil && !btmCache.IsSend {
|
||||
ndsn := BaliseCounterAdd(cache.Dsn, isLine12)
|
||||
cache.Dsn = uint32(ndsn)
|
||||
cache.ResendBaliseId = btmCache.BaliseId
|
||||
return btmCache, ndsn, byte(cache.BaliseCount), byte(cache.MessageCounter)
|
||||
}
|
||||
}
|
||||
ndsn := BaliseCounterAdd(cache.Dsn, isLine12)
|
||||
return nil, ndsn, 0, 0
|
||||
}
|
||||
func ClearBalise(train *state_proto.TrainState) {
|
||||
baliseLock.Lock()
|
||||
defer baliseLock.Unlock()
|
||||
train.BtmBaliseCacheA.BaliseList = make([]*state_proto.BTMState, 3)
|
||||
train.BtmBaliseCacheB.BaliseList = make([]*state_proto.BTMState, 3)
|
||||
}
|
||||
|
||||
// 11号线根据序列号查询
|
||||
func FindBaliseByMessageSerial(cache *state_proto.TrainBtmCache, isLine12 bool, ms byte) (*state_proto.BTMState, byte, bool) {
|
||||
baliseLock.Lock()
|
||||
defer baliseLock.Unlock()
|
||||
|
||||
for _, btmCache := range cache.BaliseList {
|
||||
if btmCache != nil {
|
||||
if btmCache.BaliseId == cache.ResendBaliseId {
|
||||
if byte(btmCache.PackageDataSN) == ms {
|
||||
bt, dsn, _, _ := FindBaliseByNotSend(cache, isLine12)
|
||||
return bt, dsn, true
|
||||
} else {
|
||||
ndsn := BaliseCounterAdd(cache.Dsn, isLine12)
|
||||
cache.Dsn = uint32(ndsn)
|
||||
return btmCache, ndsn, false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bt, dsn, _, _ := FindBaliseByNotSend(cache, isLine12)
|
||||
return bt, dsn, true
|
||||
|
||||
}
|
65
third_party/dynamics/dynamics.go
vendored
65
third_party/dynamics/dynamics.go
vendored
@ -5,6 +5,7 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"joylink.club/bj-rtsts-server/dto/state_proto"
|
||||
"log/slog"
|
||||
"net/http"
|
||||
"runtime/debug"
|
||||
@ -20,6 +21,8 @@ import (
|
||||
|
||||
type DynamicsMessageManager interface {
|
||||
CollectDynamicsTurnoutInfo() *message.DynamicsTurnoutInfo
|
||||
|
||||
CollectTrainControlState() []message.TrainControlMsg
|
||||
HandleDynamicsTrainInfo(info *message.DynamicsTrainInfo)
|
||||
GetDynamicsRunConfig() *config.DynamicsConfig
|
||||
GetDynamicsRunRepository() *message.LineBaseInfo
|
||||
@ -41,6 +44,8 @@ type Dynamics interface {
|
||||
|
||||
// 发送列车控制消息
|
||||
SendTrainControlMessage(b []byte)
|
||||
|
||||
SendTrainControl(cm *message.TrainControlMsg)
|
||||
}
|
||||
|
||||
var _default *dynamics
|
||||
@ -89,8 +94,16 @@ func (d *dynamics) updateState(state tpapi.ThirdPartyApiServiceState) {
|
||||
func (d *dynamics) State() tpapi.ThirdPartyApiServiceState {
|
||||
return d.state
|
||||
}
|
||||
|
||||
func (d *dynamics) Name() string {
|
||||
func (d *dynamics) FindAppendApiService() []tpapi.ThirdPartyApiService {
|
||||
return nil
|
||||
}
|
||||
func (d *dynamics) Type() state_proto.SimulationThirdPartyApiService_Type {
|
||||
return state_proto.SimulationThirdPartyApiService_Dynamics
|
||||
}
|
||||
func (d *dynamics) TrueService() bool {
|
||||
return true
|
||||
}
|
||||
func (d *dynamics) ServiceDesc() string {
|
||||
return Name
|
||||
}
|
||||
|
||||
@ -129,6 +142,7 @@ func (d *dynamics) requestStartSimulation(base *message.LineBaseInfo) error {
|
||||
}
|
||||
url := d.buildUrl("/api/start/")
|
||||
data, _ := json.Marshal(base)
|
||||
slog.Info(string(data))
|
||||
resp, err := d.httpClient.Post(url, "application/json", bytes.NewBuffer(data))
|
||||
if err != nil {
|
||||
return sys_error.New("动力学开始仿真请求发送错误", err)
|
||||
@ -167,6 +181,7 @@ func (d *dynamics) RequestAddTrain(info *message.InitTrainInfo) error {
|
||||
}
|
||||
url := d.buildUrl("/api/aerodynamics/init/train/")
|
||||
data, _ := json.Marshal(info)
|
||||
//fmt.Println(string(data))
|
||||
resp, err := d.httpClient.Post(url, "application/json", bytes.NewBuffer(data))
|
||||
if err != nil {
|
||||
return fmt.Errorf("动力学添加列车请求异常: %v", err)
|
||||
@ -205,6 +220,7 @@ func (d *dynamics) TrainOperationConfig(req *message.TrainOperationConfig) error
|
||||
return nil
|
||||
}
|
||||
data, _ := json.Marshal(req)
|
||||
//fmt.Println(string(data))
|
||||
url := d.buildUrl("/api/config/")
|
||||
resp, err := d.httpClient.Post(url, "application/json", bytes.NewBuffer(data))
|
||||
if err != nil {
|
||||
@ -244,11 +260,24 @@ func (d *dynamics) Start(manager DynamicsMessageManager) error {
|
||||
}
|
||||
ctx, cancle := context.WithCancel(context.Background())
|
||||
go d.sendTurnoutStateTask(ctx)
|
||||
go d.sendTrainControlStateTask(ctx)
|
||||
d.turnoutTaskCancel = cancle
|
||||
d.updateState(tpapi.ThirdPartyState_Normal)
|
||||
d.udpDelayRecorder.Start()
|
||||
return nil
|
||||
}
|
||||
func (d *dynamics) trainControl(ctx context.Context) {
|
||||
for {
|
||||
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
default:
|
||||
|
||||
time.Sleep(time.Second * 5)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 初始化客户端、服务等信息
|
||||
func (d *dynamics) initDynamics() error {
|
||||
@ -301,10 +330,32 @@ func (d *dynamics) Stop() {
|
||||
|
||||
const (
|
||||
// 道岔消息发送间隔,单位ms
|
||||
TurnoutMessageSendInterval = 50
|
||||
TurnoutMessageSendInterval = 50
|
||||
TrainControlMessageSendInterval = 15
|
||||
)
|
||||
|
||||
var turnoutStateLifeSignal uint16 //道岔消息生命信号
|
||||
// 定时发送列车控制状态
|
||||
func (d *dynamics) sendTrainControlStateTask(ctx context.Context) {
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
slog.Error("定时发送列车控制状态任务异常", "error", err, "stack", string(debug.Stack()))
|
||||
debug.PrintStack()
|
||||
}
|
||||
}()
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
default:
|
||||
}
|
||||
tccs := d.manager.CollectTrainControlState()
|
||||
for _, tcc := range tccs {
|
||||
d.SendTrainControl(&tcc)
|
||||
}
|
||||
time.Sleep(time.Millisecond * TrainControlMessageSendInterval)
|
||||
}
|
||||
}
|
||||
|
||||
// 定时发送道岔状态任务
|
||||
func (d *dynamics) sendTurnoutStateTask(ctx context.Context) {
|
||||
@ -329,5 +380,11 @@ func (d *dynamics) sendTurnoutStateTask(ctx context.Context) {
|
||||
}
|
||||
|
||||
func (d *dynamics) SendTrainControlMessage(b []byte) {
|
||||
d.trainControlUdpClient.Send(b)
|
||||
if d.trainControlUdpClient != nil {
|
||||
d.trainControlUdpClient.Send(b)
|
||||
}
|
||||
|
||||
}
|
||||
func (d *dynamics) SendTrainControl(cm *message.TrainControlMsg) {
|
||||
d.SendTrainControlMessage(cm.Encode())
|
||||
}
|
||||
|
@ -1,7 +1,11 @@
|
||||
package electrical_machinery
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"joylink.club/bj-rtsts-server/dto/common_proto"
|
||||
"joylink.club/bj-rtsts-server/dto/state_proto"
|
||||
"log/slog"
|
||||
"sync"
|
||||
|
||||
"joylink.club/bj-rtsts-server/config"
|
||||
@ -9,22 +13,30 @@ import (
|
||||
"joylink.club/bj-rtsts-server/third_party/udp"
|
||||
)
|
||||
|
||||
// 电机转速UDP
|
||||
const (
|
||||
PointA = iota + 1
|
||||
PointB
|
||||
)
|
||||
|
||||
// ElectricMachinery 速传通信服务接口
|
||||
type ElectricMachinery interface {
|
||||
Start(manager ElectricMachineryMessageManager) // 启动电机转速UDP消息处理
|
||||
Stop() // 停止电机转速消息处理
|
||||
SendElectricMachineryMessage(info *message.ElectricMachinery) // 发送电机转速消息
|
||||
Start(manager ElectricMachineryMessageManager) // 启动电机转速UDP消息处理
|
||||
Stop() // 停止电机转速消息处理
|
||||
SendElectricMachineryMessage(emMap map[int]*message.ElectricMachinery) // 发送电机转速消息
|
||||
SendElectricMachineryMessage2(info *message.DynamicsTrainInfo, trainState *state_proto.TrainState) // 发送电机转速消息
|
||||
ClearOrRemoveTrain(trainState *state_proto.TrainState)
|
||||
}
|
||||
|
||||
type ElectricMachineryMessageManager interface {
|
||||
GetElectricMachineryRunConfig() *config.ElectricMachineryConfig // 获取电机转速参数
|
||||
GetElectricMachineryRunConfig() []config.ElectricMachineryConfig // 获取电机转速参数
|
||||
}
|
||||
|
||||
type electricalMachineryImpl struct {
|
||||
electricalMachineryUdpClient udp.UdpClient
|
||||
//electricalMachineryUdpClient udp.UdpClient
|
||||
|
||||
manager ElectricMachineryMessageManager
|
||||
runConfig *config.ElectricMachineryConfig
|
||||
electricalMachineryUdpClientMap map[int]udp.UdpClient
|
||||
manager ElectricMachineryMessageManager
|
||||
runConfig []config.ElectricMachineryConfig
|
||||
}
|
||||
|
||||
func (s *electricalMachineryImpl) Start(manager ElectricMachineryMessageManager) {
|
||||
@ -35,7 +47,13 @@ func (s *electricalMachineryImpl) Start(manager ElectricMachineryMessageManager)
|
||||
panic("启动电机转速消息服务错误: 存在正在运行的任务")
|
||||
}
|
||||
s.runConfig = manager.GetElectricMachineryRunConfig()
|
||||
if s.runConfig == nil || s.runConfig.Ip == "" || !s.runConfig.Open {
|
||||
allNotOpen := true
|
||||
for _, c := range s.runConfig {
|
||||
if &c != nil && c.Open {
|
||||
allNotOpen = false
|
||||
}
|
||||
}
|
||||
if allNotOpen {
|
||||
return
|
||||
}
|
||||
// 初始化客户端、服务端
|
||||
@ -47,21 +65,95 @@ func (s *electricalMachineryImpl) Stop() {
|
||||
initMutex.Lock()
|
||||
defer initMutex.Unlock()
|
||||
_default = nil
|
||||
if s.electricalMachineryUdpClient != nil {
|
||||
s.electricalMachineryUdpClient.Close()
|
||||
//if s.electricalMachineryUdpClient != nil {
|
||||
// s.electricalMachineryUdpClient.Close()
|
||||
//}
|
||||
for _, c := range s.electricalMachineryUdpClientMap {
|
||||
if &c != nil {
|
||||
c.Close()
|
||||
}
|
||||
}
|
||||
s.manager = nil
|
||||
}
|
||||
func (s *electricalMachineryImpl) ClearOrRemoveTrain(trainState *state_proto.TrainState) {
|
||||
collectSpeedMap := initEMMsg(trainState)
|
||||
if trainState.VobcState.Tc1Active {
|
||||
collectSpeed(0, 0, trainState.TrainEndsA, collectSpeedMap)
|
||||
|
||||
func (s *electricalMachineryImpl) SendElectricMachineryMessage(info *message.ElectricMachinery) {
|
||||
if s.electricalMachineryUdpClient == nil {
|
||||
return
|
||||
} else if trainState.VobcState.Tc2Active {
|
||||
collectSpeed(0, 0, trainState.TrainEndsB, collectSpeedMap)
|
||||
}
|
||||
s.electricalMachineryUdpClient.Send(info.Encode())
|
||||
// 更新电机转速
|
||||
s.SendElectricMachineryMessage(collectSpeedMap)
|
||||
}
|
||||
func (s *electricalMachineryImpl) SendElectricMachineryMessage(emMap map[int]*message.ElectricMachinery) {
|
||||
for key, em := range emMap {
|
||||
client := s.electricalMachineryUdpClientMap[key]
|
||||
if client != nil {
|
||||
data := em.Encode()
|
||||
hexStr := hex.EncodeToString(data)
|
||||
slog.Info("发送电机转速消息key", hexStr)
|
||||
err := client.Send(data)
|
||||
if err != nil {
|
||||
slog.Info("发送电机转速消息失败key", hexStr)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
func (s *electricalMachineryImpl) SendElectricMachineryMessage2(info *message.DynamicsTrainInfo, trainState *state_proto.TrainState) {
|
||||
collectSpeedMap := initEMMsg(trainState)
|
||||
if trainState.VobcState.Tc1Active {
|
||||
collectSpeed(info.HeadSpeed1, info.HeadSpeed2, trainState.TrainEndsA, collectSpeedMap)
|
||||
|
||||
} else if trainState.VobcState.Tc2Active {
|
||||
collectSpeed(info.TailSpeed1, info.TailSpeed2, trainState.TrainEndsB, collectSpeedMap)
|
||||
}
|
||||
// 更新电机转速
|
||||
s.SendElectricMachineryMessage(collectSpeedMap)
|
||||
}
|
||||
func collectSpeed(speed1, speed2 float32, endP *common_proto.TrainEndsState, resultMap map[int]*message.ElectricMachinery) {
|
||||
pa := resultMap[PointA]
|
||||
pb := resultMap[PointB]
|
||||
if endP.SpeedSensorEnableA || endP.SpeedSensorEnableB {
|
||||
if endP.AccOutSpeed > 0 {
|
||||
pa.Speed = float32(endP.AccOutSpeed) / 3.6
|
||||
pb.Speed = float32(endP.AccOutSpeed) / 3.6
|
||||
} else {
|
||||
if endP.SpeedSensorEnableA {
|
||||
pa.Speed = speed1
|
||||
}
|
||||
if endP.SpeedSensorEnableB {
|
||||
pb.Speed = speed2
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func initEMMsg(trainState *state_proto.TrainState) map[int]*message.ElectricMachinery {
|
||||
|
||||
collectSpeedMap := map[int]*message.ElectricMachinery{
|
||||
PointA: {
|
||||
Speed: 0,
|
||||
WheelDiameter: trainState.WheelDiameter,
|
||||
IsBack: trainState.VobcState.DirectionBackward},
|
||||
PointB: {Speed: 0,
|
||||
WheelDiameter: trainState.WheelDiameter,
|
||||
IsBack: trainState.VobcState.DirectionBackward},
|
||||
}
|
||||
return collectSpeedMap
|
||||
}
|
||||
|
||||
func (s *electricalMachineryImpl) initElectricalMachinery() {
|
||||
s.electricalMachineryUdpClient = udp.NewClient(fmt.Sprintf("%v:%v", s.runConfig.Ip, s.runConfig.RemotePort))
|
||||
s.electricalMachineryUdpClientMap = map[int]udp.UdpClient{}
|
||||
for _, c := range s.runConfig {
|
||||
if &c != nil && c.Ip != "" && c.Open {
|
||||
ep := PointA
|
||||
if !c.EndPointA {
|
||||
ep = PointB
|
||||
}
|
||||
s.electricalMachineryUdpClientMap[ep] = udp.NewClient(fmt.Sprintf("%v:%v", c.Ip, c.RemotePort))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var _default ElectricMachinery
|
||||
|
25
third_party/electrical_machinery/electrical_machinery_test.go
vendored
Normal file
25
third_party/electrical_machinery/electrical_machinery_test.go
vendored
Normal file
@ -0,0 +1,25 @@
|
||||
package electrical_machinery
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"joylink.club/bj-rtsts-server/third_party/message"
|
||||
"joylink.club/bj-rtsts-server/third_party/udp"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestServer(t *testing.T) {
|
||||
udpServer := udp.NewServer(fmt.Sprintf("127.0.0.1:%d", 9999), handleCanetFrames)
|
||||
udpServer.Listen()
|
||||
select {}
|
||||
}
|
||||
func handleCanetFrames(d []byte) {
|
||||
msg := &message.ElectricMachinery{}
|
||||
msg.Decode(d)
|
||||
}
|
||||
|
||||
func TestOther(t *testing.T) {
|
||||
dd := 1714024680 - time.Now().Unix()
|
||||
fmt.Println(dd)
|
||||
fmt.Println(int32(dd))
|
||||
}
|
10
third_party/example/main.go
vendored
10
third_party/example/main.go
vendored
@ -4,7 +4,7 @@ import (
|
||||
"fmt"
|
||||
"log/slog"
|
||||
|
||||
"joylink.club/bj-rtsts-server/third_party/balise"
|
||||
"joylink.club/bj-rtsts-server/third_party/balisecodec"
|
||||
)
|
||||
|
||||
type TestMsg struct {
|
||||
@ -40,19 +40,19 @@ func main() {
|
||||
// }
|
||||
|
||||
// time.Sleep(time.Second * 60)
|
||||
// for i, v := range balise.ConvWords {
|
||||
// for i, v := range balisecodec.ConvWords {
|
||||
// fmt.Printf("0%o,", v)
|
||||
// if i%10 == 9 {
|
||||
// fmt.Println()
|
||||
// }
|
||||
// }
|
||||
v10 := balise.ToVal([]int{
|
||||
v10 := balisecodec.ToValLeftMsb([]byte{
|
||||
1, 1, 1,
|
||||
1, 1, 1, 1,
|
||||
1, 1, 1, 1,
|
||||
1, 1, 1, 1})
|
||||
fmt.Println(v10)
|
||||
bs := balise.ToBits(1982, 11)
|
||||
bs := balisecodec.ToBitsLeftMsb(1982, 11)
|
||||
fmt.Println(bs)
|
||||
// fmt.Printf("%o\n", balise.ConvWords[511])
|
||||
// fmt.Printf("%o\n", balisecodec.ConvWords[511])
|
||||
}
|
||||
|
155
third_party/example/rssp/ci/ci_server.go
vendored
155
third_party/example/rssp/ci/ci_server.go
vendored
@ -1,155 +0,0 @@
|
||||
package ci
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"joylink.club/bj-rtsts-server/config"
|
||||
"joylink.club/bj-rtsts-server/third_party/axle_device"
|
||||
"joylink.club/bj-rtsts-server/third_party/message"
|
||||
"sort"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
type CiServer interface {
|
||||
Start()
|
||||
Stop()
|
||||
//SendSectionReset 向计轴设备发送计轴命令
|
||||
SendSectionReset(sectionId string, drst bool, pdrst bool)
|
||||
//HandleSectionStatus 收到来自计轴设备的物理区段状态
|
||||
HandleSectionStatus(status []*message.SectionStatusMsg)
|
||||
}
|
||||
|
||||
// 北京12号线酒仙桥集中站物理区段码表
|
||||
var codePointMap = map[int]string{0: "北京_12_酒仙桥_9G", 1: "北京_12_酒仙桥_1DG", 2: "北京_12_酒仙桥_11G", 3: "北京_12_酒仙桥_13G", 4: "北京_12_酒仙桥_15G", 5: "北京_12_酒仙桥_6G", 6: "北京_12_酒仙桥_8G", 7: "北京_12_酒仙桥_2DG", 8: "北京_12_酒仙桥_10G", 9: "北京_12_酒仙桥_12G", 10: "北京_12_酒仙桥_14G"}
|
||||
var idRowMap = make(map[string]int)
|
||||
var sectionCmds []*message.SectionCmdMsg
|
||||
var cmdsLock = sync.Mutex{}
|
||||
|
||||
// rssp 测试
|
||||
type ciServer struct {
|
||||
//所属城市
|
||||
city string
|
||||
//所属线路
|
||||
lineId string
|
||||
//所属集中站
|
||||
centralizedStation string
|
||||
//接收方每个安全通信会话对应的发送周期值,单位ms
|
||||
sendingPeriod uint32
|
||||
//主安全通道
|
||||
rsspChannel *axle_device.RsspChannel
|
||||
//
|
||||
cancel context.CancelFunc
|
||||
}
|
||||
|
||||
func (s *ciServer) HandleSectionStatus(status []*message.SectionStatusMsg) {
|
||||
if len(codePointMap) != len(status) {
|
||||
fmt.Println("==>>接收到的区段状态与码表不对应:", "codePointsLen=", len(codePointMap), ",statusLen=", len(status))
|
||||
return
|
||||
}
|
||||
fmt.Println("==>>接收到的区段状态----------------------------------------------------------------------------i")
|
||||
for row, state := range status {
|
||||
sectionId := codePointMap[row]
|
||||
fmt.Printf("==>>[%d]区段[%s]状态: Clr=%t Occ=%t Rac=%t Rjo=%t Rjt=%t\n", row, sectionId, state.Clr, state.Occ, state.Rac, state.Rjo, state.Rjt)
|
||||
}
|
||||
fmt.Println("==>>接收到的区段状态----------------------------------------------------------------------------o")
|
||||
}
|
||||
func (s *ciServer) periodRun(runContext context.Context) {
|
||||
defer func() {
|
||||
fmt.Println("==>>CI Server 周期运行退出 ...")
|
||||
}()
|
||||
for {
|
||||
select {
|
||||
case <-runContext.Done():
|
||||
return
|
||||
default:
|
||||
}
|
||||
s.doSendSectionCmdMsg()
|
||||
time.Sleep(time.Duration(s.sendingPeriod) * time.Millisecond)
|
||||
//更新周期性参数
|
||||
s.rsspChannel.NextPeriod()
|
||||
}
|
||||
}
|
||||
func (s *ciServer) doSendSectionCmdMsg() {
|
||||
if s.rsspChannel != nil {
|
||||
data := s.collectSectionCmdsData()
|
||||
fmt.Println("==>>CI发送区段指令数据", ",len=", len(data))
|
||||
s.rsspChannel.SendUserData(data)
|
||||
}
|
||||
}
|
||||
func (s *ciServer) collectSectionCmdsData() []byte {
|
||||
defer func() {
|
||||
cmdsLock.Unlock()
|
||||
}()
|
||||
cmdsLock.Lock()
|
||||
//
|
||||
data := (&message.SectionCmdMsgPack{Ck: 0x80, Scs: sectionCmds}).Encode()
|
||||
return data
|
||||
}
|
||||
|
||||
// SendSectionReset 发送区段复位指令
|
||||
func (s *ciServer) SendSectionReset(sectionId string, drst bool, pdrst bool) {
|
||||
defer func() {
|
||||
cmdsLock.Unlock()
|
||||
}()
|
||||
cmdsLock.Lock()
|
||||
//
|
||||
sec := sectionCmds[idRowMap[sectionId]]
|
||||
sec.Drst = drst
|
||||
sec.Pdrst = pdrst
|
||||
}
|
||||
|
||||
type rowId struct {
|
||||
row int
|
||||
id string
|
||||
}
|
||||
|
||||
func (s *ciServer) Start() {
|
||||
s.rsspChannel.SetRcvUserDataCallback(s.rcvUserData)
|
||||
s.rsspChannel.Start()
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
go s.periodRun(ctx)
|
||||
s.cancel = cancel
|
||||
}
|
||||
func (s *ciServer) Stop() {
|
||||
if s.rsspChannel != nil {
|
||||
s.rsspChannel.Stop()
|
||||
s.rsspChannel = nil
|
||||
}
|
||||
if s.cancel != nil {
|
||||
s.cancel()
|
||||
}
|
||||
}
|
||||
func (s *ciServer) rcvUserData(data []byte) {
|
||||
fmt.Println("==>>CI接收到区段状态数据", ",len=", len(data))
|
||||
msg := &message.SectionStatusMsgPack{}
|
||||
msg.Decode(data)
|
||||
s.HandleSectionStatus(msg.Sms)
|
||||
}
|
||||
func NewRsspCiServer(cfg *config.RsspAxleConfig) CiServer {
|
||||
ra := &ciServer{}
|
||||
//
|
||||
ra.city = cfg.City
|
||||
ra.lineId = cfg.LineId
|
||||
ra.centralizedStation = cfg.CentralizedStation
|
||||
ra.sendingPeriod = cfg.RsspCfg.SendingPeriod
|
||||
//
|
||||
mrc := &axle_device.RsspChannel{}
|
||||
ra.rsspChannel = mrc.Init(&cfg.RsspCfg)
|
||||
//
|
||||
return ra
|
||||
}
|
||||
|
||||
func InitCiServerCodePoints() {
|
||||
var ris []*rowId
|
||||
for row, id := range codePointMap {
|
||||
idRowMap[id] = row
|
||||
ris = append(ris, &rowId{row: row, id: id})
|
||||
}
|
||||
sort.SliceStable(ris, func(i, j int) bool {
|
||||
return ris[i].row < ris[j].row
|
||||
})
|
||||
for range ris {
|
||||
sectionCmds = append(sectionCmds, &message.SectionCmdMsg{Drst: false, Pdrst: false})
|
||||
}
|
||||
}
|
46
third_party/example/rssp/main.go
vendored
46
third_party/example/rssp/main.go
vendored
@ -1,46 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"joylink.club/bj-rtsts-server/config"
|
||||
"joylink.club/bj-rtsts-server/third_party/example/rssp/ci"
|
||||
"joylink.club/bj-rtsts-server/third_party/message"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// message.InitRsspCrcTable()
|
||||
ci.InitCiServerCodePoints()
|
||||
//
|
||||
cfg := &config.RsspAxleConfig{}
|
||||
cfg.City = "北京"
|
||||
cfg.LineId = "12"
|
||||
cfg.CentralizedStation = "酒仙桥"
|
||||
cfg.RsspCfg.SrcAddr = 0x01
|
||||
cfg.RsspCfg.DstAddr = 0x02
|
||||
cfg.RsspCfg.DataVer1 = 0x0011
|
||||
cfg.RsspCfg.DataVer2 = 0x0012
|
||||
cfg.RsspCfg.SID1 = 0x10000000
|
||||
cfg.RsspCfg.SID2 = 0x00000001
|
||||
cfg.RsspCfg.SINIT1 = 0x01
|
||||
cfg.RsspCfg.SINIT2 = 0x02
|
||||
cfg.RsspCfg.SendingPeriod = 500
|
||||
cfg.RsspCfg.SsrRsspTimeout = 3
|
||||
cfg.RsspCfg.Mtv = 3
|
||||
cfg.RsspCfg.DeviceA = true
|
||||
cfg.RsspCfg.PicType = message.PIC_MASTER
|
||||
cfg.RsspCfg.RemoteIp = "192.168.3.5"
|
||||
cfg.RsspCfg.RemoteUdpPort = 6666
|
||||
cfg.RsspCfg.LocalUdpPort = 7777
|
||||
//
|
||||
ciServer := ci.NewRsspCiServer(cfg)
|
||||
ciServer.Start()
|
||||
fmt.Println("==>>ci server ...")
|
||||
for {
|
||||
time.Sleep(2 * time.Second)
|
||||
ciServer.SendSectionReset("北京_12_酒仙桥_9G", true, false)
|
||||
}
|
||||
//
|
||||
time.Sleep(3600 * time.Second)
|
||||
}
|
398
third_party/interlock/beijing11/msg.go
vendored
Normal file
398
third_party/interlock/beijing11/msg.go
vendored
Normal file
@ -0,0 +1,398 @@
|
||||
package beijing11
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"github.com/snksoft/crc"
|
||||
)
|
||||
|
||||
const ( //不知道对不对,问也不回,先瞎写吧
|
||||
ON = 0x55 //按钮-按下;道岔-定位;屏蔽门-关门;计轴区段-占用
|
||||
OFF = 0xAA //按钮-抬起;道岔-反位;屏蔽门-开门;计轴区段-出清
|
||||
)
|
||||
|
||||
const (
|
||||
SignalAspect_H = 0x01 //红灯
|
||||
SignalAspect_L = 0x04 //绿灯
|
||||
SignalAspect_U = 0x02 //黄灯
|
||||
SignalAspect_HU = 0x03 //红黄
|
||||
SignalAspect_B = 0x08 //白灯
|
||||
)
|
||||
|
||||
func GetStateByte(state bool) byte {
|
||||
if state {
|
||||
return ON
|
||||
} else {
|
||||
return OFF
|
||||
}
|
||||
}
|
||||
|
||||
// FromInterlockFrame 来自联锁的数据帧
|
||||
type FromInterlockFrame struct {
|
||||
Len uint16
|
||||
InterlockCode uint16 //联锁编号(联锁->轨旁:OC的编号,轨旁->联锁:联锁站编号)
|
||||
WaysideCode uint16 //轨旁编号
|
||||
TurnoutData *DeviceData //道岔数据
|
||||
PSDData *DeviceData //屏蔽门数据
|
||||
ESBData *ESBData //紧急停车按钮数据
|
||||
HoldTrainData *HoldTrainData //扣车按钮数据
|
||||
SignalData *DeviceData //信号机数据
|
||||
AxleSectionData *DeviceData //计轴区段数据
|
||||
WrzfData *DeviceData //无人折返数据
|
||||
FymData *DeviceData //防淹门数据
|
||||
SPKSData *SPKSData //SPKS数据
|
||||
CkmData *DeviceData //车库门数据
|
||||
//XcjData *DeviceData //洗车机数据
|
||||
}
|
||||
|
||||
func (f *FromInterlockFrame) Decode(data []byte) error {
|
||||
f.Len = binary.BigEndian.Uint16(data[1:3])
|
||||
//if int(f.Len) != len(data)-3 {
|
||||
// return errors.New(fmt.Sprintf("%s数据长度不对[%d:%d]", logTag, f.Len, len(data)-3))
|
||||
//}
|
||||
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 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],
|
||||
})
|
||||
}
|
||||
deviceData := &DeviceData{
|
||||
Num: num,
|
||||
CmdList: cmdList,
|
||||
}
|
||||
return deviceData, index + i
|
||||
}
|
||||
|
||||
func decodeESBData(data []byte, index uint16) (*ESBData, uint16) {
|
||||
data = data[index:]
|
||||
num := binary.BigEndian.Uint16(data[0:2])
|
||||
nextIndex := 2 + 4*num
|
||||
data = data[2:nextIndex]
|
||||
var cmdList []*ESBCmd
|
||||
for i := 0; i < len(data); i += 4 {
|
||||
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 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],
|
||||
})
|
||||
}
|
||||
deviceData := &HoldTrainData{
|
||||
Num: num,
|
||||
CmdList: cmdList,
|
||||
}
|
||||
return deviceData, index + nextIndex
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
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 {
|
||||
WaysideCode uint16
|
||||
InterlockCode uint16
|
||||
TurnoutStates []*TurnoutState //道岔状态
|
||||
PsdStates []*PSDState //屏蔽门状态
|
||||
ESBStates []*ESBState //紧急停车按钮状态
|
||||
//HoldTrainData []*HoldTrainState //扣车按钮状态(数量0)
|
||||
SignalStates []*SignalState //信号机状态
|
||||
AxleSectionStates []*AxleSectionState //计轴区段状态
|
||||
//WrzfData []*WrzfState //无人折返状态(数量0)
|
||||
//FymData []*FymState //防淹门状态(数量0)
|
||||
SPKSStates []*SPKSState //SPKS状态
|
||||
//CkmData []*CkmState //车库门状态(数量0)
|
||||
//XcjData []*XcjState //洗车机状态(无)
|
||||
}
|
||||
|
||||
func (t *ToInterlockFrame) encode() []byte {
|
||||
data := make([]byte, 0, 128)
|
||||
data = append(data, 0x83) //报文类型
|
||||
data = append(data, 0, 0) //报文长度占位
|
||||
data = binary.BigEndian.AppendUint16(data, t.WaysideCode)
|
||||
data = binary.BigEndian.AppendUint16(data, t.InterlockCode)
|
||||
//道岔
|
||||
data = binary.BigEndian.AppendUint16(data, uint16(len(t.TurnoutStates)))
|
||||
for _, state := range t.TurnoutStates {
|
||||
data = state.encode(data)
|
||||
}
|
||||
//屏蔽门
|
||||
data = binary.BigEndian.AppendUint16(data, uint16(len(t.PsdStates)))
|
||||
for _, state := range t.PsdStates {
|
||||
data = state.encode(data)
|
||||
}
|
||||
//紧急停车
|
||||
data = binary.BigEndian.AppendUint16(data, uint16(len(t.ESBStates)))
|
||||
for _, state := range t.ESBStates {
|
||||
data = state.encode(data)
|
||||
}
|
||||
//扣车,数量0
|
||||
data = binary.BigEndian.AppendUint16(data, 0)
|
||||
//信号机状态
|
||||
data = binary.BigEndian.AppendUint16(data, uint16(len(t.SignalStates)))
|
||||
for _, state := range t.SignalStates {
|
||||
data = state.encode(data)
|
||||
}
|
||||
//计轴区段
|
||||
data = binary.BigEndian.AppendUint16(data, uint16(len(t.AxleSectionStates)))
|
||||
for _, state := range t.AxleSectionStates {
|
||||
data = state.encode(data)
|
||||
}
|
||||
//无人折返,数量0
|
||||
data = binary.BigEndian.AppendUint16(data, 0)
|
||||
//防淹门,数量0
|
||||
data = binary.BigEndian.AppendUint16(data, 0)
|
||||
//SPKS
|
||||
data = binary.BigEndian.AppendUint16(data, uint16(len(t.SPKSStates)))
|
||||
for _, state := range t.SPKSStates {
|
||||
data = state.encode(data)
|
||||
}
|
||||
//填充字节
|
||||
fillSlice := make([]byte, 19)
|
||||
fillSlice[2] = 1
|
||||
data = append(data, fillSlice...)
|
||||
//CRC32
|
||||
data = binary.BigEndian.AppendUint32(data, crc32(data[3:]))
|
||||
//填充报文长度
|
||||
binary.BigEndian.PutUint16(data[1:3], uint16(len(data)-7))
|
||||
return data
|
||||
}
|
||||
|
||||
type TurnoutState struct {
|
||||
Id uint16 //道岔ID
|
||||
State byte //道岔的采集状态
|
||||
//Alarm byte //道岔的报警状态
|
||||
//Current1 byte //转辙机1电流值,单位A
|
||||
//Current2 byte //转辙机2电流值,单位A
|
||||
}
|
||||
|
||||
func (s *TurnoutState) encode(data []byte) []byte {
|
||||
data = binary.BigEndian.AppendUint16(data, s.Id)
|
||||
data = append(data, s.State /*, s.Alarm, s.Current1, s.Current2*/)
|
||||
return data
|
||||
}
|
||||
|
||||
type PSDState struct {
|
||||
Id uint16 //屏蔽门ID
|
||||
State byte //屏蔽门开关门状态
|
||||
Hsjc byte //互锁解除状态
|
||||
PCB byte //PCB状态
|
||||
POB byte //POB状态
|
||||
DPB byte //DPB状态
|
||||
}
|
||||
|
||||
func (s *PSDState) encode(data []byte) []byte {
|
||||
data = binary.BigEndian.AppendUint16(data, s.Id)
|
||||
data = append(data, s.State, s.Hsjc, s.PCB, s.POB, s.DPB)
|
||||
return data
|
||||
}
|
||||
|
||||
type ESBState struct {
|
||||
Id uint16 //紧急停车ID
|
||||
State byte //紧急停车按钮状态
|
||||
PlState byte //紧急停车按钮旁路状态
|
||||
//ESS byte //ESS状态(按下55,抬起aa)(解析驱动数据,没有此状态对应的驱动)
|
||||
}
|
||||
|
||||
func (s *ESBState) encode(data []byte) []byte {
|
||||
data = binary.BigEndian.AppendUint16(data, s.Id)
|
||||
data = append(data, s.State, s.PlState)
|
||||
return data
|
||||
}
|
||||
|
||||
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 //信号机当前亮灯的电流值
|
||||
}
|
||||
|
||||
func (s *SignalState) encode(data []byte) []byte {
|
||||
data = binary.BigEndian.AppendUint16(data, s.Id)
|
||||
data = append(data, s.State /*s.Current*/)
|
||||
return data
|
||||
}
|
||||
|
||||
type AxleSectionState struct {
|
||||
Id uint16 //计轴区段Id
|
||||
State byte //计轴区段的采集状态
|
||||
}
|
||||
|
||||
func (s *AxleSectionState) encode(data []byte) []byte {
|
||||
data = binary.BigEndian.AppendUint16(data, s.Id)
|
||||
data = append(data, s.State)
|
||||
return data
|
||||
}
|
||||
|
||||
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按钮状态
|
||||
}
|
||||
|
||||
func (s *SPKSState) encode(data []byte) []byte {
|
||||
data = binary.BigEndian.AppendUint16(data, s.Id)
|
||||
data = append(data, s.State, s.PlState)
|
||||
return data
|
||||
}
|
||||
|
||||
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 //模式
|
||||
}
|
||||
|
||||
var crcHash = crc.NewHash(&crc.Parameters{Width: 32, Polynomial: 0x4c11db7, Init: 0x0, ReflectIn: false, ReflectOut: false, FinalXor: 0x0})
|
||||
|
||||
func crc32(data []byte) uint32 {
|
||||
return uint32(crcHash.CalculateCRC(data))
|
||||
}
|
193
third_party/interlock/beijing11/msg_test.go
vendored
Normal file
193
third_party/interlock/beijing11/msg_test.go
vendored
Normal file
@ -0,0 +1,193 @@
|
||||
package beijing11
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"joylink.club/bj-rtsts-server/util/myreader"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestFromInterlockFrame_Decode(t *testing.T) {
|
||||
str := "58bea4c65e0e583f5bdedc310800450000c949950000801100003d0b0a0d3d0b7814413c40d800b5fcfd8200a63c0363010003000400000500000900000200040000080000020004aaaa0008aaaa0000000f000d01000e01000f060010060021060023010011060012060026060027010013060014010022060024060025010010001300001400001500001600001700001800001900001a00002c00002d00002e00002f00003000003100003200003300000000000004000daaaa000eaaaa000faaaa0010aaaa0000000000000000000000007a91cfc2"
|
||||
data, err := hex.DecodeString(str)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if data[0] != 0x82 {
|
||||
data = data[42:] //去掉wireshark抓包携带的额外字节
|
||||
}
|
||||
reader := myreader.NewReader(data)
|
||||
fmt.Printf("%x\t【报文类型】\n", reader.ReadByte())
|
||||
fmt.Printf("%x\t【报文长度】\n", reader.ReadBytes(2))
|
||||
fmt.Printf("%x\t【联锁编号】\n", reader.ReadBytes(2))
|
||||
fmt.Printf("%x\t【轨旁编号】\n", reader.ReadBytes(2))
|
||||
{ //道岔
|
||||
deviceNum := reader.ReadBytes(2)
|
||||
fmt.Printf("%x\t", deviceNum)
|
||||
for i := 0; i < int(binary.BigEndian.Uint16(deviceNum)); i++ {
|
||||
fmt.Printf("%x ", reader.ReadBytes(3))
|
||||
}
|
||||
fmt.Println("【道岔】")
|
||||
}
|
||||
{ //屏蔽门
|
||||
deviceNum := reader.ReadBytes(2)
|
||||
fmt.Printf("%x\t", deviceNum)
|
||||
for i := 0; i < int(binary.BigEndian.Uint16(deviceNum)); i++ {
|
||||
fmt.Printf("%x ", reader.ReadBytes(3))
|
||||
}
|
||||
fmt.Println("【屏蔽门】")
|
||||
}
|
||||
{ //紧急停车按钮
|
||||
deviceNum := reader.ReadBytes(2)
|
||||
fmt.Printf("%x\t", deviceNum)
|
||||
for i := 0; i < int(binary.BigEndian.Uint16(deviceNum)); i++ {
|
||||
fmt.Printf("%x ", reader.ReadBytes(4))
|
||||
}
|
||||
fmt.Println("【紧急停车按钮】")
|
||||
}
|
||||
{ //扣车按钮
|
||||
deviceNum := reader.ReadBytes(2)
|
||||
fmt.Printf("%x\t", deviceNum)
|
||||
fmt.Println("【扣车按钮】")
|
||||
}
|
||||
{ //信号机
|
||||
deviceNum := reader.ReadBytes(2)
|
||||
fmt.Printf("%x\t", deviceNum)
|
||||
for i := 0; i < int(binary.BigEndian.Uint16(deviceNum)); i++ {
|
||||
fmt.Printf("%x ", reader.ReadBytes(3))
|
||||
}
|
||||
fmt.Println("【信号机】")
|
||||
}
|
||||
{ //计轴区段
|
||||
deviceNum := reader.ReadBytes(2)
|
||||
fmt.Printf("%x\t", deviceNum)
|
||||
for i := 0; i < int(binary.BigEndian.Uint16(deviceNum)); i++ {
|
||||
fmt.Printf("%x ", reader.ReadBytes(3))
|
||||
}
|
||||
fmt.Println("【计轴区段】")
|
||||
}
|
||||
{ //无人折返
|
||||
deviceNum := reader.ReadBytes(2)
|
||||
fmt.Printf("%x\t", deviceNum)
|
||||
fmt.Println("【无人折返】")
|
||||
}
|
||||
{ //防淹门
|
||||
deviceNum := reader.ReadBytes(2)
|
||||
fmt.Printf("%x\t", deviceNum)
|
||||
fmt.Println("【防淹门】")
|
||||
}
|
||||
{ //SPKS
|
||||
deviceNum := reader.ReadBytes(2)
|
||||
fmt.Printf("%x\t", deviceNum)
|
||||
for i := 0; i < int(binary.BigEndian.Uint16(deviceNum)); i++ {
|
||||
fmt.Printf("%x ", reader.ReadBytes(4))
|
||||
}
|
||||
fmt.Println("【SPKS】")
|
||||
}
|
||||
{ //车库门
|
||||
deviceNum := reader.ReadBytes(2)
|
||||
fmt.Printf("%x\t", deviceNum)
|
||||
fmt.Println("【车库门】")
|
||||
}
|
||||
{ //预留
|
||||
deviceNum := reader.ReadBytes(10)
|
||||
fmt.Printf("%x\t", deviceNum)
|
||||
fmt.Println("【预留】")
|
||||
}
|
||||
//CRC32
|
||||
fmt.Printf("%x【CRC32】\n", reader.ReadBytes(4))
|
||||
//{ //json
|
||||
// frame := &FromInterlockFrame{}
|
||||
// err = frame.Decode(data)
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
// marshal, err := json.Marshal(frame)
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
// fmt.Println(string(marshal))
|
||||
//}
|
||||
}
|
||||
|
||||
func TestToInterlockFrame_Decode(t *testing.T) {
|
||||
str := "583f5bdedc315871778a9d89080045000172f6950000801146ae3d0b78143d0b0a0d40d8413c015ef35583014f63013c0200030004010005010009010006000202eeaaaaaa000302eeaaaaaa000402eeaaaaaa000602eeaaaaaa000702eeaaaaaa000802eeaaaaaa00060002aa000003aa000004aa000006aa000007aa000008aa000000001b000806000901000a06000b06000c06000d06000e01000f06001006001106001206001306001401001c01001d06001e01001f06002006002106002206002306002406002506002606002701002801002b01001e000b00000c00000d00000e00000f00001000001100001200001300001400001500001600001700001800001900001a00002600002700002800002900002a00002b00002c00002d00002e00002f0000300000310000320000330000000000000c0005aaff0006aaff0007aaff0008aaff0009aaff000aaaff000baaff000caaff000daaff000eaaff000faaff0010aa0400000100000000000000000000000000000000a0313220"
|
||||
data, err := hex.DecodeString(str)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if data[0] != 0x83 {
|
||||
data = data[42:] //去掉wireshark抓包携带的额外字节
|
||||
}
|
||||
reader := myreader.NewReader(data)
|
||||
fmt.Printf("%x\t【报文类型】\n", reader.ReadByte())
|
||||
fmt.Printf("%x\t【报文长度】\n", reader.ReadBytes(2))
|
||||
fmt.Printf("%x\t【轨旁编号】\n", reader.ReadBytes(2))
|
||||
fmt.Printf("%x\t【联锁编号】\n", reader.ReadBytes(2))
|
||||
{ //道岔
|
||||
deviceNum := reader.ReadBytes(2)
|
||||
fmt.Printf("%x\t", deviceNum)
|
||||
for i := 0; i < int(binary.BigEndian.Uint16(deviceNum)); i++ {
|
||||
fmt.Printf("%x ", reader.ReadBytes(3))
|
||||
}
|
||||
fmt.Println("【道岔】")
|
||||
}
|
||||
{ //屏蔽门
|
||||
deviceNum := reader.ReadBytes(2)
|
||||
fmt.Printf("%x\t", deviceNum)
|
||||
for i := 0; i < int(binary.BigEndian.Uint16(deviceNum)); i++ {
|
||||
fmt.Printf("%x ", reader.ReadBytes(7))
|
||||
}
|
||||
fmt.Println("【屏蔽门】")
|
||||
}
|
||||
{ //紧急停车按钮
|
||||
deviceNum := reader.ReadBytes(2)
|
||||
fmt.Printf("%x\t", deviceNum)
|
||||
for i := 0; i < int(binary.BigEndian.Uint16(deviceNum)); i++ {
|
||||
fmt.Printf("%x ", reader.ReadBytes(4))
|
||||
}
|
||||
fmt.Println("【紧急停车按钮】")
|
||||
}
|
||||
{ //扣车按钮
|
||||
deviceNum := reader.ReadBytes(2)
|
||||
fmt.Printf("%x\t", deviceNum)
|
||||
fmt.Println("【扣车按钮】")
|
||||
}
|
||||
{ //信号机
|
||||
deviceNum := reader.ReadBytes(2)
|
||||
fmt.Printf("%x\t", deviceNum)
|
||||
for i := 0; i < int(binary.BigEndian.Uint16(deviceNum)); i++ {
|
||||
fmt.Printf("%x ", reader.ReadBytes(3))
|
||||
}
|
||||
fmt.Println("【信号机】")
|
||||
}
|
||||
{ //计轴区段
|
||||
deviceNum := reader.ReadBytes(2)
|
||||
fmt.Printf("%x\t", deviceNum)
|
||||
for i := 0; i < int(binary.BigEndian.Uint16(deviceNum)); i++ {
|
||||
fmt.Printf("%x ", reader.ReadBytes(3))
|
||||
}
|
||||
fmt.Println("【计轴区段】")
|
||||
}
|
||||
{ //无人折返
|
||||
deviceNum := reader.ReadBytes(2)
|
||||
fmt.Printf("%x\t", deviceNum)
|
||||
fmt.Println("【无人折返】")
|
||||
}
|
||||
{ //防淹门
|
||||
deviceNum := reader.ReadBytes(2)
|
||||
fmt.Printf("%x\t", deviceNum)
|
||||
fmt.Println("【防淹门】")
|
||||
}
|
||||
{ //SPKS
|
||||
deviceNum := reader.ReadBytes(2)
|
||||
fmt.Printf("%x\t", deviceNum)
|
||||
for i := 0; i < int(binary.BigEndian.Uint16(deviceNum)); i++ {
|
||||
fmt.Printf("%x ", reader.ReadBytes(4))
|
||||
}
|
||||
fmt.Println("【SPKS】")
|
||||
}
|
||||
//CRC32
|
||||
fmt.Printf("%x【CRC32】\n", reader.ReadBytes(reader.Len()))
|
||||
}
|
52
third_party/interlock/beijing11/repo.go
vendored
Normal file
52
third_party/interlock/beijing11/repo.go
vendored
Normal file
@ -0,0 +1,52 @@
|
||||
package beijing11
|
||||
|
||||
// StationDeviceIndexTable 联锁站设备索引表
|
||||
type StationDeviceIndexTable struct {
|
||||
//StationName string //地图数据中车站的Code属性
|
||||
//InterlockCode uint16 //通信数据中的“联锁编号”
|
||||
TurnoutMap map[uint16]*Row //key-联锁编号
|
||||
PsdMap map[uint16]*Row //key-联锁编号
|
||||
EsbMap map[uint16]*Row //key-联锁编号
|
||||
HoldTrainMap map[uint16]*Row //key-联锁编号
|
||||
SignalMap map[uint16]*Row //key-联锁编号
|
||||
AxleSectionMap map[uint16]*Row //key-联锁编号
|
||||
WrzfMap map[uint16]*Row //key-联锁编号
|
||||
FymMap map[uint16]*Row //key-联锁编号
|
||||
SpksMap map[uint16]*Row //key-联锁编号
|
||||
CkmMap map[uint16]*Row //key-联锁编号
|
||||
XcjMap map[uint16]*Row //key-联锁编号
|
||||
}
|
||||
|
||||
func NewStationDeviceIndexTable( /*stationName string, interlockIndex uint16*/ ) *StationDeviceIndexTable {
|
||||
return &StationDeviceIndexTable{
|
||||
//StationName: stationName,
|
||||
//InterlockCode: 0x3C00 + interlockIndex,
|
||||
TurnoutMap: make(map[uint16]*Row),
|
||||
PsdMap: make(map[uint16]*Row),
|
||||
EsbMap: make(map[uint16]*Row),
|
||||
HoldTrainMap: make(map[uint16]*Row),
|
||||
SignalMap: make(map[uint16]*Row),
|
||||
AxleSectionMap: make(map[uint16]*Row),
|
||||
WrzfMap: make(map[uint16]*Row),
|
||||
FymMap: make(map[uint16]*Row),
|
||||
SpksMap: make(map[uint16]*Row),
|
||||
CkmMap: make(map[uint16]*Row),
|
||||
XcjMap: make(map[uint16]*Row),
|
||||
}
|
||||
}
|
||||
|
||||
type Row struct {
|
||||
interlockCode uint16 //所属联锁站编号
|
||||
commonId uint32 //地图中设备的ID
|
||||
uid string //模型仓库中的设备模型ID
|
||||
index uint16 //联锁通信中的设备ID
|
||||
relateDeviceMap map[string]string //此设备关联的其它设备 key-查找设备所用的标识,val-uid
|
||||
}
|
||||
|
||||
const ( //屏蔽门相关的继电器
|
||||
POB = "POB"
|
||||
PCB = "PCB"
|
||||
DPB = "DPB"
|
||||
S = "S" //屏蔽门短编组开门继电器索引
|
||||
L = "L" //屏蔽门长编组开门继电器索引
|
||||
)
|
741
third_party/interlock/beijing11/service.go
vendored
Normal file
741
third_party/interlock/beijing11/service.go
vendored
Normal file
@ -0,0 +1,741 @@
|
||||
// Package beijing11 北京11号线联锁通信
|
||||
package beijing11
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"joylink.club/bj-rtsts-server/config"
|
||||
"joylink.club/bj-rtsts-server/dto/data_proto"
|
||||
"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"
|
||||
"joylink.club/rtsssimulation/component"
|
||||
"joylink.club/rtsssimulation/entity"
|
||||
"joylink.club/rtsssimulation/fi"
|
||||
"log/slog"
|
||||
"math"
|
||||
"net"
|
||||
"runtime/debug"
|
||||
"sort"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
logTag = "[北京11号线联锁通信]"
|
||||
privateLogger *slog.Logger
|
||||
loggerInit sync.Once
|
||||
)
|
||||
|
||||
var (
|
||||
mu = sync.Mutex{} //启动任务时使用,避免重复启动任务
|
||||
serviceContextMap = make(map[string]*serviceContext) //每个集中站的服务上下文,key-集中站code
|
||||
waysideCode uint16 = 0x6301
|
||||
)
|
||||
|
||||
type serviceContext struct {
|
||||
cancelFunc context.CancelFunc //用来结束各个协程的函数
|
||||
ioAddr *net.UDPAddr //向联锁发送数据的客户端
|
||||
server udp.UdpServer //接收联锁数据的服务端(同时也是向联锁发送数据的udp客户端)
|
||||
sim *memory.VerifySimulation //启动服务所使用的仿真
|
||||
iConfig config.InterlockConfig //启动服务使用的联锁配置
|
||||
deviceTable *StationDeviceIndexTable //联锁站的设备ID表,key-车站名
|
||||
toInterlockFrames []*ToInterlockFrame //发给联锁的消息,因为需要按车站划分范围、排序,故将数据提前整理好,仅替换设备状态
|
||||
state tpapi.ThirdPartyApiServiceState //服务状态(用于展示)
|
||||
}
|
||||
|
||||
func CollectAllServices() []tpapi.ThirdPartyApiService {
|
||||
var services []tpapi.ThirdPartyApiService
|
||||
for _, service := range serviceContextMap {
|
||||
services = append(services, service)
|
||||
}
|
||||
return services
|
||||
}
|
||||
|
||||
func (s *serviceContext) Type() state_proto.SimulationThirdPartyApiService_Type {
|
||||
return state_proto.SimulationThirdPartyApiService_CI
|
||||
}
|
||||
|
||||
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(interlockConfig config.InterlockConfig, simulation *memory.VerifySimulation) {
|
||||
if interlockConfig.Ip == "" || !interlockConfig.Open {
|
||||
return
|
||||
}
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
//制表
|
||||
table, toInterlockFrames := preprocessingData(simulation, interlockConfig.Code)
|
||||
if table == nil { //当前仿真内没有11号线联锁通信所需数据
|
||||
return
|
||||
}
|
||||
serviceCtx := serviceContextMap[interlockConfig.Code]
|
||||
if serviceCtx != nil {
|
||||
panic(fmt.Sprintf("%s重复启动联锁站[%s]通信服务", logTag, interlockConfig.Code))
|
||||
}
|
||||
//仿真IO地址
|
||||
ioAddr, err := net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%d", interlockConfig.Ip, interlockConfig.RemotePort))
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("%s解析IP地址出错:%s", logTag, err))
|
||||
}
|
||||
|
||||
ctx, cancelFunc := context.WithCancel(context.Background())
|
||||
serviceCtx = &serviceContext{
|
||||
cancelFunc: cancelFunc,
|
||||
ioAddr: ioAddr,
|
||||
sim: simulation,
|
||||
iConfig: interlockConfig,
|
||||
deviceTable: table,
|
||||
toInterlockFrames: toInterlockFrames,
|
||||
}
|
||||
//UDP服务端
|
||||
server := udp.NewServer(fmt.Sprintf(":%d", interlockConfig.LocalPort), serviceCtx.handleDriveMsg)
|
||||
serviceCtx.server = server
|
||||
err = server.Listen()
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("%s启动UDP服务失败:%s", logTag, err))
|
||||
}
|
||||
|
||||
serviceCtx.runCollectTask(ctx)
|
||||
serviceContextMap[interlockConfig.Code] = serviceCtx
|
||||
}
|
||||
|
||||
func Stop(stationCode string) {
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
serviceContext := serviceContextMap[stationCode]
|
||||
if serviceContext != nil {
|
||||
serviceContext.cancelFunc()
|
||||
if serviceContext.server != nil {
|
||||
serviceContext.server.Close()
|
||||
}
|
||||
delete(serviceContextMap, stationCode)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *serviceContext) handleDriveMsg(data []byte) {
|
||||
logger().Info(fmt.Sprintf("收到消息:%x", data))
|
||||
frame := &FromInterlockFrame{}
|
||||
err := frame.Decode(data)
|
||||
if err != nil {
|
||||
logger().Error("解析数据出错", "error", err)
|
||||
return
|
||||
}
|
||||
|
||||
wd := entity.GetWorldData(s.sim.World)
|
||||
for _, cmd := range frame.TurnoutData.CmdList {
|
||||
uid := s.deviceTable.TurnoutMap[cmd.Id].uid
|
||||
if cmd.Cmd == 0x01 {
|
||||
err = fi.DriveTurnoutDCOn(s.sim.World, uid)
|
||||
} else if cmd.Cmd == 0x02 {
|
||||
err = fi.DriveTurnoutFCOn(s.sim.World, uid)
|
||||
} else {
|
||||
err = fi.DriveTurnoutDCOff(s.sim.World, uid)
|
||||
}
|
||||
if err != nil {
|
||||
logger().Error("驱动道岔出错", "error", err)
|
||||
}
|
||||
}
|
||||
for _, cmd := range frame.PSDData.CmdList {
|
||||
row := s.deviceTable.PsdMap[cmd.Id]
|
||||
entry := wd.EntityMap[row.uid]
|
||||
circuit := component.PsdCircuitType.Get(entry)
|
||||
switch cmd.Cmd {
|
||||
case 0xAA: //短编组开门
|
||||
wd.SetQdBit(row.relateDeviceMap[S], true)
|
||||
wd.SetQdBit(row.relateDeviceMap[L], false)
|
||||
wd.SetQdBit(component.UidType.Get(circuit.GMJ).Id, false)
|
||||
case 0xBB: //长编组开门
|
||||
wd.SetQdBit(row.relateDeviceMap[S], false)
|
||||
wd.SetQdBit(row.relateDeviceMap[L], true)
|
||||
wd.SetQdBit(component.UidType.Get(circuit.GMJ).Id, false)
|
||||
case 0x55: //关门
|
||||
wd.SetQdBit(row.relateDeviceMap[S], false)
|
||||
wd.SetQdBit(row.relateDeviceMap[L], false)
|
||||
wd.SetQdBit(component.UidType.Get(circuit.GMJ).Id, true)
|
||||
}
|
||||
}
|
||||
for _, cmd := range frame.ESBData.CmdList {
|
||||
uid := s.deviceTable.EsbMap[cmd.Id].uid
|
||||
esb := s.sim.Repo.FindEsb(uid)
|
||||
if cmd.JjtcplCmd == 0x55 {
|
||||
err = fi.PressDownButton(s.sim.World, esb.PlaId())
|
||||
if err != nil {
|
||||
logger().Error("驱动ESB出错", "error", err)
|
||||
}
|
||||
continue
|
||||
} else {
|
||||
err = fi.PressUpButton(s.sim.World, esb.PlaId())
|
||||
if err != nil {
|
||||
logger().Error("驱动ESB出错", "error", err)
|
||||
}
|
||||
}
|
||||
if cmd.JjtcCmd == 0x55 {
|
||||
err := fi.DriveRelayDown(s.sim.World, esb.RelayId())
|
||||
if err != nil {
|
||||
logger().Error("驱动ESB出错", "error", err)
|
||||
}
|
||||
} else {
|
||||
err := fi.DriveRelayUp(s.sim.World, esb.RelayId())
|
||||
if err != nil {
|
||||
logger().Error("驱动ESB出错", "error", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, cmd := range frame.SignalData.CmdList {
|
||||
uid := s.deviceTable.SignalMap[cmd.Id].uid
|
||||
entry := wd.EntityMap[uid]
|
||||
if entry.HasComponent(component.Signal2XH1ElectronicType) { // 2XH1信号机
|
||||
signal2XH1 := component.Signal2XH1ElectronicType.Get(entry)
|
||||
switch cmd.Cmd {
|
||||
case SignalAspect_H:
|
||||
wd.SetQdBit(component.UidType.Get(signal2XH1.Z2XH1_DDJ).Id, false)
|
||||
wd.SetQdBit(component.UidType.Get(signal2XH1.Z2XH1_LXJ).Id, false)
|
||||
case SignalAspect_L:
|
||||
wd.SetQdBit(component.UidType.Get(signal2XH1.Z2XH1_DDJ).Id, false)
|
||||
wd.SetQdBit(component.UidType.Get(signal2XH1.Z2XH1_LXJ).Id, true)
|
||||
default:
|
||||
wd.SetQdBit(component.UidType.Get(signal2XH1.Z2XH1_DDJ).Id, true)
|
||||
wd.SetQdBit(component.UidType.Get(signal2XH1.Z2XH1_LXJ).Id, false)
|
||||
}
|
||||
} else if entry.HasComponent(component.Signal3XH1ElectronicType) { // 3XH1信号机
|
||||
signal3XH1 := component.Signal3XH1ElectronicType.Get(entry)
|
||||
switch cmd.Cmd {
|
||||
case SignalAspect_H:
|
||||
wd.SetQdBit(component.UidType.Get(signal3XH1.Z3XH1_DDJ).Id, false)
|
||||
wd.SetQdBit(component.UidType.Get(signal3XH1.Z3XH1_LXJ).Id, false)
|
||||
wd.SetQdBit(component.UidType.Get(signal3XH1.Z3XH1_ZXJ).Id, false)
|
||||
wd.SetQdBit(component.UidType.Get(signal3XH1.Z3XH1_YXJ).Id, false)
|
||||
case SignalAspect_L:
|
||||
wd.SetQdBit(component.UidType.Get(signal3XH1.Z3XH1_DDJ).Id, false)
|
||||
wd.SetQdBit(component.UidType.Get(signal3XH1.Z3XH1_LXJ).Id, true)
|
||||
wd.SetQdBit(component.UidType.Get(signal3XH1.Z3XH1_ZXJ).Id, true)
|
||||
wd.SetQdBit(component.UidType.Get(signal3XH1.Z3XH1_YXJ).Id, false)
|
||||
case SignalAspect_U:
|
||||
wd.SetQdBit(component.UidType.Get(signal3XH1.Z3XH1_DDJ).Id, false)
|
||||
wd.SetQdBit(component.UidType.Get(signal3XH1.Z3XH1_LXJ).Id, true)
|
||||
wd.SetQdBit(component.UidType.Get(signal3XH1.Z3XH1_ZXJ).Id, false)
|
||||
wd.SetQdBit(component.UidType.Get(signal3XH1.Z3XH1_YXJ).Id, false)
|
||||
case SignalAspect_HU:
|
||||
wd.SetQdBit(component.UidType.Get(signal3XH1.Z3XH1_DDJ).Id, false)
|
||||
wd.SetQdBit(component.UidType.Get(signal3XH1.Z3XH1_LXJ).Id, false)
|
||||
wd.SetQdBit(component.UidType.Get(signal3XH1.Z3XH1_ZXJ).Id, false)
|
||||
wd.SetQdBit(component.UidType.Get(signal3XH1.Z3XH1_YXJ).Id, true)
|
||||
default:
|
||||
wd.SetQdBit(component.UidType.Get(signal3XH1.Z3XH1_DDJ).Id, true)
|
||||
wd.SetQdBit(component.UidType.Get(signal3XH1.Z3XH1_LXJ).Id, false)
|
||||
wd.SetQdBit(component.UidType.Get(signal3XH1.Z3XH1_ZXJ).Id, false)
|
||||
wd.SetQdBit(component.UidType.Get(signal3XH1.Z3XH1_YXJ).Id, false)
|
||||
}
|
||||
} else if entry.HasComponent(component.Signal3XH2ElectronicType) { // 3XH2信号机
|
||||
signal3XH2 := component.Signal3XH2ElectronicType.Get(entry)
|
||||
switch cmd.Cmd {
|
||||
case SignalAspect_H:
|
||||
wd.SetQdBit(component.UidType.Get(signal3XH2.Z3XH2_DDJ).Id, false)
|
||||
wd.SetQdBit(component.UidType.Get(signal3XH2.Z3XH2_LXJ).Id, false)
|
||||
wd.SetQdBit(component.UidType.Get(signal3XH2.Z3XH2_YXJ).Id, false)
|
||||
case SignalAspect_L:
|
||||
wd.SetQdBit(component.UidType.Get(signal3XH2.Z3XH2_DDJ).Id, false)
|
||||
wd.SetQdBit(component.UidType.Get(signal3XH2.Z3XH2_LXJ).Id, true)
|
||||
wd.SetQdBit(component.UidType.Get(signal3XH2.Z3XH2_YXJ).Id, false)
|
||||
case SignalAspect_HU:
|
||||
wd.SetQdBit(component.UidType.Get(signal3XH2.Z3XH2_DDJ).Id, false)
|
||||
wd.SetQdBit(component.UidType.Get(signal3XH2.Z3XH2_LXJ).Id, false)
|
||||
wd.SetQdBit(component.UidType.Get(signal3XH2.Z3XH2_YXJ).Id, true)
|
||||
default:
|
||||
wd.SetQdBit(component.UidType.Get(signal3XH2.Z3XH2_DDJ).Id, true)
|
||||
wd.SetQdBit(component.UidType.Get(signal3XH2.Z3XH2_LXJ).Id, false)
|
||||
wd.SetQdBit(component.UidType.Get(signal3XH2.Z3XH2_YXJ).Id, false)
|
||||
}
|
||||
} else if entry.HasComponent(component.Signal3XH3ElectronicType) { // 3XH3信号机
|
||||
signal3XH3 := component.Signal3XH3ElectronicType.Get(entry)
|
||||
switch cmd.Cmd {
|
||||
case SignalAspect_H:
|
||||
wd.SetQdBit(component.UidType.Get(signal3XH3.Z3XH3_DDJ).Id, false)
|
||||
wd.SetQdBit(component.UidType.Get(signal3XH3.Z3XH3_LXJ).Id, false)
|
||||
wd.SetQdBit(component.UidType.Get(signal3XH3.Z3XH3_YXJ).Id, false)
|
||||
case SignalAspect_U:
|
||||
wd.SetQdBit(component.UidType.Get(signal3XH3.Z3XH3_DDJ).Id, false)
|
||||
wd.SetQdBit(component.UidType.Get(signal3XH3.Z3XH3_LXJ).Id, true)
|
||||
wd.SetQdBit(component.UidType.Get(signal3XH3.Z3XH3_YXJ).Id, false)
|
||||
case SignalAspect_HU:
|
||||
wd.SetQdBit(component.UidType.Get(signal3XH3.Z3XH3_DDJ).Id, false)
|
||||
wd.SetQdBit(component.UidType.Get(signal3XH3.Z3XH3_LXJ).Id, false)
|
||||
wd.SetQdBit(component.UidType.Get(signal3XH3.Z3XH3_YXJ).Id, true)
|
||||
default:
|
||||
wd.SetQdBit(component.UidType.Get(signal3XH3.Z3XH3_DDJ).Id, true)
|
||||
wd.SetQdBit(component.UidType.Get(signal3XH3.Z3XH3_LXJ).Id, false)
|
||||
wd.SetQdBit(component.UidType.Get(signal3XH3.Z3XH3_YXJ).Id, false)
|
||||
}
|
||||
} else if entry.HasComponent(component.Signal3XH4ElectronicType) { // 3XH4信号机
|
||||
signal3XH4 := component.Signal3XH4ElectronicType.Get(entry)
|
||||
switch cmd.Cmd {
|
||||
case SignalAspect_H:
|
||||
wd.SetQdBit(component.UidType.Get(signal3XH4.Z3XH4_DDJ).Id, false)
|
||||
wd.SetQdBit(component.UidType.Get(signal3XH4.Z3XH4_LXJ).Id, false)
|
||||
wd.SetQdBit(component.UidType.Get(signal3XH4.Z3XH4_ZXJ).Id, false)
|
||||
case SignalAspect_L:
|
||||
wd.SetQdBit(component.UidType.Get(signal3XH4.Z3XH4_DDJ).Id, false)
|
||||
wd.SetQdBit(component.UidType.Get(signal3XH4.Z3XH4_LXJ).Id, true)
|
||||
wd.SetQdBit(component.UidType.Get(signal3XH4.Z3XH4_ZXJ).Id, true)
|
||||
case SignalAspect_U:
|
||||
wd.SetQdBit(component.UidType.Get(signal3XH4.Z3XH4_DDJ).Id, false)
|
||||
wd.SetQdBit(component.UidType.Get(signal3XH4.Z3XH4_LXJ).Id, true)
|
||||
wd.SetQdBit(component.UidType.Get(signal3XH4.Z3XH4_ZXJ).Id, false)
|
||||
default:
|
||||
wd.SetQdBit(component.UidType.Get(signal3XH4.Z3XH4_DDJ).Id, true)
|
||||
wd.SetQdBit(component.UidType.Get(signal3XH4.Z3XH4_LXJ).Id, false)
|
||||
wd.SetQdBit(component.UidType.Get(signal3XH4.Z3XH4_ZXJ).Id, false)
|
||||
}
|
||||
|
||||
} else {
|
||||
logger().Error(fmt.Sprintf("信号机[%s]的型号未知", uid))
|
||||
}
|
||||
}
|
||||
//{ //采集状态赋值
|
||||
// signalAspectMap := make(map[uint16]byte)
|
||||
// for _, cmd := range frame.SignalData.CmdList {
|
||||
// signalAspectMap[cmd.Id] = cmd.Cmd
|
||||
// }
|
||||
// for _, interlockFrame := range s.toInterlockFrames {
|
||||
// for _, state := range interlockFrame.SignalStates {
|
||||
// b := signalAspectMap[state.Id]
|
||||
// if b != 0 {
|
||||
// state.State = b
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
for _, cmd := range frame.AxleSectionData.CmdList {
|
||||
if cmd.Cmd == 0x80 {
|
||||
uid := s.deviceTable.AxleSectionMap[cmd.Id].uid
|
||||
physicalSectionUid := s.sim.Repo.FindAxleCountingSection(uid).PhysicalSection().Id()
|
||||
_, err := fi.PhysicalSectionDrstDrive(s.sim.World, physicalSectionUid)
|
||||
if err != nil {
|
||||
logger().Error(fmt.Sprintf("计轴区段[%s]复位出错:%s", uid, err))
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, cmd := range frame.SPKSData.CmdList {
|
||||
uid := s.deviceTable.SpksMap[cmd.Id].uid
|
||||
spks := s.sim.Repo.FindSpks(uid)
|
||||
if cmd.SPKSPLCmd == 0x55 {
|
||||
err = fi.PressDownButton(s.sim.World, spks.PlaId())
|
||||
} else {
|
||||
err = fi.PressUpButton(s.sim.World, spks.PlaId())
|
||||
}
|
||||
if cmd.SPKSCmd == 0x55 {
|
||||
err = fi.DriveRelayDown(s.sim.World, spks.Relay())
|
||||
} else {
|
||||
err = fi.DriveRelayUp(s.sim.World, spks.Relay())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func preprocessingData(sim *memory.VerifySimulation, stationCode string) (*StationDeviceIndexTable, []*ToInterlockFrame) {
|
||||
for _, mapId := range sim.MapIds {
|
||||
giType := memory.QueryGiType(mapId)
|
||||
if giType != data_proto.PictureType_StationLayout {
|
||||
continue
|
||||
}
|
||||
stationGi := memory.QueryGiData[*data_proto.RtssGraphicStorage](mapId)
|
||||
if stationGi.LianSuoData == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
frameMap := make(map[uint16]*ToInterlockFrame)
|
||||
frameSlice := make([]*ToInterlockFrame, 0, 2)
|
||||
getFrame := func(interlockCode uint16) *ToInterlockFrame {
|
||||
frame := frameMap[interlockCode]
|
||||
if frame == nil {
|
||||
frame = &ToInterlockFrame{InterlockCode: interlockCode, WaysideCode: waysideCode}
|
||||
frameMap[interlockCode] = frame
|
||||
frameSlice = append(frameSlice, frame)
|
||||
}
|
||||
return frame
|
||||
}
|
||||
|
||||
uids := memory.QueryUidStructure[*memory.StationUidStructure](mapId)
|
||||
table := NewStationDeviceIndexTable()
|
||||
stationMap := make(map[uint32]uint16)
|
||||
for _, station := range stationGi.LianSuoData.Stations {
|
||||
stationMap[station.Id] = uint16(0x3c00 + station.Index)
|
||||
}
|
||||
//道岔
|
||||
for _, data := range stationGi.LianSuoData.Switchs {
|
||||
if data.Index <= 0 {
|
||||
continue
|
||||
}
|
||||
for _, station := range uids.TurnoutIds[data.Id].CentralizedStations {
|
||||
interlockCode := stationMap[station.Common.Id]
|
||||
if interlockCode != 0 {
|
||||
frame := getFrame(interlockCode)
|
||||
frame.TurnoutStates = append(frame.TurnoutStates, &TurnoutState{Id: uint16(data.Index)})
|
||||
table.TurnoutMap[uint16(data.Index)] = &Row{
|
||||
interlockCode: interlockCode,
|
||||
commonId: data.Id,
|
||||
uid: uids.TurnoutIds[data.Id].Uid,
|
||||
index: uint16(data.Index),
|
||||
relateDeviceMap: nil,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//屏蔽门
|
||||
for _, data := range stationGi.LianSuoData.ScreenDoors {
|
||||
if data.Index <= 0 {
|
||||
continue
|
||||
}
|
||||
relateDeviceMap := make(map[string]string)
|
||||
wd := entity.GetWorldData(sim.World)
|
||||
entry := wd.EntityMap[uids.PsdIds[data.Id].Uid]
|
||||
circuit := component.PsdCircuitType.Get(entry)
|
||||
var s, l int32 = math.MaxInt32, 0
|
||||
var sRelayId, lRelayId string
|
||||
for i, relay := range circuit.KMJMap {
|
||||
if i <= s {
|
||||
s = i
|
||||
sRelayId = component.UidType.Get(relay).Id
|
||||
}
|
||||
if i >= l {
|
||||
l = i
|
||||
lRelayId = component.UidType.Get(relay).Id
|
||||
}
|
||||
}
|
||||
relateDeviceMap[S] = sRelayId
|
||||
relateDeviceMap[L] = lRelayId
|
||||
for _, station := range uids.PsdIds[data.Id].CentralizedStations {
|
||||
interlockCode := stationMap[station.Common.Id]
|
||||
if interlockCode != 0 {
|
||||
frame := getFrame(interlockCode)
|
||||
frame.PsdStates = append(frame.PsdStates, &PSDState{Id: uint16(data.Index)})
|
||||
for _, mkx := range sim.Repo.MkxList() {
|
||||
if mkx.Psd().Id() == uids.PsdIds[data.Id].Uid {
|
||||
relateDeviceMap[POB] = mkx.Pobj().Id()
|
||||
relateDeviceMap[PCB] = mkx.Pcbj().Id()
|
||||
relateDeviceMap[DPB] = mkx.Pabj().Id()
|
||||
}
|
||||
}
|
||||
table.PsdMap[uint16(data.Index)] = &Row{
|
||||
interlockCode: interlockCode,
|
||||
commonId: data.Id,
|
||||
uid: uids.PsdIds[data.Id].Uid,
|
||||
index: uint16(data.Index),
|
||||
relateDeviceMap: relateDeviceMap,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//紧急停车
|
||||
for _, data := range stationGi.LianSuoData.EsbButtons {
|
||||
if data.Index <= 0 {
|
||||
continue
|
||||
}
|
||||
for _, station := range uids.EsbIds[data.Id].CentralizedStations {
|
||||
interlockCode := stationMap[station.Common.Id]
|
||||
if interlockCode != 0 {
|
||||
frame := getFrame(interlockCode)
|
||||
frame.ESBStates = append(frame.ESBStates, &ESBState{Id: uint16(data.Index)})
|
||||
table.EsbMap[uint16(data.Index)] = &Row{
|
||||
interlockCode: interlockCode,
|
||||
commonId: data.Id,
|
||||
uid: uids.EsbIds[data.Id].Uid,
|
||||
index: uint16(data.Index),
|
||||
relateDeviceMap: nil,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//扣车,实际数据中数量为0
|
||||
//信号机
|
||||
for _, data := range stationGi.LianSuoData.Signals {
|
||||
if data.Index <= 0 {
|
||||
continue
|
||||
}
|
||||
for _, station := range uids.SignalIds[data.Id].CentralizedStations {
|
||||
interlockCode := stationMap[station.Common.Id]
|
||||
if interlockCode != 0 {
|
||||
frame := getFrame(interlockCode)
|
||||
frame.SignalStates = append(frame.SignalStates, &SignalState{Id: uint16(data.Index)})
|
||||
table.SignalMap[uint16(data.Index)] = &Row{
|
||||
interlockCode: interlockCode,
|
||||
commonId: data.Id,
|
||||
uid: uids.SignalIds[data.Id].Uid,
|
||||
index: uint16(data.Index),
|
||||
relateDeviceMap: nil,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
{ //临时补充一个信号机
|
||||
table.SignalMap[40] = &Row{
|
||||
interlockCode: 0x3c02,
|
||||
commonId: 0,
|
||||
uid: "",
|
||||
index: 40,
|
||||
relateDeviceMap: nil,
|
||||
}
|
||||
frame := getFrame(0x3c02)
|
||||
frame.SignalStates = append(frame.SignalStates, &SignalState{Id: 40})
|
||||
}
|
||||
//计轴区段
|
||||
for _, data := range stationGi.LianSuoData.AcSections {
|
||||
if data.Index <= 0 {
|
||||
continue
|
||||
}
|
||||
sectionModule := sim.Repo.FindAxleCountingSection(uids.AxleCountingSectionIds[data.Id].Uid)
|
||||
for _, station := range sectionModule.PhysicalSection().CentralizedStation() {
|
||||
//目前没发现一个计轴区段属于两个联锁区的情况,暂不处理
|
||||
interlockCode := stationMap[sim.UidMap[station.Id()].CommonId]
|
||||
if interlockCode != 0 {
|
||||
frame := getFrame(interlockCode)
|
||||
frame.AxleSectionStates = append(frame.AxleSectionStates, &AxleSectionState{Id: uint16(data.Index)})
|
||||
table.AxleSectionMap[uint16(data.Index)] = &Row{
|
||||
interlockCode: interlockCode,
|
||||
commonId: data.Id,
|
||||
uid: uids.AxleCountingSectionIds[data.Id].Uid,
|
||||
index: uint16(data.Index),
|
||||
relateDeviceMap: nil,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//无人折返,实际数据中数量为0
|
||||
//防淹门,实际数据中数量为0
|
||||
//人员防护
|
||||
for _, data := range stationGi.LianSuoData.SpksSwitchs {
|
||||
if data.Index <= 0 {
|
||||
continue
|
||||
}
|
||||
for _, station := range uids.SpksIds[data.Id].CentralizedStations {
|
||||
interlockCode := stationMap[station.Common.Id]
|
||||
if interlockCode != 0 {
|
||||
frame := getFrame(interlockCode)
|
||||
frame.SPKSStates = append(frame.SPKSStates, &SPKSState{Id: uint16(data.Index)})
|
||||
table.SpksMap[uint16(data.Index)] = &Row{
|
||||
interlockCode: interlockCode,
|
||||
commonId: data.Id,
|
||||
uid: uids.SpksIds[data.Id].Uid,
|
||||
index: uint16(data.Index),
|
||||
relateDeviceMap: nil,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//车库门,实际数据中数量为0
|
||||
//洗车机,实际数据中没有
|
||||
//按设备id大小排序
|
||||
for _, frame := range frameSlice {
|
||||
sort.Slice(frame.TurnoutStates, func(i, j int) bool {
|
||||
return frame.TurnoutStates[i].Id < frame.TurnoutStates[j].Id
|
||||
})
|
||||
sort.Slice(frame.PsdStates, func(i, j int) bool {
|
||||
return frame.PsdStates[i].Id < frame.PsdStates[j].Id
|
||||
})
|
||||
sort.Slice(frame.ESBStates, func(i, j int) bool {
|
||||
return frame.ESBStates[i].Id < frame.ESBStates[j].Id
|
||||
})
|
||||
sort.Slice(frame.SignalStates, func(i, j int) bool {
|
||||
return frame.SignalStates[i].Id < frame.SignalStates[j].Id
|
||||
})
|
||||
sort.Slice(frame.AxleSectionStates, func(i, j int) bool {
|
||||
return frame.AxleSectionStates[i].Id < frame.AxleSectionStates[j].Id
|
||||
})
|
||||
sort.Slice(frame.SPKSStates, func(i, j int) bool {
|
||||
return frame.SPKSStates[i].Id < frame.SPKSStates[j].Id
|
||||
})
|
||||
}
|
||||
|
||||
return table, frameSlice
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (s *serviceContext) runCollectTask(ctx context.Context) {
|
||||
go func() {
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
logger().Error("状态收集任务出错,记录后重启", "error", err, "stack", string(debug.Stack()))
|
||||
s.runCollectTask(ctx)
|
||||
}
|
||||
}()
|
||||
for range time.Tick(time.Duration(s.iConfig.Period) * time.Millisecond) {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
default:
|
||||
s.collectDeviceState()
|
||||
for _, frame := range s.toInterlockFrames {
|
||||
data := frame.encode()
|
||||
_, err := s.server.WriteToUdp(data, s.ioAddr)
|
||||
if err != nil {
|
||||
logger().Error("向联锁发送数据失败", "error", err)
|
||||
s.state = tpapi.ThirdPartyState_Broken
|
||||
} else {
|
||||
logger().Info(fmt.Sprintf("向联锁发送数据:%x", data))
|
||||
s.state = tpapi.ThirdPartyState_Broken
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func (s *serviceContext) collectDeviceState() {
|
||||
wd := entity.GetWorldData(s.sim.World)
|
||||
for _, frame := range s.toInterlockFrames {
|
||||
//道岔
|
||||
for _, state := range frame.TurnoutStates {
|
||||
row := s.deviceTable.TurnoutMap[state.Id]
|
||||
entry := wd.EntityMap[row.uid]
|
||||
tp := component.TurnoutPositionType.Get(entry)
|
||||
var stateByte byte
|
||||
if entry.HasComponent(component.TurnoutFaultCiqdType) {
|
||||
stateByte = 0xff
|
||||
} else {
|
||||
if tp.Dw {
|
||||
stateByte = 0x01
|
||||
} else if tp.Fw {
|
||||
stateByte = 0x02
|
||||
} else {
|
||||
stateByte = 0x08
|
||||
}
|
||||
}
|
||||
|
||||
state.State = stateByte
|
||||
}
|
||||
//屏蔽门
|
||||
for _, state := range frame.PsdStates {
|
||||
row := s.deviceTable.PsdMap[state.Id]
|
||||
entry := wd.EntityMap[row.uid]
|
||||
psdState := component.PsdStateType.Get(entry)
|
||||
mkxBytes := make([]byte, 0, 3)
|
||||
for _, mkxRelayUid := range []string{row.relateDeviceMap[POB], row.relateDeviceMap[PCB], row.relateDeviceMap[DPB]} {
|
||||
if mkxRelayUid == "" {
|
||||
continue
|
||||
}
|
||||
mkxBytes = append(mkxBytes, GetStateByte(component.BitStateType.Get(wd.EntityMap[mkxRelayUid]).Val))
|
||||
}
|
||||
var stateByte byte
|
||||
if psdState.Close {
|
||||
stateByte = 0x02
|
||||
} else {
|
||||
stateByte = 0x01
|
||||
}
|
||||
var hsjc byte
|
||||
if psdState.InterlockRelease {
|
||||
hsjc = 0xff
|
||||
} else {
|
||||
hsjc = 0xee
|
||||
}
|
||||
|
||||
state.State = stateByte
|
||||
state.Hsjc = hsjc
|
||||
state.PCB = mkxBytes[0]
|
||||
state.POB = mkxBytes[1]
|
||||
state.DPB = mkxBytes[2]
|
||||
}
|
||||
//紧急停车
|
||||
for _, state := range frame.ESBStates {
|
||||
row := s.deviceTable.EsbMap[state.Id]
|
||||
esb := s.sim.Repo.FindEsb(row.uid)
|
||||
relay := wd.EntityMap[esb.RelayId()]
|
||||
pla := wd.EntityMap[esb.PlaId()]
|
||||
|
||||
state.State = GetStateByte(!component.BitStateType.Get(relay).Val)
|
||||
state.PlState = GetStateByte(component.BitStateType.Get(pla).Val)
|
||||
}
|
||||
//信号机
|
||||
for _, state := range frame.SignalStates {
|
||||
row := s.deviceTable.SignalMap[state.Id]
|
||||
entry := wd.EntityMap[row.uid]
|
||||
if entry == nil {
|
||||
continue
|
||||
}
|
||||
lights := component.SignalLightsType.Get(entry)
|
||||
isL := false
|
||||
isH := false
|
||||
isU := false
|
||||
isA := false
|
||||
isB := false
|
||||
for _, light := range lights.Lights {
|
||||
switch {
|
||||
case light.HasComponent(component.LdTag):
|
||||
isL = component.BitStateType.Get(light).Val
|
||||
case light.HasComponent(component.HdTag):
|
||||
isH = component.BitStateType.Get(light).Val
|
||||
case light.HasComponent(component.UdTag):
|
||||
isU = component.BitStateType.Get(light).Val
|
||||
case light.HasComponent(component.BdTag):
|
||||
isB = component.BitStateType.Get(light).Val
|
||||
case light.HasComponent(component.AdTag):
|
||||
isA = component.BitStateType.Get(light).Val
|
||||
}
|
||||
}
|
||||
var stateByte byte
|
||||
if isH && isU {
|
||||
stateByte = 0x03
|
||||
} else {
|
||||
switch {
|
||||
case isL:
|
||||
stateByte = 0x04
|
||||
case isH:
|
||||
stateByte = 0x01
|
||||
case isU:
|
||||
stateByte = 0x02
|
||||
case isB:
|
||||
stateByte = 0x08
|
||||
case isA:
|
||||
stateByte = 0x09
|
||||
default:
|
||||
stateByte = 0x06
|
||||
}
|
||||
}
|
||||
state.State = stateByte
|
||||
}
|
||||
//计轴区段
|
||||
for _, state := range frame.AxleSectionStates {
|
||||
row := s.deviceTable.AxleSectionMap[state.Id]
|
||||
entry := wd.EntityMap[row.uid]
|
||||
sectionState := component.AxleCountingSectionStateType.Get(entry)
|
||||
var stateByte byte = 0x00
|
||||
if sectionState.Occupied {
|
||||
stateByte = 0x40
|
||||
}
|
||||
|
||||
state.State = stateByte
|
||||
}
|
||||
//SPKS
|
||||
for _, state := range frame.SPKSStates {
|
||||
row := s.deviceTable.SpksMap[state.Id]
|
||||
spks := s.sim.Repo.FindSpks(row.uid)
|
||||
relay := wd.EntityMap[spks.Relay()]
|
||||
pla := wd.EntityMap[spks.PlaId()]
|
||||
|
||||
state.State = GetStateByte(!component.BitStateType.Get(relay).Val)
|
||||
state.PlState = GetStateByte(component.BitStateType.Get(pla).Val)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func logger() *slog.Logger {
|
||||
loggerInit.Do(func() {
|
||||
privateLogger = slog.Default().With("tag", logTag)
|
||||
})
|
||||
return privateLogger
|
||||
}
|
267
third_party/interlock/beijing12/interlock.go
vendored
Normal file
267
third_party/interlock/beijing12/interlock.go
vendored
Normal file
@ -0,0 +1,267 @@
|
||||
// Package beijing12 北京12号线联锁通信
|
||||
package beijing12
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"joylink.club/bj-rtsts-server/dto/data_proto"
|
||||
"joylink.club/bj-rtsts-server/dto/state_proto"
|
||||
"joylink.club/bj-rtsts-server/sys_error"
|
||||
"joylink.club/bj-rtsts-server/third_party/balisecodec"
|
||||
"joylink.club/bj-rtsts-server/third_party/tcp"
|
||||
"joylink.club/bj-rtsts-server/third_party/tpapi"
|
||||
"joylink.club/bj-rtsts-server/ts/simulation/wayside/memory"
|
||||
"joylink.club/rtsssimulation/fi"
|
||||
"joylink.club/rtsssimulation/repository"
|
||||
"log/slog"
|
||||
"runtime/debug"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"joylink.club/bj-rtsts-server/config"
|
||||
"joylink.club/bj-rtsts-server/third_party/message"
|
||||
)
|
||||
|
||||
var ( //日志
|
||||
logTag = "[北京12号线联锁通信]"
|
||||
privateLogger *slog.Logger
|
||||
loggerInit sync.Once
|
||||
)
|
||||
|
||||
// 联锁代理通信接口
|
||||
type InterlockMessageManager interface {
|
||||
CollectInterlockRelayInfo(code string) *message.InterlockSendMsgPkg
|
||||
HandleInterlockDriverInfo(code string, b []byte)
|
||||
}
|
||||
|
||||
var interlockMap = make(map[string]*interlockProxy)
|
||||
var initMutex sync.Mutex
|
||||
|
||||
func Start(c config.InterlockConfig, simulation *memory.VerifySimulation, baliseList []*repository.Transponder) {
|
||||
initMutex.Lock()
|
||||
defer initMutex.Unlock()
|
||||
proxy := &interlockProxy{
|
||||
runConfig: c,
|
||||
indexBalise: make(map[byte]map[byte]*repository.Transponder),
|
||||
fromCIBytes: make([]byte, 0, fromCIBytesLen),
|
||||
}
|
||||
//构建应答器的索引映射
|
||||
for _, mapId := range simulation.MapIds {
|
||||
giType := memory.QueryGiType(mapId)
|
||||
if giType != data_proto.PictureType_StationLayout {
|
||||
continue
|
||||
}
|
||||
uids := memory.QueryUidStructure[*memory.StationUidStructure](mapId)
|
||||
for _, baliseRelationship := range uids.TransponderIds {
|
||||
balise := simulation.Repo.FindTransponder(baliseRelationship.Uid)
|
||||
if balise.LeuIndex() == 0 {
|
||||
continue
|
||||
}
|
||||
for _, station := range baliseRelationship.CentralizedStations {
|
||||
if station.StationName != c.Code {
|
||||
continue
|
||||
}
|
||||
leuIndex := byte(balise.LeuIndex())
|
||||
if proxy.indexBalise[leuIndex] == nil {
|
||||
proxy.indexBalise[leuIndex] = make(map[byte]*repository.Transponder)
|
||||
}
|
||||
proxy.indexBalise[leuIndex][byte(balise.IndexInLeu())] = balise
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
proxy.Start(simulation)
|
||||
interlockMap[c.Code] = proxy
|
||||
}
|
||||
|
||||
func Stop(c config.InterlockConfig) {
|
||||
proxy := interlockMap[c.Code]
|
||||
if proxy != nil {
|
||||
proxy.Stop()
|
||||
}
|
||||
}
|
||||
|
||||
type interlockProxy struct {
|
||||
tcpClient *tcp.TcpClient
|
||||
|
||||
simulation *memory.VerifySimulation
|
||||
collectInfoTaskCancel context.CancelFunc
|
||||
runConfig config.InterlockConfig
|
||||
|
||||
// 按索引存储的应答器,leu索引-leu内索引
|
||||
indexBalise map[byte]map[byte]*repository.Transponder
|
||||
// 联锁发送的数据过长,多个包才能凑成完成报文
|
||||
fromCIBytes []byte
|
||||
// 序列号
|
||||
serialNumber uint8
|
||||
|
||||
state tpapi.ThirdPartyApiServiceState // 服务状态(用于展示)
|
||||
}
|
||||
|
||||
func CollectAllServices() []tpapi.ThirdPartyApiService {
|
||||
var services []tpapi.ThirdPartyApiService
|
||||
for _, service := range interlockMap {
|
||||
services = append(services, service)
|
||||
}
|
||||
return services
|
||||
}
|
||||
|
||||
func (i *interlockProxy) Type() state_proto.SimulationThirdPartyApiService_Type {
|
||||
return state_proto.SimulationThirdPartyApiService_CI
|
||||
}
|
||||
|
||||
func (i *interlockProxy) State() tpapi.ThirdPartyApiServiceState {
|
||||
return i.state
|
||||
}
|
||||
|
||||
func (i *interlockProxy) FindAppendApiService() []tpapi.ThirdPartyApiService {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i *interlockProxy) TrueService() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (i *interlockProxy) ServiceDesc() string {
|
||||
return logTag
|
||||
}
|
||||
|
||||
func (i *interlockProxy) Start(simulation *memory.VerifySimulation) {
|
||||
if i.runConfig.Ip == "" || !i.runConfig.Open {
|
||||
return
|
||||
}
|
||||
if simulation == nil {
|
||||
panic(fmt.Sprintf("%s启动联锁消息服务错误: InterlockMessageManager不能为nil", logTag))
|
||||
}
|
||||
if i.simulation != nil {
|
||||
panic(fmt.Sprintf("%s启动联锁消息服务错误: 存在正在运行的任务", logTag))
|
||||
}
|
||||
i.initInterlockProxy()
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
go i.collectInfoStateTask(ctx)
|
||||
i.collectInfoTaskCancel = cancel
|
||||
i.simulation = simulation
|
||||
}
|
||||
|
||||
// 定时发送采集电路状态任务
|
||||
func (i *interlockProxy) collectInfoStateTask(ctx context.Context) {
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
logger().Error(logTag+"定时发送采集状态任务异常", "error", err, "stack", string(debug.Stack()))
|
||||
}
|
||||
}()
|
||||
for range time.Tick(time.Duration(i.runConfig.Period) * time.Millisecond) {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
default:
|
||||
}
|
||||
collectInfoState := i.simulation.CollectInterlockRelayInfo(i.runConfig.Code)
|
||||
if collectInfoState != nil {
|
||||
i.serialNumber++
|
||||
collectInfoState.SetSerialNumber(i.serialNumber)
|
||||
err := i.tcpClient.Send(collectInfoState.Encode())
|
||||
if err != nil {
|
||||
logger().Error(fmt.Sprintf("向联锁发送继电器状态失败:%s", err))
|
||||
i.state = tpapi.ThirdPartyState_Broken
|
||||
} else {
|
||||
i.state = tpapi.ThirdPartyState_Normal
|
||||
//logger().Info(fmt.Sprintf("向联锁发送继电器数据成功:%x", collectInfoState.Encode()))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (i *interlockProxy) Stop() {
|
||||
initMutex.Lock()
|
||||
defer initMutex.Unlock()
|
||||
delete(interlockMap, i.runConfig.Code)
|
||||
i.collectInfoTaskCancel()
|
||||
if i.tcpClient != nil {
|
||||
i.tcpClient.Close()
|
||||
}
|
||||
if i.collectInfoTaskCancel != nil {
|
||||
i.collectInfoTaskCancel()
|
||||
}
|
||||
i.simulation = nil
|
||||
}
|
||||
|
||||
func (i *interlockProxy) initInterlockProxy() {
|
||||
client, err := tcp.StartTcpClient(fmt.Sprintf("%s:%d", i.runConfig.Ip, i.runConfig.RemotePort), i.handleFromCiData, func(err error) {
|
||||
logger().Error(fmt.Sprintf("TCP客户端读取数据出错,终止通信服务:%s", err))
|
||||
i.Stop()
|
||||
})
|
||||
if err != nil {
|
||||
panic(sys_error.New(fmt.Sprintf("%s启动TCP客户端失败", logTag), err))
|
||||
}
|
||||
i.tcpClient = client
|
||||
}
|
||||
|
||||
func (i *interlockProxy) handleFromCiData(n int, data []byte) {
|
||||
data = data[:n]
|
||||
//logger().Info(fmt.Sprintf("收到联锁驱动及应答器报文数据,长度%d", n))
|
||||
if len(i.fromCIBytes) != 0 { //报文头已经存进去了
|
||||
i.fromCIBytes = append(i.fromCIBytes, data...)
|
||||
if len(i.fromCIBytes) < fromCIBytesLen { //当存储的报文长度不足预期长度
|
||||
return
|
||||
}
|
||||
} else if bytes.HasPrefix(data, []byte{0xff, 0xff}) { //判断是否是报文头
|
||||
i.fromCIBytes = append(i.fromCIBytes, data...)
|
||||
return
|
||||
} else {
|
||||
return
|
||||
}
|
||||
//处理联锁报文
|
||||
msg := &fromCiMsg{}
|
||||
err := msg.decode(i.fromCIBytes)
|
||||
//logger().Info(fmt.Sprintf("解析完整联锁驱动及应答器报文数据:%x", i.fromCIBytes))
|
||||
i.fromCIBytes = i.fromCIBytes[:0] //清空联锁报文存储
|
||||
if err != nil {
|
||||
logger().Error(fmt.Sprintf("解析来自CI的数据出错:%s", err.Error()))
|
||||
return
|
||||
}
|
||||
//处理驱动数据
|
||||
if i.simulation != nil {
|
||||
//logger().Info(fmt.Sprintf("解析出的驱动数据:%x", msg.driveData))
|
||||
station := i.simulation.Repo.FindStationByStationName(i.runConfig.Code)
|
||||
if station != nil {
|
||||
i.simulation.HandleInterlockDriverInfo(station.Id(), msg.driveData)
|
||||
} else {
|
||||
logger().Error(fmt.Sprintf("没有找到名为%s的车站", i.runConfig.Code))
|
||||
}
|
||||
}
|
||||
//处理应答器数据
|
||||
for _, datum := range msg.baliseTelegramData {
|
||||
indexBalise2 := i.indexBalise[datum.leuIndex]
|
||||
if indexBalise2 == nil {
|
||||
//logger().Error(fmt.Sprintf("没有leuIndex[%d]的应答器", datum.leuIndex))
|
||||
continue
|
||||
}
|
||||
balise := indexBalise2[datum.index]
|
||||
if balise == nil {
|
||||
//logger().Error(fmt.Sprintf("没有leuIndex[%d]leu内索引[%d]的应答器", datum.leuIndex, datum.index))
|
||||
continue
|
||||
}
|
||||
userTelegram, err := balisecodec.Decode(datum.telegram)
|
||||
if err != nil {
|
||||
logger().Error(fmt.Sprintf("解析应答器报文[%x]出错:%s", datum.telegram, err.Error()))
|
||||
continue
|
||||
} else {
|
||||
//logger().Info(fmt.Sprintf("解析出应答器[%s]的可变报文", balise.Id()))
|
||||
}
|
||||
err = fi.BaliseUpdateVariableTelegram(i.simulation.World, balise.Id(), datum.telegram, userTelegram, false)
|
||||
if err != nil {
|
||||
logger().Error(fmt.Sprintf("更新leuIndex[%d]leu内索引[%d]的应答器[%s]数据出错:%s", datum.leuIndex, datum.index, balise.Id(), err.Error()))
|
||||
} else {
|
||||
//logger().Info(fmt.Sprintf("更新leuIndex[%d]leu内索引[%d]的应答器[%s]数据成功", datum.leuIndex, datum.index, balise.Id()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func logger() *slog.Logger {
|
||||
loggerInit.Do(func() {
|
||||
privateLogger = slog.Default().With("tag", logTag)
|
||||
})
|
||||
return privateLogger
|
||||
}
|
79
third_party/interlock/beijing12/msg.go
vendored
Normal file
79
third_party/interlock/beijing12/msg.go
vendored
Normal file
@ -0,0 +1,79 @@
|
||||
package beijing12
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"joylink.club/bj-rtsts-server/sys_error"
|
||||
"joylink.club/bj-rtsts-server/util/myreader"
|
||||
)
|
||||
|
||||
// 后续如果有其它线路还用此协议,需要将这些常量做成配置
|
||||
const (
|
||||
syncZoneStateLen = 1289 //同步区状态字节数
|
||||
driveDataLen = 14 //驱动数据长度
|
||||
baliseTelegramDataSize = 28 //应答器报文数据个数
|
||||
baliseTelegramMsgLen = 131 //应答器报文数据长度
|
||||
)
|
||||
|
||||
// 来自联锁的报文字节长度
|
||||
var fromCIBytesLen = 8 + syncZoneStateLen + driveDataLen + baliseTelegramMsgLen*baliseTelegramDataSize
|
||||
|
||||
// fromCiMsg 来自联锁的消息
|
||||
type fromCiMsg struct {
|
||||
head1 byte // 包头1 0xFF
|
||||
head2 byte // 包头2 0xFF
|
||||
typeCode byte // 类型码
|
||||
seqNum byte // 序列号
|
||||
reserve []byte // 预留2字节
|
||||
syncZoneState []byte // 同步区状态(目前不用处理)
|
||||
driveData []byte // 驱动数据
|
||||
baliseTelegramData []*baliseTelegramMsg // 应答器报文数据
|
||||
tail []byte // 包尾 0x0000
|
||||
}
|
||||
|
||||
// driveDataLen 表示驱动数据字节数;baliseTelegramDataLen 表示应答器报文数据个数
|
||||
func (f *fromCiMsg) decode(data []byte) error {
|
||||
if len(data) != fromCIBytesLen {
|
||||
return errors.New(fmt.Sprintf("%s联锁数据长度期望值[%d],实际值[%d]", logTag, fromCIBytesLen, len(data)))
|
||||
}
|
||||
reader := myreader.NewReader(data)
|
||||
f.head1 = reader.ReadByte()
|
||||
f.head2 = reader.ReadByte()
|
||||
f.typeCode = reader.ReadByte()
|
||||
f.seqNum = reader.ReadByte()
|
||||
f.reserve = reader.ReadBytes(2)
|
||||
f.syncZoneState = reader.ReadBytes(syncZoneStateLen)
|
||||
f.driveData = reader.ReadBytes(driveDataLen)
|
||||
for i := 0; i < baliseTelegramDataSize; i++ {
|
||||
baliseBytes := reader.ReadBytes(baliseTelegramMsgLen)
|
||||
baliseMsg := &baliseTelegramMsg{}
|
||||
err := baliseMsg.decode(baliseBytes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
f.baliseTelegramData = append(f.baliseTelegramData, baliseMsg)
|
||||
}
|
||||
f.tail = reader.ReadBytes(2)
|
||||
if reader.Err != nil {
|
||||
return errors.New(fmt.Sprintf("读取字节出错:%s", reader.Err))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type baliseTelegramMsg struct {
|
||||
leuIndex byte // LEU索引
|
||||
index byte // LEU内部应答器的索引
|
||||
reserve byte // 预留
|
||||
telegram []byte // 应答器报文 128字节
|
||||
}
|
||||
|
||||
func (b *baliseTelegramMsg) decode(data []byte) error {
|
||||
if len(data) != 131 {
|
||||
return sys_error.New(fmt.Sprintf("%s应答器报文数据长度不对:%d", logTag, len(data)))
|
||||
}
|
||||
b.leuIndex = data[0]
|
||||
b.index = data[1]
|
||||
b.reserve = data[2]
|
||||
b.telegram = data[3:]
|
||||
return nil
|
||||
}
|
20
third_party/interlock/beijing12/msg_test.go
vendored
Normal file
20
third_party/interlock/beijing12/msg_test.go
vendored
Normal file
File diff suppressed because one or more lines are too long
133
third_party/interlock/interlock.go
vendored
133
third_party/interlock/interlock.go
vendored
@ -1,133 +0,0 @@
|
||||
package interlock
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"runtime/debug"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"joylink.club/bj-rtsts-server/config"
|
||||
"joylink.club/bj-rtsts-server/third_party/message"
|
||||
"joylink.club/bj-rtsts-server/third_party/udp"
|
||||
)
|
||||
|
||||
// 联锁代理通信接口
|
||||
type InterlockMessageManager interface {
|
||||
CollectInterlockRelayInfo(code string) *message.InterlockSendMsgPkg
|
||||
HandleInterlockDriverInfo(code string, b []byte)
|
||||
}
|
||||
|
||||
// 联锁接口
|
||||
type InterlockProxy interface {
|
||||
// 启动联锁消息功能
|
||||
Start(manager InterlockMessageManager)
|
||||
// 停止联锁消息功能
|
||||
Stop()
|
||||
// 发送联锁采集消息
|
||||
SendCollectMessage(b []byte)
|
||||
}
|
||||
|
||||
var interlockMap = make(map[string]InterlockProxy)
|
||||
var initMutex sync.Mutex
|
||||
|
||||
func Default(c *config.InterlockConfig) InterlockProxy {
|
||||
initMutex.Lock()
|
||||
defer initMutex.Unlock()
|
||||
if interlockMap[c.Code] == nil {
|
||||
interlockMap[c.Code] = &interlockProxy{runConfig: c}
|
||||
}
|
||||
return interlockMap[c.Code]
|
||||
}
|
||||
|
||||
type interlockProxy struct {
|
||||
driveInfoUdpServer udp.UdpServer
|
||||
sendCollectUdpClient udp.UdpClient
|
||||
|
||||
manager InterlockMessageManager
|
||||
collectInfoTaskCancel context.CancelFunc
|
||||
runConfig *config.InterlockConfig
|
||||
}
|
||||
|
||||
// 驱动信息进行转发
|
||||
func (i *interlockProxy) handleDriverInfo(b []byte) {
|
||||
handler := i.manager
|
||||
if handler != nil {
|
||||
handler.HandleInterlockDriverInfo(i.runConfig.Code, b)
|
||||
}
|
||||
}
|
||||
|
||||
func (i *interlockProxy) Start(manager InterlockMessageManager) {
|
||||
if i.runConfig == nil || i.runConfig.Ip == "" || !i.runConfig.Open {
|
||||
return
|
||||
}
|
||||
if manager == nil {
|
||||
panic("启动联锁消息服务错误: InterlockMessageManager不能为nil")
|
||||
}
|
||||
if i.manager != nil {
|
||||
panic("启动联锁消息服务错误: 存在正在运行的任务")
|
||||
}
|
||||
i.manager = manager
|
||||
// 初始化客户端、服务端
|
||||
i.initInterlockProxy()
|
||||
ctx, cancle := context.WithCancel(context.Background())
|
||||
go i.collectInfoStateTask(ctx)
|
||||
i.collectInfoTaskCancel = cancle
|
||||
}
|
||||
|
||||
// 采集电路状态发送间隔,单位ms
|
||||
const InterlockMessageSendInterval = 50
|
||||
|
||||
// 序列号
|
||||
var serialNumber uint8
|
||||
|
||||
// 定时发送采集电路状态任务
|
||||
func (i *interlockProxy) collectInfoStateTask(ctx context.Context) {
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
slog.Error("定时发送道岔状态任务异常", "error", err, "stack", string(debug.Stack()))
|
||||
debug.PrintStack()
|
||||
}
|
||||
}()
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
default:
|
||||
}
|
||||
collectInfoState := i.manager.CollectInterlockRelayInfo(i.runConfig.Code)
|
||||
if collectInfoState != nil {
|
||||
serialNumber++
|
||||
collectInfoState.SetSerialNumber(serialNumber)
|
||||
i.sendCollectUdpClient.SendMsg(collectInfoState)
|
||||
}
|
||||
time.Sleep(time.Millisecond * InterlockMessageSendInterval)
|
||||
}
|
||||
}
|
||||
|
||||
func (i *interlockProxy) Stop() {
|
||||
initMutex.Lock()
|
||||
defer initMutex.Unlock()
|
||||
delete(interlockMap, i.runConfig.Code)
|
||||
if i.sendCollectUdpClient != nil {
|
||||
i.sendCollectUdpClient.Close()
|
||||
}
|
||||
if i.driveInfoUdpServer != nil {
|
||||
i.driveInfoUdpServer.Close()
|
||||
}
|
||||
if i.collectInfoTaskCancel != nil {
|
||||
i.collectInfoTaskCancel()
|
||||
}
|
||||
i.manager = nil
|
||||
}
|
||||
|
||||
func (i *interlockProxy) SendCollectMessage(b []byte) {
|
||||
i.sendCollectUdpClient.Send(b)
|
||||
}
|
||||
|
||||
func (i *interlockProxy) initInterlockProxy() {
|
||||
i.sendCollectUdpClient = udp.NewClient(fmt.Sprintf("%v:%v", i.runConfig.Ip, i.runConfig.RemotePort))
|
||||
i.driveInfoUdpServer = udp.NewServer(fmt.Sprintf(":%d", i.runConfig.LocalPort), i.handleDriverInfo)
|
||||
i.driveInfoUdpServer.Listen()
|
||||
}
|
102
third_party/message/accelerometer.go
vendored
102
third_party/message/accelerometer.go
vendored
@ -1,49 +1,113 @@
|
||||
package message
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"math"
|
||||
)
|
||||
|
||||
const (
|
||||
accHeader = 0xA6
|
||||
uaidDefault = 0x1C
|
||||
uaidDefault = 0x71
|
||||
G = 9.80665
|
||||
)
|
||||
|
||||
type Accelerometer struct {
|
||||
Acc uint32
|
||||
Acc float32
|
||||
Aux byte
|
||||
}
|
||||
|
||||
func encodeAcc(a float32) (d2, d1, d0 byte) {
|
||||
d2 = 0
|
||||
d1 = 0
|
||||
d0 = 0
|
||||
x := a / G
|
||||
//fmt.Println(x)
|
||||
v := uint32(0)
|
||||
for i := 16; i >= 0; i-- {
|
||||
t := float32(1.0 / math.Pow(2, float64(17-i)))
|
||||
if t > x {
|
||||
continue
|
||||
} else {
|
||||
v |= 1 << i
|
||||
x -= t
|
||||
}
|
||||
}
|
||||
//fmt.Printf("%b, %b\n", v, v<<6)
|
||||
v <<= 6
|
||||
d0 = byte(v)
|
||||
d1 = byte(v >> 8)
|
||||
d2 = byte(v >> 16)
|
||||
|
||||
//accCode := v << 8 //数据向左移动8位,取后3个字节
|
||||
//d2 = byte(accCode >> 16) //取出d2数据
|
||||
//d1 = byte(accCode >> 8 << 8) // & 0xff //取出d1
|
||||
//d0 = byte(accCode << 20) //& 0xf //取出d0高2位
|
||||
|
||||
//fmt.Printf("%b, %b, %b\n", d2, d1, d0)
|
||||
return
|
||||
}
|
||||
|
||||
func decode2Acc(d2, d1, d0 byte) float32 {
|
||||
v := uint32(d2)<<10 | uint32(d1)<<2 | uint32(d0>>6)
|
||||
fmt.Printf("%b\n", v)
|
||||
x := float32(0)
|
||||
for i := 17; i >= 0; i-- {
|
||||
if v&(1<<i) != 0 {
|
||||
t := float32(1.0 / math.Pow(2, float64(17-i)))
|
||||
x += t
|
||||
}
|
||||
}
|
||||
//fmt.Println(x)
|
||||
return x * G
|
||||
}
|
||||
|
||||
func (acc *Accelerometer) BuildCanData() []byte {
|
||||
accData := acc.Encode()
|
||||
appData := make([]byte, 0)
|
||||
appData = append(appData, accData...)
|
||||
appData = append(appData, byte(0))
|
||||
canId := CanFrameId{ID1: 0xff, ID2: 0xff, ID3: 0xff, ID4: 0xff}
|
||||
canNet := &CanetFrame{FF: false, RTR: false, CanLen: 7, CanId: canId, CanData: appData}
|
||||
return canNet.Encode()
|
||||
}
|
||||
func (acc *Accelerometer) Encode() []byte {
|
||||
d2, d1, d0 := encodeAcc(acc.Acc)
|
||||
if acc.Acc > 0 {
|
||||
setBit(d2, 7, 0)
|
||||
} else {
|
||||
setBit(d2, 7, 1)
|
||||
}
|
||||
data := make([]byte, 0)
|
||||
data = append(data, accHeader)
|
||||
data = append(data, uaidDefault)
|
||||
acc.Acc = acc.Acc | 0x0000FF
|
||||
data = binary.LittleEndian.AppendUint32(data, acc.Acc)
|
||||
data = append(data, d0)
|
||||
data = append(data, d1)
|
||||
data = append(data, d2)
|
||||
data = append(data, byte(0))
|
||||
cs := checkSum(data)
|
||||
data = append(data, ^cs+1)
|
||||
data = append(data, cs)
|
||||
return data
|
||||
}
|
||||
|
||||
func checkSum(data []byte) byte {
|
||||
var sum byte = 0
|
||||
var sum uint16 = 0
|
||||
|
||||
for _, d := range data {
|
||||
sum += d
|
||||
sum += uint16(d)
|
||||
}
|
||||
return sum
|
||||
highByte := byte(sum >> 8) // 取高8位
|
||||
lowByte := byte(sum) // 取低8位
|
||||
secondSum := highByte + lowByte // 再次累加
|
||||
|
||||
//inverse := ^secondSum
|
||||
return ^secondSum
|
||||
}
|
||||
|
||||
func (acc *Accelerometer) Decode(data []byte) error {
|
||||
if len(data) < 7 {
|
||||
func (acc *Accelerometer) Decode(Packet []byte) error {
|
||||
if len(Packet) < 7 {
|
||||
return fmt.Errorf("")
|
||||
}
|
||||
buf := bytes.NewBuffer(data)
|
||||
_, _ = buf.ReadByte()
|
||||
_, _ = buf.ReadByte()
|
||||
var dataInt uint32 = 0
|
||||
binary.Read(buf, binary.LittleEndian, &dataInt)
|
||||
//newData := math.Float32frombits(dataInt & 0xffffc000)
|
||||
acc.Acc = dataInt & 0xffffc000
|
||||
decode2Acc(Packet[4], Packet[3], Packet[2])
|
||||
|
||||
return nil
|
||||
}
|
||||
|
94
third_party/message/btm_data.go
vendored
Normal file
94
third_party/message/btm_data.go
vendored
Normal file
@ -0,0 +1,94 @@
|
||||
package message
|
||||
|
||||
import "encoding/binary"
|
||||
|
||||
//数据帧(0x80+扩展类型),一个CAN帧只能存储8个字节,对于一个应答器报文,分片到多个CAN数据帧中
|
||||
|
||||
// BtmDataMessageFrame 数据帧-应答器报文
|
||||
// offset = [0x00,0x0c] 共13个即可用于存储应答器报文的共104个字节
|
||||
// BtmDataMessageTimeAFrame 数据帧-时间戳A
|
||||
// offset = 0x0d
|
||||
// BtmDataMessageTimeBFrame 数据帧-时间戳B
|
||||
// offset = 0x0e
|
||||
// BtmDataMessageEndFrame 数据帧-结束
|
||||
// offset = 0x7f
|
||||
const (
|
||||
DATA_TIME_A_OFFSET = 0x0d
|
||||
DATA_TIME_B_OFFSET = 0x0e
|
||||
DATA_END_OFFSET = 0x7f
|
||||
)
|
||||
|
||||
type BtmDataMessageAtpFrame struct {
|
||||
//帧ID
|
||||
FId CanFrameId
|
||||
//8字节,应答器报文片段
|
||||
Message []byte
|
||||
}
|
||||
type BtmDataMessageAtpTimeAndEndFrame struct {
|
||||
//帧ID
|
||||
FId CanFrameId
|
||||
//时间戳A
|
||||
Time uint32
|
||||
//CRC
|
||||
Crc32 uint32
|
||||
}
|
||||
|
||||
func CreateBtmDataMessageDataAtpFrame(sn byte, offset byte) *BtmDataMessageAtpFrame {
|
||||
return &BtmDataMessageAtpFrame{
|
||||
FId: *NewCanFrameId(CAN_ADDR_RSP_ATP, CAN_ADDR_RSP_BTM, 0x80+offset, sn),
|
||||
}
|
||||
}
|
||||
|
||||
func (f *BtmDataMessageAtpFrame) Encode() *BtmHeadFrame {
|
||||
cf := &BtmHeadFrame{}
|
||||
cf.CanId = f.FId
|
||||
cf.CanLen = 8
|
||||
|
||||
dataBuf := make([]byte, 0, 8)
|
||||
for _, data := range f.Message {
|
||||
dataBuf = append(dataBuf, data)
|
||||
}
|
||||
cf.CanData = dataBuf
|
||||
return cf
|
||||
}
|
||||
func (f *BtmDataMessageAtpFrame) Decode(cf *BtmHeadFrame) bool {
|
||||
f.FId = cf.CanId
|
||||
//
|
||||
reader := NewCanBitsReader(cf.CanData, 8)
|
||||
//
|
||||
f.Message = make([]byte, 0, 8)
|
||||
for c := 0; c < 8; c++ {
|
||||
data, ok := reader.ReadByte()
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
f.Message = append(f.Message, data)
|
||||
}
|
||||
//
|
||||
return true
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////
|
||||
|
||||
func (atp *BtmDataMessageAtpTimeAndEndFrame) Encode() *BtmHeadFrame {
|
||||
cf := &BtmHeadFrame{}
|
||||
cf.CanId = atp.FId
|
||||
cf.CanLen = 8
|
||||
data := make([]byte, 0)
|
||||
data = binary.BigEndian.AppendUint32(data, atp.Time)
|
||||
data = binary.BigEndian.AppendUint32(data, atp.Crc32)
|
||||
cf.CanData = data
|
||||
return cf
|
||||
}
|
||||
|
||||
func CreateBtmDataMessageTimeAtpFrame(sn byte, offset byte) *BtmDataMessageAtpTimeAndEndFrame {
|
||||
return &BtmDataMessageAtpTimeAndEndFrame{FId: *NewCanFrameId(CAN_ADDR_RSP_ATP, CAN_ADDR_RSP_BTM, 0x80+offset, sn)}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
|
||||
func BtmDataMessageEndAtpFrame(sn byte) *BtmDataMessageAtpTimeAndEndFrame {
|
||||
return &BtmDataMessageAtpTimeAndEndFrame{
|
||||
FId: *NewCanFrameId(CAN_ADDR_RSP_ATP, CAN_ADDR_RSP_BTM, 0xff, sn),
|
||||
}
|
||||
}
|
67
third_party/message/btm_net.go
vendored
Normal file
67
third_party/message/btm_net.go
vendored
Normal file
@ -0,0 +1,67 @@
|
||||
package message
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// CanetFrame USR-CANET200_V1.0.7 设备 以太网-CAN 透传协议帧(12字节)
|
||||
type BtmHeadFrame struct {
|
||||
CanLen byte //CAN帧数据长度[0,8],CanData中有效数据字节数
|
||||
CanId CanFrameId //CAN帧ID
|
||||
CanData []byte //CAN帧数据,8字节
|
||||
|
||||
}
|
||||
|
||||
func NewBtmHeadFrame(buf []byte) *BtmHeadFrame {
|
||||
cf := &BtmHeadFrame{}
|
||||
cf.Decode(buf)
|
||||
return cf
|
||||
}
|
||||
func (p *BtmHeadFrame) Encode() []byte {
|
||||
buf := make([]byte, 0)
|
||||
//CAN 帧ID
|
||||
frameId := uint32(p.CanId.ID1)<<21 | uint32(p.CanId.ID2)<<13 | uint32(p.CanId.ID3)<<5 | uint32(p.CanId.ID4)
|
||||
buf = binary.BigEndian.AppendUint32(buf, frameId)
|
||||
//CAN 帧数据
|
||||
if len(p.CanData) != 8 {
|
||||
panic(fmt.Sprintf("len(p.CanData)!=8 datalen:%v", len(p.CanData)))
|
||||
}
|
||||
buf = append(buf, p.CanData...)
|
||||
return buf
|
||||
}
|
||||
|
||||
func (p *BtmHeadFrame) Decode(buf []byte) {
|
||||
bb := bytes.NewBuffer(buf)
|
||||
var idSource uint32
|
||||
binary.Read(bb, binary.BigEndian, &idSource)
|
||||
data := make([]byte, bb.Len())
|
||||
_, err := bb.Read(data)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
b1 := byte((idSource >> 21) & 0xFF)
|
||||
b2 := byte((idSource >> 13) & 0xFF)
|
||||
b3 := byte((idSource >> 5) & 0xFF)
|
||||
b4 := byte(idSource & 0x1F)
|
||||
|
||||
p.CanId.ID1 = b1
|
||||
p.CanId.ID2 = b2
|
||||
p.CanId.ID3 = b3
|
||||
p.CanId.ID4 = b4
|
||||
p.CanData = data
|
||||
}
|
||||
|
||||
func (p *BtmHeadFrame) String() string {
|
||||
sb := strings.Builder{}
|
||||
sb.WriteString(fmt.Sprintf("CanetFrame CanLen = %d, ID1 = 0x%0x, ID2 = 0x%0x, ID3 = 0x%0x, ID4 = 0x%0x", p.CanLen,
|
||||
p.CanId.ID1, p.CanId.ID2, p.CanId.ID3, p.CanId.ID4))
|
||||
sb.WriteString("CanData = ")
|
||||
for _, d := range p.CanData {
|
||||
sb.WriteString(fmt.Sprintf(" 0x%0x ", d))
|
||||
}
|
||||
return sb.String()
|
||||
}
|
347
third_party/message/btm_vobc_data.go
vendored
Normal file
347
third_party/message/btm_vobc_data.go
vendored
Normal file
@ -0,0 +1,347 @@
|
||||
package message
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"github.com/snksoft/crc"
|
||||
"log/slog"
|
||||
"strconv"
|
||||
"sync"
|
||||
)
|
||||
|
||||
const (
|
||||
TRAIN_BTM_ENDA = 0x75E1
|
||||
TRAIN_BTM_ENDB = 0x75E2
|
||||
ID_COMMAND_TYPE = 0xF8 //命令帧
|
||||
REQT_TYPE = 0xE6 //请求帧
|
||||
|
||||
)
|
||||
|
||||
const (
|
||||
COMMAND_TYPE = 0x90 //ID 命令帧 VOBC→BTM20 字节
|
||||
REQUEST_TYPE = 0x91 //请求帧 VOBC→BTM34 字节
|
||||
MESSAGE_TYPE = 0x92 //报文帧 BTM→VOBC 135 字节
|
||||
FREE_MESSAGE_TYPE = 0x94 //空闲帧 BTM→VOBC 135 字节
|
||||
)
|
||||
|
||||
var PACKAGE_HEAD = []byte{0xFF, 0xFE}
|
||||
var PACKAGE_END = []byte{0xFF, 0xFD}
|
||||
|
||||
func BtmVobcDecode(packData []byte) (byte, []byte, error) {
|
||||
head := packData[:2]
|
||||
end := packData[len(packData)-2:]
|
||||
|
||||
if !bytes.Equal(head, PACKAGE_HEAD) || !bytes.Equal(end, PACKAGE_END) {
|
||||
slog.Error("btm vobc 数据包头或包尾尾错误", hex.EncodeToString(packData))
|
||||
return 0, nil, fmt.Errorf("btm vobc 数据包头或包尾尾错误")
|
||||
}
|
||||
data := packData[2 : len(packData)-2]
|
||||
command := data[0]
|
||||
if command != ID_COMMAND_TYPE && command != REQT_TYPE {
|
||||
slog.Error("btm vobc 解析未知命令帧", strconv.FormatInt(int64(command), 16), command)
|
||||
return 0, nil, fmt.Errorf("btm vobc 解析未知命令帧")
|
||||
}
|
||||
dataText := data[1:]
|
||||
if command == ID_COMMAND_TYPE {
|
||||
|
||||
frameType := dataText[0]
|
||||
return frameType, dataText, nil
|
||||
} else if command == REQT_TYPE {
|
||||
newDatatext, _ := TranslateFromFFFE(dataText)
|
||||
rssp := &RsspRsd{}
|
||||
rsspErr := rssp.Decode(newDatatext)
|
||||
if rsspErr != nil {
|
||||
//slog.Error("解析请求帧rssp-i失败,元数据:", hex.EncodeToString(cfs), "错误信息:", rsspErr.Error())
|
||||
return 0, nil, fmt.Errorf("解析请求帧rssp-i失败")
|
||||
}
|
||||
return rssp.Sad[0], rssp.Sad, nil
|
||||
}
|
||||
return 0, nil, fmt.Errorf("btm vobc 解析未知命令帧")
|
||||
}
|
||||
|
||||
const (
|
||||
WAIT_FF_C1 = 0x00
|
||||
WAIT_FF_C2 = 0x01
|
||||
WAIT_NO_FF = 0x02
|
||||
WAIT_FF_POS = 0x03
|
||||
ABNORMAL_STATE = 0x04
|
||||
|
||||
/*FFFE数据接收状态*/
|
||||
COM_WAIT_START_FF = 0x00
|
||||
COM_WAIT_START_FE = 0x01
|
||||
COM_WAIT_STOP_FF = 0x02
|
||||
COM_WAIT_STOP_FD = 0x03
|
||||
)
|
||||
|
||||
/*
|
||||
func SourceDataDecode(packData []byte) []BaseBtmVobc {
|
||||
data, _ := TranslateFromFFFE(packData)
|
||||
buf := bytes.NewBuffer(data)
|
||||
|
||||
}
|
||||
|
||||
func parseData(buf *bytes.Buffer) {
|
||||
h1, _ := buf.ReadByte()
|
||||
h2, _ := buf.ReadByte()
|
||||
if bytes.Equal(PACKAGE_HEAD, []byte{h1, h2}) {
|
||||
t, _ := buf.ReadByte()
|
||||
var ft byte = 0
|
||||
if t == ID_COMMAND_TYPE || t == REQT_TYPE {
|
||||
ft, _ = buf.ReadByte()
|
||||
if t == ID_COMMAND_TYPE {
|
||||
return parseIdFrame(ft, buf.Bytes())
|
||||
} else if t == REQT_TYPE {
|
||||
|
||||
}
|
||||
} else {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
func parseIdFrame(frame byte, data []byte) BtmVobcIdCommand {
|
||||
buf := bytes.NewBuffer(data)
|
||||
dataLen, _ := buf.ReadByte()
|
||||
dsn, _ := buf.ReadByte()
|
||||
var btmId uint16
|
||||
var vobcId uint16
|
||||
var vobcLifeId uint32
|
||||
var yuliu [5]byte
|
||||
var crc32 uint32
|
||||
binary.Read(buf, binary.BigEndian, &btmId)
|
||||
binary.Read(buf, binary.BigEndian, &vobcId)
|
||||
binary.Read(buf, binary.BigEndian, &vobcLifeId)
|
||||
binary.Read(buf, binary.BigEndian, &yuliu)
|
||||
binary.Read(buf, binary.BigEndian, &crc32)
|
||||
return BtmVobcIdCommand{BaseBtmVobc: BaseBtmVobc{Frame: frame, FrameLen: dataLen, AutoIdFrame: dsn, Crc32: crc32}}
|
||||
}
|
||||
|
||||
type BaseBtmVobc struct {
|
||||
Frame byte //帧类型
|
||||
FrameLen byte //帧长定义为 TEXT+CRC32,ID 命令帧为 20 字节;
|
||||
AutoIdFrame byte // 无论何时传输数据,该数都在 1-255 范围内递增,在错误重传时也是递增的,255 之后是 1,不使用 0;
|
||||
Crc32 uint32
|
||||
}
|
||||
|
||||
func baseDecode(d []byte) (BaseBtmVobc, *bytes.Buffer) {
|
||||
buf := bytes.NewBuffer(d)
|
||||
|
||||
frame, _ := buf.ReadByte()
|
||||
frameLen, _ := buf.ReadByte()
|
||||
autoIdFrame, _ := buf.ReadByte()
|
||||
crc32 := binary.BigEndian.Uint32(d[len(d)-4:])
|
||||
|
||||
return BaseBtmVobc{Frame: frame, FrameLen: frameLen, Crc32: crc32, AutoIdFrame: autoIdFrame}, buf
|
||||
}
|
||||
func baseEncode(source []byte) []byte {
|
||||
data := make([]byte, 0)
|
||||
crc32 := crc.CalculateCRC(crc.CRC32, source)
|
||||
newSource := binary.BigEndian.AppendUint32(source, uint32(crc32))
|
||||
|
||||
f2 := make([]byte, len(newSource)*2)
|
||||
lens := aa(newSource, f2)
|
||||
fffeData := f2[:lens]
|
||||
data = append(data, PACKAGE_HEAD...)
|
||||
data = append(data, fffeData...)
|
||||
data = append(data, PACKAGE_END...)
|
||||
return data
|
||||
}
|
||||
|
||||
// ID 命令帧的正文
|
||||
type BtmVobcIdCommand struct {
|
||||
BaseBtmVobc
|
||||
BtmId uint16 //VOBC 向 BTM 分配的 ID,暂定 0x75E1 或者0x75A2,其他无效;
|
||||
VobcId uint16 //暂定 0x5511,其他无效
|
||||
VobcLifeNum uint32 //7~10 1~FFFFFFFF,0 不使用
|
||||
yuliu []byte //11-15 预留字节
|
||||
}
|
||||
|
||||
func (b *BtmVobcIdCommand) Decode(data []byte) {
|
||||
base, buf := baseDecode(data)
|
||||
b.BaseBtmVobc = base
|
||||
var btmId uint16
|
||||
var vobcId uint16
|
||||
var lifeNum uint32
|
||||
binary.Read(buf, binary.BigEndian, &btmId)
|
||||
binary.Read(buf, binary.BigEndian, &vobcId)
|
||||
binary.Read(buf, binary.BigEndian, &lifeNum)
|
||||
|
||||
b.BtmId = btmId
|
||||
b.VobcId = vobcId
|
||||
b.VobcLifeNum = lifeNum
|
||||
}
|
||||
|
||||
const (
|
||||
REQ_FRAME_STATUS_OK = 0x06
|
||||
REQ_FRAME_STATUS_ERROR = 0x15
|
||||
REQ_FRAME_STATUS_BOOT = 0x00
|
||||
|
||||
REQ_PACKETS_TYPE_FREE = 0x05
|
||||
REQ_PACKETS_TYPE_MSG = 0x0A
|
||||
REQ_PACKETS_TYPE_BOOT = 0x00
|
||||
)
|
||||
|
||||
// 请求帧的正文
|
||||
type BtmVobcReq struct {
|
||||
BaseBtmVobc
|
||||
FrameStatus byte //帧正确/不正确 06h:帧正确15h:不正确00h:开机状态填00 其它无效
|
||||
MessageType byte // 空闲/报文数据 05h:空闲0Ah:报文数据00h:开机状态填00 其它无效
|
||||
MessageSerial byte //报文序列号 1-255;开机时使用0
|
||||
yuliu []byte //预留字节(10 字节)
|
||||
VobcLifeNum uint32 // VOBC 周期号 1~FFFFFFFF,0 不使用
|
||||
TimeStamp []byte //年月日时分秒各占一个字节
|
||||
Speed uint16 //速度 单位:cm/s
|
||||
VobcLifeWalkDistance uint16 //VOBC 周期走行距离 单位:cm
|
||||
}
|
||||
|
||||
func (b *BtmVobcReq) Decode(data []byte) {
|
||||
base, buf := baseDecode(data)
|
||||
|
||||
b.BaseBtmVobc = base
|
||||
|
||||
b.FrameStatus, _ = buf.ReadByte()
|
||||
b.MessageType, _ = buf.ReadByte()
|
||||
b.MessageSerial, _ = buf.ReadByte()
|
||||
var tyuli = make([]byte, 10)
|
||||
buf.Read(tyuli)
|
||||
//b.yuliu = data[5:15]
|
||||
var lifeNum uint32
|
||||
ts := make([]byte, 6)
|
||||
var speed uint16
|
||||
var walkDis uint16
|
||||
binary.Read(buf, binary.BigEndian, &lifeNum)
|
||||
buf.Read(ts)
|
||||
binary.Read(buf, binary.BigEndian, &speed)
|
||||
binary.Read(buf, binary.BigEndian, &walkDis)
|
||||
b.VobcLifeNum = lifeNum
|
||||
b.TimeStamp = ts
|
||||
b.Speed = speed
|
||||
b.VobcLifeWalkDistance = walkDis
|
||||
}
|
||||
|
||||
var lock sync.Mutex
|
||||
|
||||
const (
|
||||
btm_status_ok = 0x00
|
||||
btm_status_warn = 0x04
|
||||
)
|
||||
|
||||
var mesage_yuliu = []byte{0, 0, 0, 0, 0, 0, 0, 0, 0} //11~19 预留
|
||||
var yuliu3 = []byte{0, 0, 0, 0, 0, 0} //8~13 保留
|
||||
|
||||
const (
|
||||
BTM_STSTUS_NORMAL = 0x00
|
||||
BTM_STSTUS_WARN = 0x04
|
||||
)
|
||||
|
||||
// 报文帧的正文
|
||||
type BtmVobcMessage struct {
|
||||
BaseBtmVobc
|
||||
FontTtl uint16 //前沿 TTL 时间 单位为 ms,溢出为0xffff,高字节在前;车体及应答器器地面环境理想情况下误差小于 5ms
|
||||
MsgSerial byte //报文序列号 1-255;不使用 0
|
||||
BtmStatus byte //BTM 工作状态 00H-工作正常;04H-警告 BTM 有轻微故障(单套故障,整机能工作),向BDMS 汇报;FFH-BTM 故障,不能正常工作;在MMI进行故障提示并向ATS 汇报
|
||||
Enter1 byte //1 通道好码率 0~100
|
||||
Enter2 byte //2 通道好码率 0~100
|
||||
DecodeTime uint16 //解码时间:从包络前沿到解码成功的时间,单位0.1ms。
|
||||
yuliu []byte //11~19 预留 填 0
|
||||
BackTtl uint16 //后沿 TTL 时间单位为 ms,溢出为0xffff,高字节在前;车体及应答器器地面环境理想情况下误差小于 5ms.
|
||||
BtmMsg []byte //22~125 应答器报文
|
||||
ResponseTime byte //0~150,其他非法,单位0.1ms 误差小于 3ms
|
||||
VobcLifeNum uint32 //VOBC 周期号 1~FFFFFFFF,0 不使用。
|
||||
}
|
||||
|
||||
func (b *BtmVobcMessage) Encode() []byte {
|
||||
|
||||
data := make([]byte, 0)
|
||||
buf := bytes.NewBuffer(data)
|
||||
|
||||
binary.Write(buf, binary.BigEndian, byte(MESSAGE_TYPE))
|
||||
binary.Write(buf, binary.BigEndian, byte(0x87))
|
||||
|
||||
//binary.Write(buf, binary.BigEndian, b.AutoIdFrame)
|
||||
binary.Write(buf, binary.BigEndian, b.AutoIdFrame)
|
||||
binary.Write(buf, binary.BigEndian, b.FontTtl)
|
||||
binary.Write(buf, binary.BigEndian, b.MsgSerial)
|
||||
binary.Write(buf, binary.BigEndian, b.BtmStatus)
|
||||
binary.Write(buf, binary.BigEndian, byte(100)) //b.Enter1
|
||||
binary.Write(buf, binary.BigEndian, byte(100)) //b.Enter2
|
||||
binary.Write(buf, binary.BigEndian, b.DecodeTime)
|
||||
binary.Write(buf, binary.BigEndian, mesage_yuliu)
|
||||
binary.Write(buf, binary.BigEndian, b.BackTtl)
|
||||
binary.Write(buf, binary.BigEndian, b.BtmMsg)
|
||||
binary.Write(buf, binary.BigEndian, b.ResponseTime)
|
||||
binary.Write(buf, binary.BigEndian, b.VobcLifeNum)
|
||||
|
||||
return baseEncode(buf.Bytes())
|
||||
}
|
||||
|
||||
type BtmVobcMsgFree struct {
|
||||
BaseBtmVobc
|
||||
//yuliu1 byte
|
||||
//yuliu2 byte
|
||||
MsgSerial byte //1-255;开机时为0
|
||||
BtmStatus byte //BTM 工作状态 00H-工作正常;04H-警告 BTM 有轻微故障(单套故障,整机能工作),向BDMS 汇报;FFH-BTM 故障,不能正常工作;在MMI 进行故障提示并向ATS 汇报
|
||||
WorkTemperature byte
|
||||
//yuliu3 []byte //8~13 保留 填 0
|
||||
|
||||
//0-1 位 功放 1 电流状态 功放电流状态:“1”-欠流;“2”- 过流 ;“3”-正常(欠流和过流仅作为功放状态参考)
|
||||
//2-3 位 功放 1 电压状态 功放电压状态:“1”-欠压;“2”- 过压 ;“3”-正常(欠压和过压仅作为功放状态参考)
|
||||
//4-5 位 (预留) 功放 2 电流状态 功放电流状态:“1”-欠流;“2”- 过流 ;“3”-正常(欠流和过流仅作为功放状态参考)
|
||||
//6-7 位 功放 2 电压状 功放电压状态:“1”-欠压;“2”- 过压 ;“3”-正常(欠压和过压仅作为功放状态参考)
|
||||
//8-9 位 天线 1 状态 天线 1 状态:“1”-故障;“3”-正常。
|
||||
//10-11 位 线缆 1 状态 线缆 1 状态:“1”-开路;“2”-短路;“3”-正常。
|
||||
//12-13 位 (预留) 天线 2 状态 天线 2 状态:“1”-故障;“3”-正常。
|
||||
//14-15 位 (预留) 线缆 2 状态 线缆 2 状态:“1”-开路;“2”-短路;“3”-正常。
|
||||
Fun1 uint16 //第 14-15 字节(功放板、天线状态)
|
||||
//第 0 位 上行自检码检测通道 1 状态:“1”-正常;“0”-故障。(故障时,检查处理器板和接收板)
|
||||
//第 1 位 上行自检码检测通道 2 状态:“1”-正常;“0”-故障。(故障时,检查处理器板和接收板)
|
||||
//第 2 位 FSK 连接线状态通道 1状态:“1”-正常;“0”-故障。
|
||||
//第 3 位 FSK 连接线状态通道 2状态:“1”-正常;“0”-故障。
|
||||
//第 4-5 位 保留 保留
|
||||
//第 6-7 位 接收板状态 状态:“0”-双通道故障;“1”-单通道故障;“3”-正常第
|
||||
//8-15位保留 保留
|
||||
Fun2 uint16 //第 16-17 字节(接收板状态)
|
||||
//第 0-1 位 通 道 1-24V 状态 状态:“1”-过压;“2”-欠压;“3”-正常;(欠压和过压仅作为电源板状态参考)
|
||||
//第 2-3 位 通 道 2-24V 状态 状态:“1”-过压;“2”-欠压;“3”-正常;(欠压和过压仅作为电源板状态参考)
|
||||
//第 4 位 通 道 1-23V 状态 状态:“1”-正常;“0”-故障。
|
||||
//第 5 位 通 道 2-23V 状态 状态:“1”-正常;“0”-故障。
|
||||
//第 6-13位保留 保留
|
||||
//第 14-15位 电源板状态 电源板状态:“0”-故障;“1”-单通道故障;“2”-单通道故障;“3”- 正常。
|
||||
Fun3 uint16 //第 18-19 字节(电源板状态)
|
||||
//第 0-1 位 板卡 ID 槽位号 “0”-1 号板卡;“1”-2 号板卡;“2”-3 号板卡;“3”-4 号板卡机箱正面从右往左顺序为处理器板1,2,3(预留),4(预留),同一个通信周期只有一个板卡报警
|
||||
//第 2-3 位 工作温度状态 “0”-过高;“1”-温度报警;“2”-未知;“3”-正常。
|
||||
//其他位保留 注:保留位全部为1
|
||||
Fun4 uint16 //第 20-21 字节(处理器板)
|
||||
|
||||
FreeMsg []byte //22~125 空闲数据 全填 00H
|
||||
RespTime byte //响应时间 0~150,其他非法,单位0.1ms
|
||||
VobcLifeNum uint32 //VOBC 周期号 1~FFFFFFFF,0 不使用。
|
||||
}
|
||||
|
||||
func (b *BtmVobcMsgFree) Encode() []byte {
|
||||
data := make([]byte, 0)
|
||||
buf := bytes.NewBuffer(data)
|
||||
|
||||
binary.Write(buf, binary.BigEndian, byte(FREE_MESSAGE_TYPE))
|
||||
binary.Write(buf, binary.BigEndian, byte(0x87))
|
||||
|
||||
binary.Write(buf, binary.BigEndian, b.AutoIdFrame)
|
||||
binary.Write(buf, binary.BigEndian, byte(0)) //保留
|
||||
binary.Write(buf, binary.BigEndian, byte(0)) //保留
|
||||
binary.Write(buf, binary.BigEndian, b.MsgSerial)
|
||||
binary.Write(buf, binary.BigEndian, b.BtmStatus)
|
||||
//binary.Write(buf, binary.BigEndian, b.WorkTemperature)
|
||||
binary.Write(buf, binary.BigEndian, byte(35))
|
||||
binary.Write(buf, binary.BigEndian, yuliu3)
|
||||
binary.Write(buf, binary.BigEndian, b.Fun1)
|
||||
binary.Write(buf, binary.BigEndian, b.Fun2)
|
||||
binary.Write(buf, binary.BigEndian, b.Fun3)
|
||||
binary.Write(buf, binary.BigEndian, b.Fun4)
|
||||
binary.Write(buf, binary.BigEndian, b.FreeMsg)
|
||||
binary.Write(buf, binary.BigEndian, b.RespTime)
|
||||
binary.Write(buf, binary.BigEndian, b.VobcLifeNum)
|
||||
|
||||
return baseEncode(buf.Bytes())
|
||||
}
|
283
third_party/message/btm_vobc_translate_fffe.go
vendored
Normal file
283
third_party/message/btm_vobc_translate_fffe.go
vendored
Normal file
@ -0,0 +1,283 @@
|
||||
package message
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func aa(src []byte, dest []byte) uint16 {
|
||||
var (
|
||||
SrcPos, TgtPos, Pos1, Pos2, iii, Gap uint16
|
||||
Got1stFF uint8
|
||||
)
|
||||
srouceLen := uint16(len(src))
|
||||
for SrcPos = 0; SrcPos < srouceLen; SrcPos++ {
|
||||
if Got1stFF == 1 {
|
||||
if src[SrcPos] == 0xff {
|
||||
//Got2ndFF = 1
|
||||
Pos2 = SrcPos
|
||||
dest[TgtPos] = byte(Pos2 - Pos1)
|
||||
TgtPos++
|
||||
|
||||
for iii = Pos1 + 1; iii < Pos2; iii++ {
|
||||
dest[TgtPos] = src[iii]
|
||||
TgtPos++
|
||||
}
|
||||
Got1stFF = 0
|
||||
//Got2ndFF = 0
|
||||
Pos1 = 0
|
||||
Pos2 = 0
|
||||
Gap = 0
|
||||
} else {
|
||||
/*已遇到前一个FF,且当前遇到非FF*/
|
||||
Gap++
|
||||
if 252 == Gap {
|
||||
Got1stFF = 0
|
||||
Gap = 0
|
||||
dest[TgtPos] = 0
|
||||
TgtPos++
|
||||
for iii = Pos1 + 1; iii <= SrcPos; iii++ {
|
||||
dest[TgtPos] = src[iii]
|
||||
TgtPos++
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/*尚未遇到前一个FF*/
|
||||
dest[TgtPos] = src[SrcPos]
|
||||
TgtPos++
|
||||
if 0xFF == src[SrcPos] {
|
||||
/*遇到前一个FF*/
|
||||
Got1stFF = 1
|
||||
Pos1 = SrcPos
|
||||
Gap = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
if 1 == Got1stFF {
|
||||
dest[TgtPos] = 0
|
||||
TgtPos++
|
||||
for iii = Pos1 + 1; iii < srouceLen; iii++ {
|
||||
dest[TgtPos] = src[iii]
|
||||
TgtPos++
|
||||
}
|
||||
}
|
||||
return TgtPos
|
||||
}
|
||||
|
||||
func TranslateFromFFFE(pSrc []byte) ([]byte, uint16) {
|
||||
var (
|
||||
srcPos, tgtPos, nonFFCount, nextFFPos uint16
|
||||
char uint8
|
||||
state int
|
||||
)
|
||||
|
||||
if len(pSrc) == 0 {
|
||||
return nil, 0 // 入参错误
|
||||
}
|
||||
|
||||
pTgt := make([]byte, len(pSrc)) // 预分配与输入等长的缓冲区
|
||||
tgtPos = 0
|
||||
state = WAIT_FF_C1
|
||||
nonFFCount = 0
|
||||
|
||||
for srcPos = 0; srcPos < uint16(len(pSrc)); srcPos++ {
|
||||
char = pSrc[srcPos]
|
||||
switch state {
|
||||
case WAIT_FF_C1:
|
||||
if char == 0xFF {
|
||||
pTgt[tgtPos] = char
|
||||
tgtPos++
|
||||
state = WAIT_FF_C2
|
||||
} else {
|
||||
pTgt[tgtPos] = char
|
||||
tgtPos++
|
||||
}
|
||||
case WAIT_FF_C2:
|
||||
if char == 0xFD || char == 0xFE || char == 0xFF {
|
||||
state = ABNORMAL_STATE
|
||||
} else {
|
||||
if char == 0 {
|
||||
state = WAIT_NO_FF
|
||||
} else if char == 1 {
|
||||
pTgt[tgtPos] = 0xFF
|
||||
tgtPos++
|
||||
state = WAIT_FF_C1
|
||||
} else {
|
||||
nextFFPos = srcPos + uint16(char) - 1
|
||||
state = WAIT_FF_POS
|
||||
}
|
||||
}
|
||||
case WAIT_NO_FF:
|
||||
nonFFCount++
|
||||
if char == 0xFF && nonFFCount < 252 {
|
||||
state = ABNORMAL_STATE
|
||||
} else {
|
||||
pTgt[tgtPos] = char
|
||||
tgtPos++
|
||||
if nonFFCount == 252 {
|
||||
nonFFCount = 0
|
||||
state = WAIT_FF_C1
|
||||
}
|
||||
}
|
||||
case WAIT_FF_POS:
|
||||
if char == 0xFF {
|
||||
state = ABNORMAL_STATE
|
||||
} else {
|
||||
pTgt[tgtPos] = char
|
||||
tgtPos++
|
||||
if srcPos == nextFFPos {
|
||||
pTgt[tgtPos] = 0xFF
|
||||
tgtPos++
|
||||
state = WAIT_FF_C1
|
||||
}
|
||||
}
|
||||
default:
|
||||
state = ABNORMAL_STATE
|
||||
}
|
||||
|
||||
if state == ABNORMAL_STATE {
|
||||
tgtPos = 0
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// 退出时的状态判断
|
||||
if state == WAIT_FF_C2 || state == WAIT_FF_POS {
|
||||
tgtPos = 0
|
||||
}
|
||||
|
||||
return pTgt[:tgtPos], tgtPos
|
||||
}
|
||||
|
||||
// TranslateToFFFE 对给定的字节切片进行FFFE转义处理
|
||||
func TranslateToFFFE(src []byte) ([]byte, error) {
|
||||
if src == nil {
|
||||
return nil, fmt.Errorf("source data is nil")
|
||||
}
|
||||
var tgt []byte
|
||||
var pos1, pos2 int
|
||||
var gap int
|
||||
var got1stFF bool
|
||||
|
||||
for i, b := range src {
|
||||
if got1stFF {
|
||||
if b == 0xFF {
|
||||
// 已遇到前一个FF,且当前又遇到FF
|
||||
got1stFF = false
|
||||
pos2 = i
|
||||
if gap > 252 {
|
||||
// 间隙过大,特殊处理
|
||||
tgt = append(tgt, 0)
|
||||
tgt = append(tgt, src[pos1+1:pos2]...)
|
||||
} else {
|
||||
// 写入间隙长度
|
||||
tgt = append(tgt, byte(gap))
|
||||
// 写入间隙中的字节
|
||||
tgt = append(tgt, src[pos1+1:pos2]...)
|
||||
}
|
||||
} else {
|
||||
// 已遇到前一个FF,且当前遇到非FF,增加gap计数
|
||||
gap++
|
||||
}
|
||||
} else {
|
||||
// 尚未遇到前一个FF
|
||||
tgt = append(tgt, b)
|
||||
if b == 0xFF {
|
||||
// 遇到前一个FF
|
||||
got1stFF = true
|
||||
pos1 = i
|
||||
gap = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 如果在数据末尾遇到了FF且没有第二个FF
|
||||
if got1stFF {
|
||||
if gap > 252 {
|
||||
tgt = append(tgt, 0)
|
||||
tgt = append(tgt, src[pos1+1:]...)
|
||||
} else {
|
||||
tgt = append(tgt, 0)
|
||||
tgt = append(tgt, src[pos1+1:len(src)]...)
|
||||
}
|
||||
}
|
||||
|
||||
return tgt, nil
|
||||
}
|
||||
|
||||
func TranslateToFFFE3(pSrc []uint8, SrcLen uint16) ([]byte, uint16) {
|
||||
var (
|
||||
SrcPos, TgtPos, Pos1, Pos2, iii uint16
|
||||
Gap uint16
|
||||
Got1stFF uint8
|
||||
pTgt []uint8
|
||||
)
|
||||
|
||||
if pSrc == nil {
|
||||
fmt.Println("入口参数错误")
|
||||
return nil, 0
|
||||
}
|
||||
|
||||
pTgt = make([]uint8, 0, SrcLen*2) // 预分配足够的空间以避免频繁扩容
|
||||
TgtPos = 0
|
||||
|
||||
for SrcPos = 0; SrcPos < SrcLen; SrcPos++ {
|
||||
if Got1stFF == 1 {
|
||||
if pSrc[SrcPos] == 0xFF {
|
||||
// 已遇到前一个FF,且当前又遇到FF
|
||||
//Got2ndFF = 1
|
||||
Pos2 = SrcPos
|
||||
pTgt = append(pTgt, uint8(Pos2-Pos1-1))
|
||||
TgtPos++
|
||||
|
||||
for iii = Pos1 + 1; iii < Pos2; iii++ {
|
||||
pTgt = append(pTgt, pSrc[iii])
|
||||
TgtPos++
|
||||
}
|
||||
|
||||
Got1stFF = 0
|
||||
//Got2ndFF = 0
|
||||
Pos1 = 0
|
||||
Pos2 = 0
|
||||
Gap = 0
|
||||
} else {
|
||||
// 已遇到前一个FF,且当前遇到非FF
|
||||
Gap++
|
||||
if Gap == 252 {
|
||||
Got1stFF = 0
|
||||
Gap = 0
|
||||
pTgt = append(pTgt, 0)
|
||||
TgtPos++
|
||||
|
||||
for iii = Pos1 + 1; iii <= SrcPos; iii++ {
|
||||
pTgt = append(pTgt, pSrc[iii])
|
||||
TgtPos++
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 尚未遇到前一个FF
|
||||
pTgt = append(pTgt, pSrc[SrcPos])
|
||||
TgtPos++
|
||||
if pSrc[SrcPos] == 0xFF {
|
||||
// 遇到前一个FF
|
||||
Got1stFF = 1
|
||||
Pos1 = SrcPos
|
||||
Gap = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 已经遇到了前一个FF, 且源数据已到了末尾仍未遇到后一个FF
|
||||
if Got1stFF == 1 {
|
||||
pTgt = append(pTgt, 0)
|
||||
TgtPos++
|
||||
|
||||
for iii = Pos1 + 1; iii < SrcLen; iii++ {
|
||||
pTgt = append(pTgt, pSrc[iii])
|
||||
TgtPos++
|
||||
}
|
||||
}
|
||||
|
||||
return pTgt, TgtPos
|
||||
}
|
51
third_party/message/can_atp_req.go
vendored
51
third_party/message/can_atp_req.go
vendored
@ -1,6 +1,8 @@
|
||||
package message
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"strings"
|
||||
@ -68,6 +70,14 @@ type AtpRequestFrame struct {
|
||||
Crc16 uint16
|
||||
//解码时CRC16校验结果,true-校验通过
|
||||
Crc16CheckOk bool
|
||||
//IsTrainPcSim bool
|
||||
}
|
||||
|
||||
func (f *AtpRequestFrame) IsResend() bool {
|
||||
if f.ResendRequest == 2 {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// NewAtpRequestFrame 创建ATP查询帧
|
||||
@ -89,6 +99,44 @@ func (f *AtpRequestFrame) String() string {
|
||||
sb.WriteString(fmt.Sprintf(",Crc16CheckOk = %t", f.Crc16CheckOk))
|
||||
return sb.String()
|
||||
}
|
||||
|
||||
func (f *AtpRequestFrame) Decode2(cf *BtmHeadFrame) bool {
|
||||
f.FId = cf.CanId
|
||||
buf := bytes.NewBuffer(cf.CanData)
|
||||
if d, err := buf.ReadByte(); err == nil {
|
||||
f.Speed = uint16(d << 4)
|
||||
rr := byte(0)
|
||||
rr = setBit(rr, 0, GetBit(d, 4))
|
||||
rr = setBit(rr, 1, GetBit(d, 5))
|
||||
f.ResendRequest = rr
|
||||
f.PowerAmplifierControlledByAtp = true
|
||||
f.PowerAmplifierTurnOn = true
|
||||
if rr == 2 {
|
||||
slog.Error(fmt.Sprintf("获取ATP请求帧数据:%b,rr :=%v ,result:%v id1:%v,id2:%v,id3:%v,id4:%v", d, rr, rr, f.FId.ID1, f.FId.ID2, f.FId.ID3, f.FId.ID4))
|
||||
|
||||
}
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
if d, err := buf.ReadByte(); err == nil {
|
||||
f.Speed |= uint16(d)
|
||||
}
|
||||
var time uint32
|
||||
binary.Read(buf, binary.BigEndian, &time)
|
||||
f.Time = time
|
||||
var crc uint16
|
||||
binary.Read(buf, binary.BigEndian, &crc)
|
||||
f.Crc16 = crc
|
||||
timeArr := make([]byte, 4)
|
||||
binary.BigEndian.PutUint32(timeArr, time)
|
||||
crc16 := Can_Crc16BtmReq(timeArr)
|
||||
f.Crc16CheckOk = f.Crc16 == crc16
|
||||
if !f.Crc16CheckOk {
|
||||
slog.Debug("解码AtpRequestFrame,CRC16校验未通过!!")
|
||||
}
|
||||
return true
|
||||
|
||||
}
|
||||
func (f *AtpRequestFrame) Decode(cf *CanetFrame) bool {
|
||||
f.FId = cf.CanId
|
||||
//
|
||||
@ -145,7 +193,7 @@ func (f *AtpRequestFrame) Decode(cf *CanetFrame) bool {
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
crc16 := Can_Crc16(timeArr)
|
||||
crc16 := Can_Crc16BtmReq(timeArr)
|
||||
t1 := uint32(timeArr[0])
|
||||
t2 := uint32(timeArr[1])
|
||||
t3 := uint32(timeArr[2])
|
||||
@ -178,6 +226,7 @@ func (f *AtpRequestFrame) Decode(cf *CanetFrame) bool {
|
||||
}
|
||||
func (f *AtpRequestFrame) Encode() *CanetFrame {
|
||||
cf := &CanetFrame{}
|
||||
|
||||
cf.CanId = f.FId
|
||||
cf.CanLen = 8
|
||||
cf.FF = true
|
||||
|
29
third_party/message/can_btm_data.go
vendored
29
third_party/message/can_btm_data.go
vendored
@ -18,17 +18,18 @@ func NewBtmDataMessageFrame(sn byte, offset byte) *BtmDataMessageFrame {
|
||||
}
|
||||
func (f *BtmDataMessageFrame) Encode() *CanetFrame {
|
||||
cf := &CanetFrame{}
|
||||
|
||||
cf.CanId = f.FId
|
||||
cf.CanLen = 8
|
||||
cf.FF = true
|
||||
cf.RTR = false
|
||||
//
|
||||
writer := NewCanBitsWriter(8)
|
||||
//
|
||||
for _, data := range f.Message {
|
||||
writer.AddByte(data)
|
||||
}
|
||||
cf.CanData = writer.(CanBusData).GetData()
|
||||
|
||||
//
|
||||
return cf
|
||||
}
|
||||
@ -67,29 +68,31 @@ func NewBtmDataMessageTimeAFrame(sn byte) *BtmDataMessageTimeAFrame {
|
||||
FId: *NewCanFrameId(CAN_ADDR_RSP_ATP, CAN_ADDR_RSP_BTM, 0x80+0x0d, sn),
|
||||
}
|
||||
}
|
||||
|
||||
func (f *BtmDataMessageTimeAFrame) Encode() *CanetFrame {
|
||||
cf := &CanetFrame{}
|
||||
|
||||
cf.CanId = f.FId
|
||||
cf.CanLen = 8
|
||||
cf.FF = true
|
||||
cf.RTR = false
|
||||
//
|
||||
|
||||
writer := NewCanBitsWriter(8)
|
||||
//
|
||||
|
||||
byteMsk := uint32(0x00_00_00_ff)
|
||||
//
|
||||
|
||||
writer.AddByte(byte((f.TimeA >> 24) & byteMsk))
|
||||
writer.AddByte(byte((f.TimeA >> 16) & byteMsk))
|
||||
writer.AddByte(byte((f.TimeA >> 8) & byteMsk))
|
||||
writer.AddByte(byte(f.TimeA & byteMsk))
|
||||
//
|
||||
|
||||
writer.AddByte(byte((f.Crc32A >> 24) & byteMsk))
|
||||
writer.AddByte(byte((f.Crc32A >> 16) & byteMsk))
|
||||
writer.AddByte(byte((f.Crc32A >> 8) & byteMsk))
|
||||
writer.AddByte(byte(f.Crc32A & byteMsk))
|
||||
//
|
||||
|
||||
cf.CanData = writer.(CanBusData).GetData()
|
||||
//
|
||||
|
||||
return cf
|
||||
}
|
||||
|
||||
@ -177,21 +180,21 @@ func (f *BtmDataMessageTimeBFrame) Encode() *CanetFrame {
|
||||
cf.RTR = false
|
||||
//
|
||||
writer := NewCanBitsWriter(8)
|
||||
//
|
||||
|
||||
byteMsk := uint32(0x00_00_00_ff)
|
||||
//
|
||||
|
||||
writer.AddByte(byte((f.TimeB >> 24) & byteMsk))
|
||||
writer.AddByte(byte((f.TimeB >> 16) & byteMsk))
|
||||
writer.AddByte(byte((f.TimeB >> 8) & byteMsk))
|
||||
writer.AddByte(byte(f.TimeB & byteMsk))
|
||||
//
|
||||
|
||||
writer.AddByte(byte((f.Crc32B >> 24) & byteMsk))
|
||||
writer.AddByte(byte((f.Crc32B >> 16) & byteMsk))
|
||||
writer.AddByte(byte((f.Crc32B >> 8) & byteMsk))
|
||||
writer.AddByte(byte(f.Crc32B & byteMsk))
|
||||
//
|
||||
|
||||
cf.CanData = writer.(CanBusData).GetData()
|
||||
//
|
||||
|
||||
return cf
|
||||
}
|
||||
func (f *BtmDataMessageTimeBFrame) Decode(cf *CanetFrame) bool {
|
||||
@ -271,8 +274,10 @@ func NewBtmDataMessageEndFrame(sn byte) *BtmDataMessageEndFrame {
|
||||
FId: *NewCanFrameId(CAN_ADDR_RSP_ATP, CAN_ADDR_RSP_BTM, 0x80+0x7f, sn),
|
||||
}
|
||||
}
|
||||
|
||||
func (f *BtmDataMessageEndFrame) Encode() *CanetFrame {
|
||||
cf := &CanetFrame{}
|
||||
|
||||
cf.CanId = f.FId
|
||||
cf.CanLen = 8
|
||||
cf.FF = true
|
||||
|
106
third_party/message/can_btm_rsp.go
vendored
106
third_party/message/can_btm_rsp.go
vendored
@ -1,20 +1,115 @@
|
||||
package message
|
||||
|
||||
import "log/slog"
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
)
|
||||
|
||||
//BTM与ATP之间为双向通信,ATP(主)定时发送请求帧,BTM(从)在未接收到应答器报文时回复状态应答器帧和时间同步帧,在接收到应答器报文时回复所有帧
|
||||
// 返回应答器数据帧(数据帧 + timeA+timeB+结束帧)
|
||||
func CreateBtmAtpDataRspFramesData(statusRsp *BtmHeadFrame, msg []byte, msgPackError, haxBaliseData bool, msgTimeA uint32, msgTimeB uint32, tkTimeB uint32) ([]byte, bool) {
|
||||
sn := statusRsp.CanId.ID4
|
||||
//应答器整个是16个数据帧,其中报文占13帧,timeA,timeB 占2帧,最后是结束帧
|
||||
//数据
|
||||
dms := make([]*BtmDataMessageAtpFrame, 13)
|
||||
for mr := 0x00; mr <= 0x0c; mr++ {
|
||||
dms[mr] = CreateBtmDataMessageDataAtpFrame(sn, byte(mr))
|
||||
dms[mr].Message = make([]byte, 8) //8字节数组,默认值0
|
||||
//
|
||||
if !msgPackError {
|
||||
mi := mr * 8
|
||||
if mi < len(msg) { //数据帧中有<=8个字节数据
|
||||
if mi+7 < len(msg) {
|
||||
dms[mr].Message = msg[mi : mi+8]
|
||||
} else {
|
||||
for i, d := range msg[mi:] {
|
||||
dms[mr].Message[i] = d
|
||||
}
|
||||
}
|
||||
}
|
||||
} else { //BTM解包发生错误,则数据帧及CRC32A/B全填“0xFF”
|
||||
for c := 0; c < 8; c++ {
|
||||
dms[mr].Message[c] = 0xff
|
||||
}
|
||||
}
|
||||
}
|
||||
dtA := createTimeA(sn, DATA_TIME_A_OFFSET, msgTimeA, msgPackError, msg, haxBaliseData)
|
||||
dtB := createTimeA(sn, DATA_TIME_B_OFFSET, msgTimeB, msgPackError, msg, haxBaliseData)
|
||||
dtACf := dtA.Encode()
|
||||
dtBCf := dtB.Encode()
|
||||
|
||||
// CreateBtmRspFramesData 数据帧与状态应答帧同时发送给ATP
|
||||
//结束帧
|
||||
|
||||
end := createDataAtpEnd(sn, tkTimeB, statusRsp.CanData, msg, dtACf.CanData, dtBCf.CanData)
|
||||
|
||||
endCf := end.Encode()
|
||||
rt := make([]byte, 0) //17*12
|
||||
for _, dmCf := range dms {
|
||||
rt = append(rt, dmCf.Encode().Encode()...)
|
||||
}
|
||||
rt = append(rt, dtACf.Encode()...)
|
||||
rt = append(rt, dtBCf.Encode()...)
|
||||
rt = append(rt, endCf.Encode()...)
|
||||
if len(rt) != 192 {
|
||||
slog.Warn(fmt.Sprintf("len(rt)!=192 实际数据长度:%v", len(rt)))
|
||||
return nil, false
|
||||
}
|
||||
return rt, true
|
||||
}
|
||||
|
||||
func createDataAtpEnd(sn byte, time uint32, statusData, msg, dtAData, dtBData []byte) *BtmDataMessageAtpTimeAndEndFrame {
|
||||
end := BtmDataMessageEndAtpFrame(sn)
|
||||
end.Time = time
|
||||
crc32cData := make([]byte, 0)
|
||||
|
||||
crc32cData = append(crc32cData, statusData...)
|
||||
crc32cData = append(crc32cData, msg...)
|
||||
crc32cData = append(crc32cData, dtAData...)
|
||||
crc32cData = append(crc32cData, dtBData...)
|
||||
crc32cData = binary.BigEndian.AppendUint32(crc32cData, end.Time)
|
||||
end.Crc32 = Can_Crc32(crc32cData)
|
||||
|
||||
return end
|
||||
}
|
||||
|
||||
func createTimeA(sn, offset byte, time uint32, msgPackError bool, msg []byte, hasBaliseData bool) *BtmDataMessageAtpTimeAndEndFrame {
|
||||
tf := CreateBtmDataMessageTimeAtpFrame(sn, offset)
|
||||
tf.Time = time
|
||||
if !msgPackError {
|
||||
var crcData []byte
|
||||
crcData = append(crcData, msg...)
|
||||
crcData = binary.BigEndian.AppendUint32(crcData, time)
|
||||
if !hasBaliseData {
|
||||
tf.Crc32 = 0
|
||||
} else {
|
||||
if offset == DATA_TIME_A_OFFSET {
|
||||
tf.Crc32 = Can_Crc32A(crcData) //CRC32A的校验范围是:报文+时间戳B
|
||||
|
||||
} else {
|
||||
tf.Crc32 = Can_Crc32B(crcData) //CRC32B的校验范围是:报文+时间戳B
|
||||
}
|
||||
}
|
||||
} else { //BTM解包发生错误,则数据帧及CRC32A/B全填“0xFF”
|
||||
tf.Crc32 = 0xff_ff_ff_ff
|
||||
}
|
||||
return tf
|
||||
}
|
||||
|
||||
// CreateBtmRspFramesData BTM与ATP之间为双向通信,ATP(主)定时发送请求帧,BTM(从)在未接收到应答器报文时回复状态应答器帧和时间同步帧,在接收到应答器报文时回复所有帧
|
||||
//
|
||||
// 数据帧与状态应答帧同时发送给ATP
|
||||
//
|
||||
// 共17帧,17X12个字节,每个帧12字节
|
||||
// msg - 应答器报文
|
||||
// msgPackError - true BTM解包发生错误,则数据帧及CRC32A/B全填“0xFF”
|
||||
func CreateBtmRspFramesData(statusRsp *BtmStatusRspFrame, msg []byte, msgPackError bool, msgTimeA uint32, msgTimeB uint32, tkTimeB uint32) ([]byte, bool) {
|
||||
if len(msg) > 104 { //数据帧最多存储13*8个字节
|
||||
/* if len(msg) > 104 { //数据帧最多存储13*8个字节
|
||||
return nil, false
|
||||
}
|
||||
}*/
|
||||
//最近一次ATP查询请求序列号
|
||||
sn := statusRsp.FId.ID4
|
||||
//13个BtmDataMessageFrame [0x00,0x0c]
|
||||
//数据
|
||||
dms := make([]*BtmDataMessageFrame, 13)
|
||||
for mr := 0x00; mr <= 0x0c; mr++ {
|
||||
dms[mr] = NewBtmDataMessageFrame(sn, byte(mr))
|
||||
@ -96,5 +191,6 @@ func CreateBtmRspFramesData(statusRsp *BtmStatusRspFrame, msg []byte, msgPackErr
|
||||
slog.Warn("len(rt)!=221")
|
||||
return nil, false
|
||||
}
|
||||
|
||||
return rt, true
|
||||
}
|
||||
|
31
third_party/message/can_btm_status_rsp.go
vendored
31
third_party/message/can_btm_status_rsp.go
vendored
@ -1,6 +1,7 @@
|
||||
package message
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
@ -32,6 +33,8 @@ type BtmStatusRspFrame struct {
|
||||
func NewBtmStatusRspFrame(sn byte) *BtmStatusRspFrame {
|
||||
return &BtmStatusRspFrame{
|
||||
FId: *NewCanFrameId(CAN_ADDR_RSP_ATP, CAN_ADDR_RSP_BTM, CAN_FRAME_STATUS_RSP, sn),
|
||||
//FId: *NewCanFrameId(0x00, 0x00, 0x05, sn),
|
||||
|
||||
}
|
||||
}
|
||||
func (f *BtmStatusRspFrame) Decode(cf *CanetFrame) bool {
|
||||
@ -111,6 +114,33 @@ func (f *BtmStatusRspFrame) Decode(cf *CanetFrame) bool {
|
||||
//
|
||||
return true
|
||||
}
|
||||
|
||||
func (f *BtmStatusRspFrame) EncodeBtmAtp() *BtmHeadFrame {
|
||||
cf := &BtmHeadFrame{}
|
||||
cf.CanId = f.FId
|
||||
cf.CanLen = 8
|
||||
data := make([]byte, 0)
|
||||
data = append(data, f.Dsn)
|
||||
data = append(data, f.BaliseCounter)
|
||||
data = append(data, f.MessageCounter)
|
||||
var state byte = 0
|
||||
if f.PowerAmplifierOn {
|
||||
state = setBit(state, 7, 1)
|
||||
}
|
||||
if f.AtpReqCrcCheckWrong {
|
||||
state = setBit(state, 6, 1)
|
||||
}
|
||||
if f.PowerAmplifierFailure {
|
||||
state = setBit(state, 5, 1)
|
||||
}
|
||||
if f.AntennaFault {
|
||||
state = setBit(state, 4, 1)
|
||||
}
|
||||
data = append(data, state)
|
||||
data = binary.BigEndian.AppendUint32(data, f.TkTimeA)
|
||||
cf.CanData = data
|
||||
return cf
|
||||
}
|
||||
func (f *BtmStatusRspFrame) Encode() *CanetFrame {
|
||||
cf := &CanetFrame{}
|
||||
cf.CanId = f.FId
|
||||
@ -122,6 +152,7 @@ func (f *BtmStatusRspFrame) Encode() *CanetFrame {
|
||||
writer.AddByte(f.Dsn)
|
||||
writer.AddByte(f.BaliseCounter)
|
||||
writer.AddByte(f.MessageCounter)
|
||||
|
||||
//
|
||||
if f.PowerAmplifierOn {
|
||||
writer.AddBits(1, 1)
|
||||
|
16
third_party/message/can_btm_time_sync_rsp.go
vendored
16
third_party/message/can_btm_time_sync_rsp.go
vendored
@ -1,6 +1,7 @@
|
||||
package message
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
@ -20,6 +21,8 @@ type BtmTimeSyncCheckFrame struct {
|
||||
func NewBtmTimeSyncCheckFrame(sn byte) *BtmTimeSyncCheckFrame {
|
||||
return &BtmTimeSyncCheckFrame{
|
||||
FId: *NewCanFrameId(CAN_ADDR_RSP_ATP, CAN_ADDR_RSP_BTM, CAN_FRAME_TIME_SYNC_RSP, sn),
|
||||
//FId: *NewCanFrameId(0x00, 0x00, 0x35, sn),
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -74,8 +77,21 @@ func (f *BtmTimeSyncCheckFrame) Decode(cf *CanetFrame) bool {
|
||||
//
|
||||
return true
|
||||
}
|
||||
|
||||
func (f *BtmTimeSyncCheckFrame) EncodeBtmAtp() *BtmHeadFrame {
|
||||
cf := &BtmHeadFrame{}
|
||||
cf.CanId = f.FId
|
||||
cf.CanLen = 8
|
||||
data := make([]byte, 0)
|
||||
data = binary.BigEndian.AppendUint32(data, f.T2)
|
||||
data = binary.BigEndian.AppendUint32(data, f.T3)
|
||||
cf.CanData = data
|
||||
|
||||
return cf
|
||||
}
|
||||
func (f *BtmTimeSyncCheckFrame) Encode() *CanetFrame {
|
||||
cf := &CanetFrame{}
|
||||
|
||||
cf.CanId = f.FId
|
||||
cf.CanLen = 8
|
||||
cf.FF = true
|
||||
|
66
third_party/message/can_bus.go
vendored
66
third_party/message/can_bus.go
vendored
@ -1,6 +1,8 @@
|
||||
package message
|
||||
|
||||
import "github.com/snksoft/crc"
|
||||
import (
|
||||
"github.com/snksoft/crc"
|
||||
)
|
||||
|
||||
/////////////////////CAN串行总线 bit 流处理 ////////////////////////
|
||||
|
||||
@ -199,47 +201,53 @@ const (
|
||||
CAN_BITS_LEN8 = byte(0b1111_1111)
|
||||
)
|
||||
|
||||
///////////////////// CRC ////////////////////////
|
||||
|
||||
// const (
|
||||
// CAN_CRC16_ATPREQ = 0x11021
|
||||
// CAN_CRC32 = 0x04C11DB7
|
||||
// )
|
||||
|
||||
var (
|
||||
CAN_CRC16 = crc.CCITT
|
||||
CAN_CRC32 = &crc.Parameters{Width: 32, Polynomial: 0x4A503DF1, Init: 0x00FF0000, ReflectIn: true, ReflectOut: true, FinalXor: 0xFFFFFFFF}
|
||||
CAN_CRC32 = &crc.Parameters{Width: 32, Polynomial: 0x4A503DF1, Init: 0x00FF0000, ReflectIn: false, ReflectOut: false, FinalXor: 0x000000}
|
||||
ATP_TIMEA = crc.CRC32
|
||||
ATP_TIMEB = crc.Castagnoli
|
||||
ATP_REQ = crc.XMODEM
|
||||
)
|
||||
|
||||
func Can_Crc16(data []byte) uint16 {
|
||||
return uint16(crc.CalculateCRC(CAN_CRC16, data))
|
||||
}
|
||||
func Can_Crc16BtmReq(data []byte) uint16 {
|
||||
return uint16(crc.CalculateCRC(ATP_REQ, data))
|
||||
}
|
||||
|
||||
func Can_Crc32(data []byte) uint32 {
|
||||
return uint32(crc.CalculateCRC(CAN_CRC32, data))
|
||||
}
|
||||
func Can_Crc32A(data []byte) uint32 {
|
||||
return uint32(crc.CalculateCRC(ATP_TIMEA, data))
|
||||
}
|
||||
func Can_Crc32B(data []byte) uint32 {
|
||||
return uint32(crc.CalculateCRC(ATP_TIMEB, data))
|
||||
}
|
||||
|
||||
// var (
|
||||
// crc16AtpReqTable []uint32 = nil
|
||||
// crc32CanTable []uint32 = nil
|
||||
// )
|
||||
//var (
|
||||
// crc16AtpReqTable []uint32 = nil
|
||||
// crc32CanTable []uint32 = nil
|
||||
//)
|
||||
|
||||
// func CreateCanCrcTable() {
|
||||
// if crc16AtpReqTable == nil {
|
||||
// crc16AtpReqTable = CreateCrcTable(CAN_CRC16_ATPREQ, 16, false)
|
||||
// }
|
||||
// if crc32CanTable == nil {
|
||||
// crc32CanTable = CreateCrcTable(CAN_CRC32, 32, false)
|
||||
// }
|
||||
// }
|
||||
// func calculateAtpReqCrc16(data []byte) uint16 {
|
||||
// crc := CrcTableBased(data, 16, 0, false, false, 0, crc16AtpReqTable)
|
||||
// return uint16(crc)
|
||||
// }
|
||||
// func calculateCanCrc32(data []byte) uint32 {
|
||||
// crc := CrcTableBased(data, 32, 0xFF_FF_FF_FF, false, false, 0, crc32CanTable)
|
||||
// return crc
|
||||
// }
|
||||
// func CreateCanCrcTable() {
|
||||
// if crc16AtpReqTable == nil {
|
||||
// crc16AtpReqTable = CreateCrcTable(CAN_CRC16_ATPREQ, 16, false)
|
||||
// }
|
||||
// if crc32CanTable == nil {
|
||||
// crc32CanTable = CreateCrcTable(CAN_CRC32, 32, false)
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// func calculateAtpReqCrc16(data []byte) uint16 {
|
||||
// crc := CrcTableBased(data, 16, 0, false, false, 0, crc16AtpReqTable)
|
||||
// return uint16(crc)
|
||||
// }
|
||||
//func calculateCanCrc32(data []byte) uint32 {
|
||||
// crc := CrcTableBased(data, 32, 0xFF_FF_FF_FF, false, false, 0, crc32CanTable)
|
||||
// return crc
|
||||
//}
|
||||
|
||||
// // CRC32A的校验范围是:报文+时间戳A
|
||||
// func calculateDataRspCrc32A(data []byte) uint32 {
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user