Compare commits

..

244 Commits

Author SHA1 Message Date
167e518b70 [修改]11号线驾驶台通信逻辑修改 2024-11-22 13:18:15 +08:00
3203edf906 [修改]11号线BTM与VOBC通信服务逻辑 2024-11-19 10:48:13 +08:00
5735f54b30 [重写]11号线BTM与VOBC通信服务 2024-11-15 09:52:20 +08:00
64a5989b71 [新增]11号线驾驶台与工装通信服务 2024-11-05 13:59:46 +08:00
cc71c128cf Merge remote-tracking branch 'origin/develop' into develop 2024-10-21 14:48:13 +08:00
7ee68a4ae8 [修改]11号线信号机联锁通信bug 2024-10-21 14:47:58 +08:00
tiger_zhou
f43de7bf71 列车连接三方服务调整
All checks were successful
local-test分支打包构建docker并发布运行 / Docker-Build (push) Successful in 1m55s
2024-10-17 17:06:57 +08:00
tiger_zhou
c2fc8f61dd 过/欠标调整
Some checks failed
local-test分支打包构建docker并发布运行 / Docker-Build (push) Has been cancelled
2024-10-17 16:54:50 +08:00
tiger_zhou
27322532e0 列车动力学添加车组,仿真内添加列车添加车组
All checks were successful
local-test分支打包构建docker并发布运行 / Docker-Build (push) Successful in 1m51s
2024-10-15 09:16:35 +08:00
tiger_zhou
c6fe82c7e6 Merge remote-tracking branch 'origin/develop' into develop
All checks were successful
local-test分支打包构建docker并发布运行 / Docker-Build (push) Successful in 19m32s
2024-10-09 18:05:20 +08:00
tiger_zhou
0dd0d5db67 权限不足添加对应的错误code,所有http get请求直接放过 2024-10-09 18:02:42 +08:00
54e2d88db3 [新增]11号线联锁、12号线联锁、12号计轴第三方通信服务的状态采集
Some checks failed
local-test分支打包构建docker并发布运行 / Docker-Build (push) Failing after 5m19s
2024-10-09 17:38:52 +08:00
tiger_zhou
0921835d27 车载不接收动力学速度为负数 2024-10-08 18:17:45 +08:00
81c36c6fb1 Merge remote-tracking branch 'origin/develop' into develop
All checks were successful
local-test分支打包构建docker并发布运行 / Docker-Build (push) Successful in 18m19s
2024-09-30 16:24:16 +08:00
735423ca67 [修改]11号线通信服务结束时,调用服务上下文中的cancelFunc以结束死循环协程 2024-09-30 16:24:02 +08:00
tiger_zhou
76183883da 动力学初始化停车点数据 2024-09-29 09:33:00 +08:00
tiger_zhou
2463edd996 停车点构建proto 2024-09-25 11:19:18 +08:00
3a22950790 [修改]11号线联锁通信逻辑忽略配置中的集中站,全线通信;创建仿真时,通过设置继电器状态,初始化道岔位置到定位 2024-09-18 11:40:20 +08:00
tiger_zhou
c6cfc42774 列车连接三方服务调整 2024-09-14 10:12:46 +08:00
tiger_zhou
15bdc45892 列车折返rm模式调整
All checks were successful
local-test分支打包构建docker并发布运行 / Docker-Build (push) Successful in 2m58s
2024-09-11 21:19:42 +08:00
tiger_zhou
449162856b 折返速度调整 2024-09-11 13:28:56 +08:00
tiger_zhou
8b5ef2e597 列车换端计算,里程ato调整 2024-09-10 18:55:15 +08:00
tiger_zhou
903fd14a61 列车折返12号线激活调整 2024-09-10 18:05:04 +08:00
tiger_zhou
9809df4b27 折返 2024-09-10 18:00:44 +08:00
tiger_zhou
9a5e8be378 折返 2024-09-10 16:12:39 +08:00
tiger_zhou
f144f29386 折返 2024-09-10 15:51:59 +08:00
tiger_zhou
efe1ff69c9 Merge branch 'develop' of http://120.46.212.6:3000/joylink/rts-sim-testing-service into develop_t
# Conflicts:
#	rts-sim-module
2024-09-10 15:38:31 +08:00
tiger_zhou
f9ffa1cf10 折返 2024-09-10 15:37:40 +08:00
3d66efe8f4 [修改]11号线模式口采集和驱动逻辑调通 2024-09-05 15:14:39 +08:00
tiger_zhou
39b4ffb633 列车11号线btm调整 2024-09-05 09:22:19 +08:00
tiger_zhou
f485e7a639 列车11号线btm调整 2024-09-05 09:21:49 +08:00
tiger_zhou
cf3d38a2f5 列车驾驶ato牵引,制动初始版,添加列车紧急制动属性,添加ato开关门指示灯
Some checks failed
local-test分支打包构建docker并发布运行 / Docker-Build (push) Failing after 3m4s
2024-08-30 18:08:14 +08:00
tiger_zhou
13baeda0a0 列车模型调整
All checks were successful
local-test分支打包构建docker并发布运行 / Docker-Build (push) Successful in 3m55s
2024-08-29 17:18:13 +08:00
tiger_zhou
d1504e7bf0 列车12号线操控调整
All checks were successful
local-test分支打包构建docker并发布运行 / Docker-Build (push) Successful in 2m5s
2024-08-29 16:09:07 +08:00
6c1ef600a8 Merge remote-tracking branch 'origin/develop' into develop 2024-08-28 11:32:47 +08:00
2af5007698 [修改]11号线联锁通信暂时提交 2024-08-28 11:32:27 +08:00
tiger_zhou
c50b1b640d 列车操控
All checks were successful
local-test分支打包构建docker并发布运行 / Docker-Build (push) Successful in 1m53s
2024-08-27 11:58:08 +08:00
dcdb4610f8 [修改]移除屏蔽门继电器的自保持逻辑(因为自保持会导致12号线联锁宕机) 2024-08-26 11:23:51 +08:00
a165321c39 [新增]设备公里表长短链校正 2024-08-22 14:03:33 +08:00
tiger_zhou
fa600673a6 列车控制支持门模式更新
All checks were successful
local-test分支打包构建docker并发布运行 / Docker-Build (push) Successful in 3m15s
2024-08-22 13:12:03 +08:00
f8517f0ead [修改]暂时注释掉12号线联锁和计轴的大段日志 2024-08-21 09:18:55 +08:00
tiger_zhou
0de305b397 列车操控调整 2024-08-16 15:33:30 +08:00
tiger_zhou
15c015a9a5 列车应答器调整 2024-08-15 14:54:16 +08:00
eae1b8d6df [bug]之前,在仿真运行时联锁配置中的code被替换为了车站uid,后来我只是简单将此转换去掉了,导致有些将此code当uid使用的场景出错 2024-08-15 14:43:20 +08:00
tiger_zhou
270f5a9772 列车btm调整重构
All checks were successful
local-test分支打包构建docker并发布运行 / Docker-Build (push) Successful in 1m41s
2024-08-13 17:33:57 +08:00
tiger_zhou
30c932f178 列车btm调整重构 2024-08-13 17:32:49 +08:00
98310f427b Merge branch 'local-test' into develop 2024-08-13 11:01:02 +08:00
5e70ad158e [修改]11号线联锁通信,区段状态从物理区段改为计轴区段
Some checks failed
local-test分支打包构建docker并发布运行 / Docker-Build (push) Failing after 50s
2024-08-13 11:00:27 +08:00
tiger_zhou
06a4331135 应答器修改 2024-08-13 09:09:25 +08:00
4044b7f4c2 [新增]计轴区段的模型、ecs实体、ecs系统、发给前端的状态
All checks were successful
local-test分支打包构建docker并发布运行 / Docker-Build (push) Successful in 1m44s
2024-08-12 18:57:36 +08:00
016ce0e8bb [修改]设备状态proto消息中增加计轴区段状态,原“所有区段状态”改为物理区段状态“ 2024-08-12 15:18:52 +08:00
tiger_zhou
272274a5c4 列车应答器添加可变报文,修改列车初始化制动力及列车行驶制动力
All checks were successful
local-test分支打包构建docker并发布运行 / Docker-Build (push) Successful in 2m54s
2024-08-09 16:41:41 +08:00
85d5781122 [bug]现在ESB已经不是按钮了,去掉状态采集;构建repo时没有对站台方向赋值 2024-08-09 11:38:53 +08:00
3da710891e [bug]ESB模型构建bug 2024-08-09 11:19:19 +08:00
d706a42b1b [清理]删除几处打印逻辑 2024-08-08 13:38:56 +08:00
f1efd5a2f2 [补充]12号线联锁通信应答器数据解析;
[修改]联锁配置中的车站编号不再改为车站uid
2024-08-08 13:23:25 +08:00
2d38a7bd63 [bug]uid和继电器编号对不上导致的缺继电器问题 2024-08-07 16:46:18 +08:00
844560ae1e Merge remote-tracking branch 'origin/develop' into develop
# Conflicts:
#	rts-sim-module
2024-08-07 14:54:37 +08:00
5fa5345981 [补充]11号线联锁通信 2024-08-07 14:53:57 +08:00
soul-walker
58ebf3842a 实现应答器1023报文和830报文的编解码 2024-08-07 14:40:49 +08:00
tiger_zhou
6a0a47083f 列车控制修复“测速测距”失败问题调整 2024-08-07 10:23:44 +08:00
tiger_zhou
bedb2bf413 列车控制修复“测速测距”失败问题 2024-08-07 09:31:46 +08:00
tiger_zhou
961b9dc5ff 列车控制连接版本记录1 2024-08-05 16:32:21 +08:00
fc6b51cc24 [bug]信号机电路系统逻辑没有正确处理驱动状态
All checks were successful
local-test分支打包构建docker并发布运行 / Docker-Build (push) Successful in 5m29s
2024-08-01 14:25:45 +08:00
5433ae43d6 [重写]计轴的组件、实体、系统、预复位/复位逻辑 2024-07-31 17:08:54 +08:00
afaf9d64c7 [修改]北京12号线连锁rsd、sse、ssr编解码算法及交互流程 2024-07-29 13:53:46 +08:00
tiger_zhou
8a34935d0f 列车车载应答器调试初始 2024-07-25 14:55:46 +08:00
72ca826be2 Merge remote-tracking branch 'origin/develop' into develop 2024-07-24 18:27:51 +08:00
7e38a912d4 [修改]crc32校验逻辑 2024-07-24 18:27:41 +08:00
tiger_zhou
ff67c84f18 列车控制连接多车调整,连接状态显示调整
All checks were successful
local-test分支打包构建docker并发布运行 / Docker-Build (push) Successful in 4m7s
2024-07-19 15:24:28 +08:00
ccaf53ec67 [修改]北京12号线连锁rsd、sse、ssr编解码算法 2024-07-17 15:44:47 +08:00
tiger_zhou
2fa2e90d57 列车控制连接,重新连接调整 2024-07-15 15:00:33 +08:00
tiger_zhou
037cf2f1be 12号线列车控制基本调试 2024-07-11 14:58:34 +08:00
aa04a9fbf4 [bug]北京12号线计轴通信编解码bug 2024-07-11 09:20:51 +08:00
8a6de531c8 [修改]北京12号线计轴通信配置 2024-07-08 16:14:26 +08:00
4bdd91c18b [修改]北京12号线计轴通信crc、svc等编码及校验逻辑 2024-07-08 10:33:19 +08:00
tiger_zhou
b8dc207a0e 列车控制消息
All checks were successful
local-test分支打包构建docker并发布运行 / Docker-Build (push) Successful in 1m48s
2024-07-04 17:39:56 +08:00
tiger_zhou
1a69bee090 列车控制消息
All checks were successful
local-test分支打包构建docker并发布运行 / Docker-Build (push) Successful in 1m44s
2024-07-04 17:28:02 +08:00
tiger_zhou
a0cf17c845 列车控制消息 2024-07-04 17:26:46 +08:00
tiger_zhou
0fe48de228 列车控制连接
All checks were successful
local-test分支打包构建docker并发布运行 / Docker-Build (push) Successful in 2m9s
2024-07-04 14:47:50 +08:00
tiger_zhou
0947fd9b73 列车控制btn调整 2024-07-04 14:47:24 +08:00
731aea8c8d [重写]北京12号线计轴通信交互逻辑(未完,主要是安全校验域计算逻辑) 2024-07-04 13:59:39 +08:00
tiger_zhou
1166d7ffe5 列车调整连接12号线车载,静态情况 2024-07-04 09:26:37 +08:00
6ed9aec51a [新增]继电器设置初始位置逻辑
All checks were successful
local-test分支打包构建docker并发布运行 / Docker-Build (push) Successful in 2m51s
2024-06-28 11:10:10 +08:00
fe49cd4665 更新引用的镜像从国内镜像源拉
All checks were successful
local-test分支打包构建docker并发布运行 / Docker-Build (push) Successful in 2m46s
2024-06-27 17:45:42 +08:00
tiger_zhou
df0c9f77c8 仿真列车模拟量输出修改
Some checks failed
local-test分支打包构建docker并发布运行 / Docker-Build (push) Failing after 2m6s
2024-06-27 10:37:52 +08:00
ff7abeb414 [修改]车站EMP继电器组合缺少蜂鸣器等元素时不再报错;
[新增]车站电子元件组合构建逻辑
2024-06-26 17:27:48 +08:00
d3e9041760 [回滚]成工院本地配置
Some checks failed
local-test分支打包构建docker并发布运行 / Docker-Build (push) Failing after 2m7s
2024-06-21 19:48:44 +08:00
d610a15fdb [新增]ecs物理区段增加区段电路组件及逻辑;
[修改]12号线联锁通信服务如果启动失败,则启动参数无法更换bug
2024-06-21 15:53:17 +08:00
tiger_zhou
6211f61ecd 日志调整及现场测试 2024-06-18 17:10:21 +08:00
2a380347c6 [修改]日志中的堆栈信息无换行问题 2024-06-16 12:26:48 +08:00
tiger_zhou
815b181a0c 日志调整及现场测试 2024-06-14 16:48:59 +08:00
tiger_zhou
8aebff96a5 日志调整及现场测试
All checks were successful
local-test分支打包构建docker并发布运行 / Docker-Build (push) Successful in 1m41s
2024-06-14 13:19:04 +08:00
38b1a79633 [bug]半实物服务意料之外的报错 2024-06-14 12:17:08 +08:00
0018492f8d Merge remote-tracking branch 'origin/develop' into develop 2024-06-14 11:37:25 +08:00
1a114a9c44 [bug]12号线联锁通信服务TCP连接失败后再次启动会提示任务已启动 2024-06-14 11:37:15 +08:00
tiger_zhou
cbd2c4a0ab 日志调整及现场测试
All checks were successful
local-test分支打包构建docker并发布运行 / Docker-Build (push) Successful in 1m45s
2024-06-14 09:21:51 +08:00
807bce391c [修改]平西府配置文件;
[新增]11号线联锁数据整理(未完)
2024-06-13 18:35:17 +08:00
tiger_zhou
1b3f130e4e 日志调整及现场测试
All checks were successful
local-test分支打包构建docker并发布运行 / Docker-Build (push) Successful in 1m45s
2024-06-13 18:13:21 +08:00
tiger_zhou
081581140e Merge branch 'develop' into local-test
All checks were successful
local-test分支打包构建docker并发布运行 / Docker-Build (push) Successful in 1m38s
# Conflicts:
#	third_party/interlock/beijing12/interlock.go
2024-06-13 14:17:17 +08:00
tiger_zhou
bec948357a 日志调整及现场测试 2024-06-13 14:16:52 +08:00
bff5c61cf4 [修改]12号线通信方式改为TCP 2024-06-13 11:10:42 +08:00
1afa01bffb Merge remote-tracking branch 'origin/develop' into develop 2024-06-13 10:25:13 +08:00
tiger_zhou
a808f8f73e 日志调整及现场测试 2024-06-13 10:06:54 +08:00
e2c537d381 Merge remote-tracking branch 'origin/develop' into develop 2024-06-13 09:07:35 +08:00
tiger_zhou
35920fbfeb 日志调整及现场测试 2024-06-12 19:50:36 +08:00
f1e406a678 Merge remote-tracking branch 'origin/develop' into develop 2024-06-12 17:49:13 +08:00
518c3d3f6e [新增]11号线联锁通信逻辑(未完);
[修改]proto编译插件指定版本
2024-06-12 17:49:02 +08:00
tiger_zhou
8a6b5a0145 Merge branch 'develop' into local-test
All checks were successful
local-test分支打包构建docker并发布运行 / Docker-Build (push) Successful in 1m45s
2024-06-12 17:46:02 +08:00
tiger_zhou
a8c2a61106 日志调整及现场测试 2024-06-12 17:45:47 +08:00
tiger_zhou
63032efdba Merge branch 'develop' into local-test
All checks were successful
local-test分支打包构建docker并发布运行 / Docker-Build (push) Successful in 1m36s
2024-06-12 16:23:03 +08:00
tiger_zhou
3bf7a146af 日志调整及现场测试 2024-06-12 16:22:31 +08:00
tiger_zhou
cdfffc65e8 Merge branch 'develop' into local-test
All checks were successful
local-test分支打包构建docker并发布运行 / Docker-Build (push) Successful in 1m30s
2024-06-12 14:33:28 +08:00
tiger_zhou
650488e1c7 日志调整及现场测试 2024-06-12 14:26:47 +08:00
tiger_zhou
c22ddfcae9 Merge branch 'develop' into local-test
All checks were successful
local-test分支打包构建docker并发布运行 / Docker-Build (push) Successful in 2m29s
# Conflicts:
#	third_party/btm_vobc/btm_vobc.go
2024-06-07 13:27:46 +08:00
tiger_zhou
ed4dc9a224 修改列车删除或更改连接时电机继续转动的情况 2024-06-07 13:26:46 +08:00
tiger_zhou
31180597a5 btm vobc 模块功能 2024-06-06 18:56:25 +08:00
c23c70ce05 [修改]12号线联锁通信增加日志;
All checks were successful
local-test分支打包构建docker并发布运行 / Docker-Build (push) Successful in 1m37s
[bug]12号线联锁驱动字节解析bug
2024-06-06 18:40:48 +08:00
tiger_zhou
a58a4350c2 btm vobc 模块功能 2024-06-06 17:57:30 +08:00
tiger_zhou
e7b7ff6781 日志调整及现场测试
All checks were successful
local-test分支打包构建docker并发布运行 / Docker-Build (push) Successful in 1m42s
2024-06-04 16:19:41 +08:00
497bbad8ed [修改]11号线联锁数据解析删除洗车机数据;删除紧急停车后两个命令
All checks were successful
local-test分支打包构建docker并发布运行 / Docker-Build (push) Successful in 1m37s
2024-05-31 14:25:59 +08:00
0a25ee3170 [修改]11号线联锁数据解析删除数据长度校验
All checks were successful
local-test分支打包构建docker并发布运行 / Docker-Build (push) Successful in 1m41s
2024-05-30 16:58:02 +08:00
a83952f7b4 [修改]11号线联锁解析err类型
All checks were successful
local-test分支打包构建docker并发布运行 / Docker-Build (push) Successful in 1m34s
2024-05-29 17:23:31 +08:00
cac309f4b1 [新增]11号线联锁通信所属线路配置;11号线联锁通信原始数据及解析后的json数据日志
All checks were successful
local-test分支打包构建docker并发布运行 / Docker-Build (push) Successful in 1m31s
2024-05-29 17:20:51 +08:00
tiger_zhou
61e60e4e64 Merge branch 'develop' into local-test
All checks were successful
local-test分支打包构建docker并发布运行 / Docker-Build (push) Successful in 1m31s
2024-05-29 15:19:19 +08:00
tiger_zhou
099cb89bbb 日志调整及现场测试 2024-05-29 15:19:04 +08:00
tiger_zhou
5627ca5315 Merge branch 'develop' into local-test
All checks were successful
local-test分支打包构建docker并发布运行 / Docker-Build (push) Successful in 1m31s
2024-05-29 13:55:35 +08:00
tiger_zhou
c04c0afba5 日志调整及现场测试 2024-05-29 13:55:21 +08:00
tiger_zhou
fc1c773298 Merge branch 'develop' into local-test
All checks were successful
local-test分支打包构建docker并发布运行 / Docker-Build (push) Successful in 1m37s
2024-05-29 13:35:17 +08:00
tiger_zhou
3a7795c875 列车btm vobc 连接功能 2024-05-29 13:34:50 +08:00
tiger_zhou
70da3d6dfd Merge branch 'develop' into local-test
All checks were successful
local-test分支打包构建docker并发布运行 / Docker-Build (push) Successful in 1m36s
2024-05-29 12:10:14 +08:00
tiger_zhou
550218e9e0 日志调整及现场测试 2024-05-29 12:10:01 +08:00
tiger_zhou
3dcafa50e8 Merge branch 'develop' into local-test
All checks were successful
local-test分支打包构建docker并发布运行 / Docker-Build (push) Successful in 1m43s
2024-05-29 11:48:25 +08:00
tiger_zhou
d529c283f2 日志调整及现场测试 2024-05-29 11:48:04 +08:00
tiger_zhou
bc6e0369a1 Merge branch 'develop' into local-test
All checks were successful
local-test分支打包构建docker并发布运行 / Docker-Build (push) Successful in 1m38s
2024-05-27 14:59:38 +08:00
tiger_zhou
5df2b1c810 加速度计编码调整,pc仿真添加日志 2024-05-27 14:59:17 +08:00
8bb725f234 Merge remote-tracking branch 'origin/local-test' into local-test
All checks were successful
local-test分支打包构建docker并发布运行 / Docker-Build (push) Successful in 1m36s
2024-05-27 10:18:05 +08:00
c919c59b49 Merge branch 'develop' into local-test 2024-05-27 10:17:49 +08:00
91e63a9763 [修改]联锁通信日志级别;采集状态发送频率 2024-05-27 10:17:29 +08:00
tiger_zhou
4b9e398f67 Merge branch 'develop' into local-test
All checks were successful
local-test分支打包构建docker并发布运行 / Docker-Build (push) Successful in 1m30s
2024-05-24 09:01:24 +08:00
tiger_zhou
088740408e Merge remote-tracking branch 'origin/develop' into develop 2024-05-24 09:00:53 +08:00
tiger_zhou
6b5932c617 列车pc仿真调整支持多个 2024-05-24 09:00:43 +08:00
5d232cf4e8 Merge branch 'develop' into local-test
All checks were successful
local-test分支打包构建docker并发布运行 / Docker-Build (push) Successful in 1m55s
2024-05-23 19:21:26 +08:00
9fbc60e93a [修改]联锁通信日志格式 2024-05-23 19:19:51 +08:00
42f299f553 [bug]修改联锁通信逻辑中的编码bug 2024-05-23 19:14:10 +08:00
tiger_zhou
a4b761f720 Merge remote-tracking branch 'origin/develop' into develop 2024-05-23 19:09:37 +08:00
tiger_zhou
fca6610047 列车pc仿真调整 2024-05-23 19:08:46 +08:00
fd23150725 Merge remote-tracking branch 'origin/local-test' into local-test
Some checks failed
local-test分支打包构建docker并发布运行 / Docker-Build (push) Failing after 47s
2024-05-23 16:36:21 +08:00
c700a78613 Merge branch 'develop' into local-test 2024-05-23 16:35:01 +08:00
78c55b3b21 [+log]增加联锁通信收到及发送数据的日志 2024-05-23 16:34:22 +08:00
tiger_zhou
8b9c8a6156 列车pc仿真调整 2024-05-23 16:17:12 +08:00
tiger_zhou
736052740a Merge branch 'develop' into local-test
All checks were successful
local-test分支打包构建docker并发布运行 / Docker-Build (push) Successful in 1m39s
2024-05-20 18:00:02 +08:00
tiger_zhou
968a46fbe7 列车pc仿真调整 2024-05-20 17:59:40 +08:00
tiger_zhou
e7f6f8e74d Merge branch 'develop' into local-test
All checks were successful
local-test分支打包构建docker并发布运行 / Docker-Build (push) Successful in 1m30s
2024-05-20 17:23:50 +08:00
tiger_zhou
531950f272 列车删除调整 2024-05-20 17:23:22 +08:00
tiger_zhou
2569265a1f Merge branch 'develop' into local-test
All checks were successful
local-test分支打包构建docker并发布运行 / Docker-Build (push) Successful in 2m42s
2024-05-20 15:23:20 +08:00
tiger_zhou
06f8445a98 现场加速度计测试连接 2024-05-20 15:22:58 +08:00
tiger_zhou
8d28ac1d59 Merge branch 'develop' into local-test
Some checks failed
local-test分支打包构建docker并发布运行 / Docker-Build (push) Has been cancelled
2024-05-20 15:22:14 +08:00
tiger_zhou
11a14b2ef6 列车激活方向调整 2024-05-20 15:21:22 +08:00
37799cd2c9 [修改]区段、Link、运营方向/位置之间互相转换的函数的bug 2024-05-17 14:39:26 +08:00
b3270c5921 [修改]区段、Link、运营方向/位置之间互相转换的函数的bug 2024-05-17 14:38:14 +08:00
59520435d2 [新增]区段、Link、运营方向/位置之间互相转换的函数 2024-05-16 21:19:53 +08:00
149678b3f0 Merge remote-tracking branch 'origin/develop' into develop 2024-05-13 13:17:43 +08:00
509d06e39e [新增]增加GJ继电器相关逻辑 2024-05-13 13:17:16 +08:00
tiger_zhou
d24717c13a Merge branch 'develop' into local-test
All checks were successful
local-test分支打包构建docker并发布运行 / Docker-Build (push) Successful in 1m51s
2024-05-10 08:24:05 +08:00
tiger_zhou
a6119b890f 列车控制调整 2024-05-10 08:23:50 +08:00
tiger_zhou
3bf98d1ddf Merge branch 'develop' into local-test
All checks were successful
local-test分支打包构建docker并发布运行 / Docker-Build (push) Successful in 1m52s
2024-05-09 14:36:44 +08:00
tiger_zhou
6fa4c6e6a9 列车控制调整 2024-05-09 14:36:13 +08:00
tiger_zhou
0b25327b28 列车控制,方向设置“0”问题调整 2024-05-08 14:39:23 +08:00
tiger_zhou
7ed8fcbed7 列车pc仿真调整 2024-05-08 10:00:53 +08:00
tiger_zhou
8136167d8e Merge branch 'develop' into local-test
All checks were successful
local-test分支打包构建docker并发布运行 / Docker-Build (push) Successful in 3m18s
2024-05-07 18:08:15 +08:00
tiger_zhou
a298e11da2 列车pc仿真调整 2024-05-07 15:09:08 +08:00
tiger_zhou
4f8eab5a7a 列车pc仿真调整 上下行方向,紧急停车 2024-05-07 15:00:59 +08:00
tiger_zhou
398d1f1658 mqtt 连接等待 2024-05-07 10:52:29 +08:00
4ccd5a3cab 调整docker run开放端口 2024-04-30 11:47:37 +08:00
tiger_zhou
5da5e324e4 Merge branch 'develop' into local-test
All checks were successful
local-test分支打包构建docker并发布运行 / Docker-Build (push) Successful in 1m32s
2024-04-30 10:15:39 +08:00
tiger_zhou
34aa20ca65 列车默认值 2024-04-30 10:15:20 +08:00
tiger_zhou
7fc152ad67 Merge remote-tracking branch 'origin/local-test' into local-test
All checks were successful
local-test分支打包构建docker并发布运行 / Docker-Build (push) Successful in 3m18s
2024-04-29 14:22:03 +08:00
3c018ddad3 【修改】成工院本地部署配置 2024-04-29 14:21:16 +08:00
tiger_zhou
4b77a87785 仿真列车连接调整 2024-04-29 13:58:14 +08:00
tiger_zhou
39f01066ad Merge remote-tracking branch 'origin/local-test' into local-test
Some checks failed
local-test分支打包构建docker并发布运行 / Docker-Build (push) Failing after 3m10s
2024-04-28 11:01:40 +08:00
tiger_zhou
4901c00199 仿真测试调试 2024-04-28 11:01:12 +08:00
a67b02c389 【修改】成工院本地部署配置
All checks were successful
local-test分支打包构建docker并发布运行 / Docker-Build (push) Successful in 1m49s
2024-04-19 18:02:21 +08:00
tiger_zhou
20f9ac94c9 列车pc仿真调整 2024-04-19 16:55:51 +08:00
tiger_zhou
a13a01f99f 列车pc仿真调整 2024-04-18 11:18:26 +08:00
tiger_zhou
4666c84860 列车pc仿真调整 2024-04-18 11:14:05 +08:00
tiger_zhou
29fc4e265b 列车pc仿真调整
All checks were successful
local-test分支打包构建docker并发布运行 / Docker-Build (push) Successful in 1m37s
2024-04-17 09:32:25 +08:00
tiger_zhou
5a7b371005 列车pc仿真调整
All checks were successful
local-test分支打包构建docker并发布运行 / Docker-Build (push) Successful in 1m37s
2024-04-16 18:03:02 +08:00
tiger_zhou
92d21d559f 列车pc仿真调整
All checks were successful
local-test分支打包构建docker并发布运行 / Docker-Build (push) Successful in 1m55s
2024-04-16 17:26:37 +08:00
tiger_zhou
117ff89562 列车pc仿真调整
All checks were successful
local-test分支打包构建docker并发布运行 / Docker-Build (push) Successful in 1m33s
2024-04-15 09:51:02 +08:00
tiger_zhou
d45752ddb5 列车pc仿真调整
All checks were successful
local-test分支打包构建docker并发布运行 / Docker-Build (push) Successful in 1m40s
2024-04-15 08:44:37 +08:00
tiger_zhou
e0b0ae4566 列车pc仿真调整
All checks were successful
local-test分支打包构建docker并发布运行 / Docker-Build (push) Successful in 1m43s
2024-04-13 09:40:25 +08:00
43022220a0 233发布docker运行暴露4000端口 2024-04-11 10:32:08 +08:00
tiger_zhou
8405d9619d 列车pc仿真调整
All checks were successful
local-test分支打包构建docker并发布运行 / Docker-Build (push) Successful in 1m45s
2024-04-11 08:55:34 +08:00
tiger_zhou
e011e5d409 列车pc仿真调整 2024-04-11 08:55:12 +08:00
tiger_zhou
225279dbd2 列车pc仿真调整
All checks were successful
local-test分支打包构建docker并发布运行 / Docker-Build (push) Successful in 1m41s
2024-04-09 10:40:18 +08:00
tiger_zhou
7de95e55f2 列车pc仿真测试调整
All checks were successful
local-test分支打包构建docker并发布运行 / Docker-Build (push) Successful in 1m35s
2024-04-09 08:55:33 +08:00
a24ae0a14e 【bug】mqtt没有正确发送PSL状态
All checks were successful
local-test分支打包构建docker并发布运行 / Docker-Build (push) Successful in 1m32s
2024-04-08 09:25:06 +08:00
b394b3128c 【新增】mqtt发送车库门门旁路状态 2024-04-07 14:48:32 +08:00
1facbfeb2b 【bug】车库门强制开关门无效;洗车机取消故障后依然有紧急停车
All checks were successful
local-test分支打包构建docker并发布运行 / Docker-Build (push) Successful in 1m38s
2024-04-07 13:38:30 +08:00
3ef9f59184 【bug】重新编译proto文件
All checks were successful
local-test分支打包构建docker并发布运行 / Docker-Build (push) Successful in 1m44s
2024-04-03 16:11:06 +08:00
859561b6a7 Merge remote-tracking branch 'origin/local-test' into local-test
# Conflicts:
#	dto/state_proto/device_state.pb.go
#	rts-sim-testing-message
2024-04-03 16:08:17 +08:00
3f91e6cbe7 【bug】车库门状态未发送
【新增】车库门增加本地/远程状态
2024-04-03 16:04:39 +08:00
tiger_zhou
1d77dd0fec Merge branch 'develop' into local-test
Some checks failed
local-test分支打包构建docker并发布运行 / Docker-Build (push) Failing after 1m2s
2024-04-02 18:29:32 +08:00
tiger_zhou
b4b2fc9e72 列车pc仿真 2024-04-02 18:29:00 +08:00
tiger_zhou
fddb10af77 Merge branch 'develop' into local-test
Some checks failed
local-test分支打包构建docker并发布运行 / Docker-Build (push) Failing after 38s
2024-04-02 18:25:10 +08:00
tiger_zhou
edf154afa3 列车pc仿真 2024-04-02 18:24:46 +08:00
tiger_zhou
224670fa15 Merge branch 'develop' into local-test
Some checks failed
local-test分支打包构建docker并发布运行 / Docker-Build (push) Failing after 40s
2024-04-02 18:20:30 +08:00
tiger_zhou
9b58348297 列车pc仿真 2024-04-02 18:20:10 +08:00
tiger_zhou
1221cd89d4 列车前端操作接口,列车连接三方映射接口及ws返回列车连接状态
Some checks failed
local-test分支打包构建docker并发布运行 / Docker-Build (push) Failing after 46s
2024-04-02 18:17:16 +08:00
tiger_zhou
dedca1e149 Merge branch 'develop' into local-test
Some checks failed
local-test分支打包构建docker并发布运行 / Docker-Build (push) Failing after 42s
2024-04-02 18:00:10 +08:00
tiger_zhou
089d62cfc2 列车控制修改列车参数类型 2024-04-02 17:42:27 +08:00
91bb4ecb46 Merge branch 'develop' into local-test
All checks were successful
local-test分支打包构建docker并发布运行 / Docker-Build (push) Successful in 1m40s
2024-04-02 16:00:22 +08:00
f12b72cc04 【bug】洗车机状态发送及洗车机操作 2024-04-02 15:59:55 +08:00
80b45be543 【新增】洗车机状态发送 2024-04-02 14:17:18 +08:00
49e3199c06 【新增】洗车机模型构建、ecs实体构建
【修改】洗车机ecs电路系统逻辑
2024-04-02 14:09:42 +08:00
a0cee6634e Merge branch 'develop' into local-test
All checks were successful
local-test分支打包构建docker并发布运行 / Docker-Build (push) Successful in 1m36s
2024-04-01 14:08:52 +08:00
c9dda72725 【新增】继电器组合的设备类型是防淹门时,视同车库门 2024-04-01 14:07:56 +08:00
9e5304a270 Merge branch 'develop' into local-test
All checks were successful
local-test分支打包构建docker并发布运行 / Docker-Build (push) Successful in 1m41s
2024-04-01 13:32:19 +08:00
3ae2f14a16 【新增】防淹门构建为车库门模型逻辑
【bug】当缺少继电器时,车库门的ecs系统逻辑和状态发送逻辑执行出错
2024-04-01 13:31:38 +08:00
32c575125f 【修改】车库门继电器状态丢失的故障由应用层实现;不再影响继电器的实际状态;也不再导致门故障继电器接通; 2024-03-29 16:46:31 +08:00
6f327f751b 【新增】防淹门模型构建(暂时与车库门用相同模型)
【修改】因车库门PSL盘对象被删除,修改相应的构建逻辑
2024-03-29 14:59:20 +08:00
24c0254c01 【bug】车库门ecs系统逻辑没有对MMSJ做处理 2024-03-27 17:11:24 +08:00
c004aa9e57 子模块同步 2024-03-27 16:45:25 +08:00
ffd989b13b Merge branch 'develop' of http://120.46.212.6:3000/joylink/rts-sim-testing-service into develop 2024-03-27 16:44:23 +08:00
772de0b017 调整任务及步骤名称
添加清理tag为none镜像步骤
2024-03-27 16:44:07 +08:00
3d8f41b309 【补档】
All checks were successful
CI / Docker-Build (push) Successful in 1m27s
2024-03-27 15:14:05 +08:00
237c6b5297 Merge branch 'develop' into local-test
All checks were successful
CI / Docker-Build (push) Successful in 2m37s
2024-03-27 14:19:43 +08:00
ef9e431afd 【bug】车库门操作、状态发送等逻辑中的bug 2024-03-27 14:15:35 +08:00
9d6acd9c20 【新增】测试模块车库门状态proto定义及车库门状态发送逻辑
【删除】ecs的车库门状态及相关逻辑
2024-03-26 16:48:37 +08:00
141cc633b9 【新增】车库门PSL模型构建及相关ecs逻辑;
【改动】车库门ecs逻辑
2024-03-26 13:12:17 +08:00
da98777b04 【新增】车库门及车库门控制盒操作接口 2024-03-20 14:35:35 +08:00
6f3154ec19 Merge branch 'develop' into local-test
All checks were successful
CI / Docker-Build (push) Successful in 2m8s
2024-03-19 21:25:49 +08:00
5856236b12 Merge branch 'master' into develop 2024-03-19 21:24:46 +08:00
4339ea2d73 submodule update 2024-03-19 21:24:06 +08:00
cf6db6ef40 更新local-setup-go版本,实现缓存go build依赖包
删除dockerfile赋予权限步骤
2024-03-19 16:27:57 +08:00
9cee828d0d 修改submodule url 2024-03-19 09:38:11 +08:00
3f64881096 Merge pull request '更新Dockerfile添加文件执行权限' (#4) from develop into local-test
All checks were successful
CI / Docker-Build (push) Successful in 1m55s
Reviewed-on: #4
2024-03-18 18:36:45 +08:00
77991dbbdd 更新Dockerfile添加文件执行权限 2024-03-18 18:34:30 +08:00
f176602179 Merge pull request '修改ci为local-test分支的CI,并添加SSH发布步骤' (#3) from develop into local-test
All checks were successful
CI / Docker-Build (push) Successful in 1m46s
Reviewed-on: #3
2024-03-18 18:15:16 +08:00
b123e8b921 修改ci为local-test分支的CI,并添加SSH发布步骤 2024-03-18 17:20:34 +08:00
accbe3c216 更新setup buildx版本为插件安装版本
All checks were successful
CI / Docker-Build (push) Successful in 2m4s
2024-03-18 14:31:47 +08:00
cc576f4840 添加设置docker buildx
Some checks failed
CI / Docker-Build (push) Failing after 1m43s
2024-03-18 13:57:08 +08:00
ab14155ebd 测试CI
Some checks failed
CI / Docker-Build (push) Failing after 1m24s
2024-03-18 10:50:40 +08:00
147 changed files with 19976 additions and 5328 deletions

View File

@ -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)

3
.gitignore vendored
View File

@ -26,4 +26,5 @@ output/
# Go workspace file
/go.work.sum
/pom.xml
/pom.xml
/bj-rtsts-server

6
.gitmodules vendored
View File

@ -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

View File

@ -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 ./

View File

@ -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

View File

@ -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{}))
}
// 解析环境配置结构

View File

@ -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)

View File

@ -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
View 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
View 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

View 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
}

View File

@ -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
View 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

View File

@ -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_116进制4字节"`
RemoteSid2 string `json:"remoteSid2" description:"联锁SID_216进制4字节"`
LocalSid1 string `json:"localSid1" description:"计轴SID_116进制4字节"`
LocalSid2 string `json:"localSid2" description:"计轴SID_216进制4字节"`
RemoteSinit1 string `json:"remoteSinit1" description:"联锁SINIT_116进制4字节"`
RemoteSinit2 string `json:"remoteSinit2" description:"联锁SINIT_216进制4字节"`
LocalSinit1 string `json:"localSinit1" description:"计轴SINIT_116进制4字节"`
LocalSinit2 string `json:"localSinit2" description:"计轴SINIT_216进制4字节"`
RemoteDataVer1 string `json:"remoteDataVer1" description:"联锁DATAVER_116进制4字节"`
RemoteDataVer2 string `json:"remoteDataVer2" description:"联锁DATAVER_216进制4字节"`
LocalDataVer1 string `json:"localDataVer1" description:"计轴DATAVER_116进制4字节"`
LocalDataVer2 string `json:"localDataVer2" description:"计轴DATAVER_216进制4字节"`
MaxDeviation int `json:"maxDeviation" description:"可容忍的最大时序偏差"`
WaitSSRTimeout int `json:"waitSSRTimeout" description:"等待SSR回应的定时器超时值ms"`
Period int `json:"period" description:"RSD发送周期ms"`
}
///////////////////////////////////////////////////////////////////////////////////////

View File

@ -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

View File

@ -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": [
{

View File

@ -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 (

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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,
},

View File

@ -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

View File

@ -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

View File

@ -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,

View 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()
}
}

View 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
View 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单位为mmt单位为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
View 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
View 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
View File

@ -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
View File

@ -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=

View File

@ -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=

View File

@ -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
View 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)
}

View 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")
}
}

View File

@ -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)
}

View File

@ -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
}

View File

@ -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 {

View File

@ -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)
}

View File

@ -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))
}
}

View File

@ -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)
}

View File

@ -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
}

View File

@ -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

View File

@ -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 {

View File

@ -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,

View File

@ -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,

View File

@ -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

View File

@ -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
}

View File

@ -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 {

View 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
}

View 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-70字节的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}
}

View 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)
}

View 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)
}

View 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
}

View 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))
}

View File

@ -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)
}
}

View File

@ -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}
}

View File

@ -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)
}

View File

@ -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
View 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")
}
}
}

View File

@ -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)
}

View File

@ -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
View 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
View 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
View 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~0xFFFFFFFF0不使用
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~0xFFFFFFFF0不使用
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 //上行自检码检测通道10位1正常0故障
selfCheckChannel2Fault bool //上行自检码检测通道21位1正常0故障
fskChannel1Fault bool //FSK连接线状态通道12位1正常0故障
fskChannel2Fault bool //FSK连接线状态通道23位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))
}

View 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
}

View File

@ -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()

View File

@ -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 {

View 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
}

View File

@ -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())
}

View File

@ -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

View 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))
}

View File

@ -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])
}

View File

@ -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})
}
}

View File

@ -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
View 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))
}

View 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
View 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" //屏蔽门长编组开门继电器索引
)

View 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
}

View 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
View 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
}

File diff suppressed because one or more lines are too long

View File

@ -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()
}

View File

@ -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
View 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
View 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
View 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+CRC32ID 命令帧为 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~FFFFFFFF0 不使用
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~FFFFFFFF0 不使用
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~FFFFFFFF0 不使用。
}
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~FFFFFFFF0 不使用。
}
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())
}

View 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
}

View File

@ -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("解码AtpRequestFrameCRC16校验未通过")
}
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

View File

@ -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

View File

@ -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
}

View File

@ -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)

View File

@ -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

View File

@ -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