This commit is contained in:
xzb 2023-12-27 09:59:13 +08:00
commit d9a9349cb7
13 changed files with 5287 additions and 76 deletions

View File

@ -2,34 +2,35 @@ syntax = "proto3";
package message; package message;
option go_package = "./repository/message_proto"; option go_package = "./repo/dto";
message Simulation {
enum Type {
//
CG = 0;
//
GT = 1;
}
}
message Repo { message Repo {
// 仿 string id = 1;
Simulation.Type simType = 1;
oneof data { oneof data {
CG cg = 2; CGData cgData = 2;
GT gt = 3; GTData gtData = 3;
} }
} }
// //
message GT { message GTData {
} }
// (Train Running Direction)
enum TRD {
//
Upward = 0;
//
Downward = 1;
}
// //
message CG { message CGData {
// 线
repeated Line lines = 1; repeated Line lines = 1;
// (IBP/PSL/MKX等),线
repeated JKP jkps = 2;
} }
// 线 // 线
@ -37,62 +38,90 @@ message Line {
// //
string city = 1; string city = 1;
// 线 // 线
int32 lineId = 2; string lineId = 2;
//
XHBZ xhbz = 3;
//
repeated CiJDQBZ jdqbz = 4;
} }
// //
message XHBZ { message XHBZ {
// id // id
string gid = 1; string id = 1;
// //
repeated Station stations = 2; repeated Station stations = 2;
// //
repeated Platform platforms = 3; repeated Platform platforms = 3;
// //
repeated Psd psds = 4; repeated Psd psds = 4;
// //
repeated PhysicalSection physicalSections = 5; repeated Section sections = 5;
// //
repeated Turnout turnouts = 6; repeated Turnout turnouts = 6;
//
repeated DevidingPoint devidingPoints = 7;
// //
repeated Signal signals = 7; repeated Signal signals = 8;
// // ()
repeated Balise balises = 8; repeated Balise balises = 9;
// //
repeated ParkingSpot parkingSpots = 9; repeated ParkingSpot parkingSpots = 10;
// //
repeated Pd pds = 10; repeated Pd pds = 11;
// //
repeated Qd qds = 11; repeated Qd qds = 12;
// / // ()
repeated EMP emps = 12; repeated EMP emps = 13;
// // (Train Depart Timer,TDT Depart Time Indicator,DTI)
repeated TDT tdts = 13; repeated TDT tdts = 14;
// //
repeated Mkx mkxs = 14; repeated Mkx mkxs = 15;
// //
repeated PSL psls = 15; repeated PSL psls = 16;
// //
repeated SPKS spks = 16; repeated SPKS spks = 17;
//
repeated DeviceEcc deviceEccs = 18;
} }
// //
message SPKS { message SPKS {
uint32 id = 1; uint32 id = 1;
//
string code = 2;
// id
uint32 platformId = 3;
} }
// //
message PSL { message PSL {
uint32 id = 1; uint32 id = 1;
//
string code = 2;
// id
uint32 psdId = 3;
} }
// //
message Mkx { message Mkx {
uint32 id = 1; uint32 id = 1;
//
string code = 2;
// id
uint32 psdId = 3;
} }
// //
message TDT { message TDT {
uint32 id = 1; uint32 id = 1;
//
string code = 2;
// id
uint32 platformId = 3;
} }
// / // /
message EMP { message EMP {
uint32 id = 1; uint32 id = 1;
//
string code = 2;
// id
uint32 platformId = 3;
} }
// //
message Qd { message Qd {
@ -105,37 +134,63 @@ message Pd {
// //
message ParkingSpot { message ParkingSpot {
uint32 id = 1; uint32 id = 1;
//
uint32 tgn = 2;
//
GLB glb = 3;
} }
// //
message Balise { message Balise {
uint32 id = 1; //
}
//
message CheckPoint{
//
enum Type { enum Type {
Boundary = 0; // //
JZ = 1; // FB = 0;
JYJ = 2; // //
TB = 1;
} }
uint32 id = 1; uint32 id = 1;
// //
Type type = 2; Type type = 2;
//
string code = 3;
// id
uint32 ecsId = 4;
}
//
message DevidingPoint {
//
enum Type {
//
Boundary = 0;
//
JZ = 1;
//
JYJ = 2;
}
uint32 id = 1;
//
Type type = 2;
//
string code = 3;
// //
GLB glb = 3; GLB glb = 4;
// / // /
repeated DevicePort devicePorts = 4; repeated SectionPort sectionPorts = 5;
// id
repeated uint32 ecsIds = 6;
} }
// //
message Device { message Device {
// //
enum Type { enum Type {
//
Unknown = 0;
// //
Section = 0; Section = 1;
// //
Turnout = 1; Turnout = 2;
// //
Signal = 2; Signal = 3;
} }
// () // ()
enum Port { enum Port {
@ -149,35 +204,128 @@ message Device {
Type type = 2; Type type = 2;
} }
// //
message DevicePort { message SectionPort {
// id //
uint32 id = 1; enum SectionType {
// //
Device.Type type = 2; Section = 0;
// //
Device.Port port = 3; Turnout = 1;
}
//
enum PortType {
A = 0;
B = 1;
C = 2;
}
//
SectionType type = 1;
// / id
uint32 id = 2;
//
PortType port = 3;
} }
// //
message Signal { message Signal {
//
enum Model { enum Model {
HLU = 0; // 绿
// 绿
// 绿,,,
HLU_0_1_1 = 0;
// 绿,,,
HLU_0_0_1 = 1;
// 绿,,,
HLU_0_1_0 = 2;
// 绿,,,
HLU_U_0_0 = 3;
// 绿,绿,,
HLU_L_1_1 = 4;
// 绿,绿,,
HLU_L_1_0 = 5;
// 绿,
HLU_LU_0_0 = 6;
// 绿
// 绿,
HL_0 = 7;
// 绿,绿
HL_L = 8;
// ,
HU_0 = 9;
// ,
HU_U = 10;
//
H = 11;
//
AB = 12;
//
// ,,,
HBU_0_1_0 = 13;
// ,,,
HBU_U_0_0 = 14;
} }
uint32 id = 1; uint32 id = 1;
// //
string code = 2; string code = 2;
//
Model model = 3;
// id
uint32 ecsId = 4;
} }
// //
message Turnout { message Turnout {
//
enum Model {
// ZDJ9单机牵引
ZDJ9_1 = 0;
// ZDJ9双机牵引
ZDJ9_2 = 1;
}
uint32 id = 1; uint32 id = 1;
//
string code = 2;
//
Model model = 3;
// A端关联的区段/
SectionPort asp = 4;
// B端关联的区段/
SectionPort bsp = 5;
// C端关联的区段/
SectionPort csp = 6;
// id
uint32 ecsId = 7;
} }
// //
message PhysicalSection { message Section {
//
enum Type {
//
Physical = 0;
//
TurnoutPhysical = 1;
//
Logical = 2;
}
uint32 id = 1; uint32 id = 1;
//
string code = 2;
//
Type type = 3;
// A端关联的区段/
SectionPort asp = 4;
// B端关联的区段/
SectionPort bsp = 5;
// id
uint32 ecsId = 6;
} }
// //
message Psd { message Psd {
uint32 id = 1; uint32 id = 1;
//
string code = 2;
//
uint32 platformId = 3;
} }
// //
message Platform { message Platform {
@ -196,8 +344,14 @@ message Station {
string name = 2; string name = 2;
// //
string zname = 3; string zname = 3;
// () //
GLB glb = 4; GLB glb = 4;
// (Equipment centralized station)
bool isEcs = 5;
// /
bool isCd = 6;
//
repeated uint32 ecsStationIds = 7;
} }
// //
message GLB { message GLB {
@ -211,21 +365,21 @@ message GLB {
// //
message CiSBBZ { message CiJDQBZ {
// uid // uid
string ecsId = 1; string ecsName = 1;
// //
repeated Relay relays = 2; repeated Relay relays = 2;
// //
repeated PFP pfps = 3; repeated PFP pfps = 3;
// //
repeated DeviceEcc deviceEcgs = 4; repeated DeviceEcc deviceEcgDatas = 4;
// //
repeated CiCjTable cjt = 5; repeated CiCjTable cjt = 5;
// //
repeated CiQdTable qdt = 6; repeated CiQdTable qdt = 6;
// //
repeated string sectionIds = 7; repeated uint32 sectionIds = 7;
} }
// //
message CiSectionTable { message CiSectionTable {
@ -234,17 +388,18 @@ message CiSectionTable {
} }
// //
message CiQdTable { message CiQdTable {
// id // id()
repeated uint32 relayIds = 1; repeated string relayIds = 1;
} }
// //
message CiCjTable { message CiCjTable {
// ()
repeated CiCjItem items = 1; repeated CiCjItem items = 1;
} }
// //
message CiCjItem { message CiCjItem {
// id // id
uint32 relayId = 1; string uint32 = 1;
// (/),true-,false- // (/),true-,false-
bool q = 2; bool q = 2;
} }
@ -275,21 +430,27 @@ message Relay {
} }
// (IBP//PSL等) // (IBP//PSL等)
message JKP { message JKP {
//
enum Type { enum Type {
// IBP盘()
IBP = 0; IBP = 0;
//
PSL = 1; PSL = 1;
//
MKX = 2; MKX = 2;
} }
// id // id
uint32 gid = 1; string gid = 1;
//
Type type = 2;
// //
repeated KaiGuan kgs = 2; repeated KaiGuan kgs = 3;
// //
repeated Lamp lamps = 3; repeated Lamp lamps = 4;
// //
repeated Alarm alarms = 4; repeated Alarm alarms = 5;
// //
repeated DeviceEcc deviceEccs = 5; repeated DeviceEcc deviceEccs = 6;
} }
// / // /
message Alarm { message Alarm {
@ -302,8 +463,10 @@ message KaiGuan {
uint32 id = 1; uint32 id = 1;
// //
string code = 2; string code = 2;
//
bool zfw = 3;
// //
bool lamp = 3; bool lamp = 4;
} }
// //
message Lamp { message Lamp {
@ -314,10 +477,10 @@ message Lamp {
// //
message DeviceEcc { message DeviceEcc {
//
string deviceCode = 1;
// //
Device.Type deviceType = 2; Device.Type deviceType = 1;
//
string deviceCode = 2;
// //
repeated Ecc ecc = 3; repeated Ecc ecc = 3;
} }
@ -325,6 +488,6 @@ message DeviceEcc {
message Ecc { message Ecc {
// //
string code = 1; string code = 1;
// id // ()id
repeated uint32 ids = 2; repeated uint32 ids = 2;
} }

17
repo/api.go Normal file
View File

@ -0,0 +1,17 @@
package repo
import "joylink.club/rtsssimulation/repo/model"
type Repo interface {
// 模型仓库id
Id() string
// 通过uid查询模型对象
FindByUid(uid string) model.Model
}
type IdMap interface {
// 获取数据元素id
DeId() string
// 获取uid
Uid() string
}

23
repo/check_test.go Normal file
View File

@ -0,0 +1,23 @@
package repo
import "testing"
func TestIsFieldEmpty(t *testing.T) {
tests := []struct {
give any
want bool
}{
{give: nil, want: true},
{give: "", want: true},
{give: 0, want: true},
{give: []string{}, want: true},
{give: map[string]string{}, want: true},
{give: []int{}, want: true},
}
for _, tt := range tests {
if got := isFieldEmpty(tt.give); got != tt.want {
t.Errorf("IsFieldEmpty(%v) = %v, want = %v", tt.give, got, tt.want)
}
}
}

4239
repo/dto/repo.pb.go Normal file

File diff suppressed because it is too large Load Diff

25
repo/error_record.go Normal file
View File

@ -0,0 +1,25 @@
package repo
// 构建错误记录
type ErrorRecord struct {
// 告警信息
Warns []string
// 错误信息
Errs []string
}
func NewErrorRecord() *ErrorRecord {
return &ErrorRecord{}
}
func (r *ErrorRecord) AddError(err string) {
r.Errs = append(r.Errs, err)
}
func (r *ErrorRecord) HasError() bool {
return len(r.Errs) > 0
}
func (r *ErrorRecord) AddWarn(warn string) {
r.Warns = append(r.Warns, warn)
}

484
repo/idmapping_build.go Normal file
View File

@ -0,0 +1,484 @@
package repo
import (
"fmt"
"reflect"
"strconv"
"strings"
"joylink.club/rtsssimulation/repo/dto"
"joylink.club/rtsssimulation/repo/model"
)
// 城轨uid
type CgUid struct {
// 城市
city string
// 线路
line string
// 设备编号
codes []string
// codestr
codestr string
// uid
idstr string
}
func NewCgUid(city string, line string, codes ...string) *CgUid {
if city == "" || line == "" {
panic(fmt.Errorf("创建城轨uid错误: 城市、线路、车站不能为空 city=%s, line=%s", city, line))
}
l := len(codes)
if l == 0 {
panic(fmt.Errorf("创建城轨uid错误: codes不能为空"))
}
codestr := strings.Join(codes, "_")
elems := []string{city, line, codestr}
uid := strings.Join(elems, "_")
return &CgUid{
city: city,
line: line,
codes: codes,
codestr: codestr,
idstr: uid,
}
}
func (u *CgUid) Id() string {
return u.idstr
}
func (u *CgUid) Code() string {
return u.codestr
}
type idMap struct {
// 数据id
Did string
// 元素id
Eid uint32
// 唯一id
Uid model.Uid
}
func NewIdMap(did string, eid uint32, uid model.Uid) *idMap {
return &idMap{
Did: did,
Eid: eid,
Uid: uid,
}
}
func (m *idMap) DeId() string {
return DeID(m.Did, m.Eid)
}
func (m *idMap) Debug() string {
return fmt.Sprintf("{数据id=%s, 元素id=%v, uid=%s}", m.Did, m.Eid, m.Uid.Id())
}
// 数据元素id key生成
// did - 数据id
// eid - 元素id
func DeID(did string, eid uint32) string {
return fmt.Sprintf("%s_%v", did, eid)
}
// id映射表
type IdMapping struct {
// 数据元素id映射
deidMap map[string]*idMap
// uid映射(只存储最初的idMap,实际中一个uid可能对应多个数据元素id)
uidMap map[string]*idMap
}
func (m *IdMapping) queryByDeId(deid string) *idMap {
return m.deidMap[deid]
}
func (m *IdMapping) queryByDidAndEid(did string, eid uint32) *idMap {
id := DeID(did, eid)
return m.deidMap[id]
}
func (m *IdMapping) add(idmap *idMap) {
if idmap == nil {
panic(fmt.Errorf("添加id映射表错误: idmap不能为空"))
}
m.deidMap[idmap.DeId()] = idmap
}
func (m *IdMapping) checkUidAndAdd(idmap *idMap) (existed *idMap, uidExisted bool) {
if idmap == nil {
panic(fmt.Errorf("添加id映射表错误: idmap不能为空"))
}
m.deidMap[idmap.DeId()] = idmap
existed, ok := m.uidMap[idmap.Uid.Id()]
if ok {
uidExisted = true
} else {
m.uidMap[idmap.Uid.Id()] = idmap
}
return
}
// 构建id映射
func BuildIdMapping(msgs *dto.Repo, errRecord *ErrorRecord) *IdMapping {
if cg := msgs.GetCgData(); cg != nil {
return BuildCgIdMapping(cg, errRecord)
} else if gt := msgs.GetGtData(); gt != nil {
return BuildGtIdMapping(gt)
}
errRecord.AddError("构建id映射表错误: 没有数据")
return nil
}
// 构建城轨id映射
func BuildCgIdMapping(cg *dto.CGData, errRecord *ErrorRecord) *IdMapping {
if cg == nil {
errRecord.AddError("构建城轨id映射表错误: 未关联任何数据")
return nil
}
idMapping := &IdMapping{
deidMap: make(map[string]*idMap),
uidMap: make(map[string]*idMap),
}
lineSet := make(map[string]struct{})
for _, l := range cg.Lines {
if _, ok := lineSet[l.City+l.LineId]; ok {
errRecord.AddError(fmt.Sprintf("构建城轨id映射表错误: 线路重复:%s_%s", l.City, l.LineId))
return nil
}
lineSet[l.City+l.LineId] = struct{}{}
}
for _, line := range cg.Lines {
buildXhbz(idMapping, line, errRecord)
if errRecord.HasError() {
return nil
}
}
return idMapping
}
// 构建信号布置数据id映射
func buildXhbz(idMapping *IdMapping, line *dto.Line, errRecord *ErrorRecord) {
xhbz := line.Xhbz
if xhbz == nil {
errRecord.AddError("构建城轨id映射表错误: 信号布置数据为空")
return
}
city := line.City // 城市
lineId := line.LineId // 线路id
did := xhbz.Id // 数据id
errPrefix := "信号布置数据错误"
// 车站uid
for _, station := range xhbz.Stations {
eid := station.Id
if checkFieldEmpty(did, eid, station.Name, errPrefix, "车站名称", errRecord) {
continue
}
idmap := NewIdMap(did, eid, NewCgUid(city, lineId, station.Name))
if checkRepeatOrAddIdMap(idmap, idMapping, errPrefix, errRecord) {
continue
}
}
// 车站站台uid
for _, p := range xhbz.Platforms {
eid := p.Id
if checkFieldEmpty(did, eid, p.Code, errPrefix, "站台编号", errRecord) {
continue
}
if checkFieldEmpty(did, eid, p.StationId, errPrefix, "站台关联车站", errRecord) {
continue
}
stationIdm := idMapping.queryByDidAndEid(did, p.StationId)
if checkRelationNil(did, eid, p.StationId, stationIdm, errPrefix, "站台关联车站", errRecord) {
continue
}
// 站台code: 车站名称_站台编号
idmap := NewIdMap(did, eid, NewCgUid(city, lineId, stationIdm.Uid.Code(), p.Code))
if checkRepeatOrAddIdMap(idmap, idMapping, errPrefix, errRecord) {
continue
}
}
// 屏蔽门uid
for _, p := range xhbz.Psds {
eid := p.Id
if checkFieldEmpty(did, eid, p.Code, errPrefix, "屏蔽门编号", errRecord) {
continue
}
if checkFieldEmpty(did, eid, p.PlatformId, errPrefix, "屏蔽门关联站台", errRecord) {
continue
}
relIdMap := idMapping.queryByDidAndEid(did, p.PlatformId)
if checkRelationNil(did, eid, p.PlatformId, relIdMap, errPrefix, "屏蔽门关联站台", errRecord) {
continue
}
// 屏蔽门code: 站台编号_屏蔽门编号
idmap := NewIdMap(did, eid, NewCgUid(city, lineId, relIdMap.Uid.Code(), p.Code))
if checkRepeatOrAddIdMap(idmap, idMapping, errPrefix, errRecord) {
continue
}
}
// 物理区段
for _, ps := range xhbz.PhysicalSections {
eid := ps.Id
if checkFieldEmpty(did, eid, ps.Code, errPrefix, "物理区段编号", errRecord) {
continue
}
if checkFieldEmpty(did, eid, ps.EcsId, errPrefix, "物理区段关联集中站", errRecord) {
continue
}
relIdMap := idMapping.queryByDidAndEid(did, ps.EcsId)
if checkRelationNil(did, eid, ps.EcsId, relIdMap, errPrefix, "物理区段关联集中站", errRecord) {
continue
}
// 物理区段code: 集中站名称_物理区段编号
idmap := NewIdMap(did, eid, NewCgUid(city, lineId, relIdMap.Uid.Code(), ps.Code))
if checkRepeatOrAddIdMap(idmap, idMapping, errPrefix, errRecord) {
continue
}
}
// 道岔uid
for _, t := range xhbz.Turnouts {
eid := t.Id
if checkFieldEmpty(did, eid, t.Code, errPrefix, "道岔编号", errRecord) {
continue
}
if checkFieldEmpty(did, eid, t.EcsId, errPrefix, "道岔关联集中站", errRecord) {
continue
}
relIdMap := idMapping.queryByDidAndEid(did, t.EcsId)
if checkRelationNil(did, eid, t.EcsId, relIdMap, errPrefix, "道岔关联集中站", errRecord) {
continue
}
// 道岔code: 集中站名称_道岔编号
idmap := NewIdMap(did, eid, NewCgUid(city, lineId, relIdMap.Uid.Code(), t.Code))
if checkRepeatOrAddIdMap(idmap, idMapping, errPrefix, errRecord) {
continue
}
}
// 区段分界点uid
for _, c := range xhbz.DevidingPoints {
eid := c.Id
if checkFieldEmpty(did, eid, c.Code, errPrefix, "区段分界点编号", errRecord) {
continue
}
if checkFieldEmpty(did, eid, c.EcsIds, errPrefix, "区段分界点关联集中站", errRecord) {
continue
}
ecsErr := false
for _, v := range c.EcsIds {
relIdMap := idMapping.queryByDidAndEid(did, v)
if checkRelationNil(did, eid, v, relIdMap, errPrefix, "区段分界点关联集中站", errRecord) {
ecsErr = true
continue
}
}
if ecsErr {
continue
}
idmap := NewIdMap(did, eid, NewCgUid(city, lineId, c.Code))
if checkRepeatOrAddIdMap(idmap, idMapping, errPrefix, errRecord) {
continue
}
}
// 信号机uid
for _, s := range xhbz.Signals {
eid := s.Id
if checkFieldEmpty(did, eid, s.Code, errPrefix, "信号机编号", errRecord) {
continue
}
if checkFieldEmpty(did, eid, s.EcsId, errPrefix, "信号机关联集中站", errRecord) {
continue
}
relIdMap := idMapping.queryByDidAndEid(did, s.EcsId)
if checkRelationNil(did, eid, s.EcsId, relIdMap, errPrefix, "信号机关联集中站", errRecord) {
continue
}
idmap := NewIdMap(did, eid, NewCgUid(city, lineId, relIdMap.Uid.Code(), s.Code))
if checkRepeatOrAddIdMap(idmap, idMapping, errPrefix, errRecord) {
continue
}
}
// 应答器uid
for _, b := range xhbz.Balises {
eid := b.Id
if checkFieldEmpty(did, eid, b.Code, errPrefix, "应答器编号", errRecord) {
continue
}
if checkFieldEmpty(did, eid, b.EcsId, errPrefix, "应答器关联集中站", errRecord) {
continue
}
relIdMap := idMapping.queryByDidAndEid(did, b.EcsId)
if checkRelationNil(did, eid, b.EcsId, relIdMap, errPrefix, "应答器关联集中站", errRecord) {
continue
}
idmap := NewIdMap(did, eid, NewCgUid(city, lineId, relIdMap.Uid.Code(), b.Code))
if checkRepeatOrAddIdMap(idmap, idMapping, errPrefix, errRecord) {
continue
}
}
// 停车点
for _, ps := range xhbz.ParkingSpots {
idmap := NewIdMap(did, ps.Id, NewCgUid(city, lineId, did, strconv.Itoa(int(ps.Id))))
if checkRepeatOrAddIdMap(idmap, idMapping, errPrefix, errRecord) {
continue
}
}
// 坡度
for _, v := range xhbz.Pds {
idmap := NewIdMap(did, v.Id, NewCgUid(city, lineId, did, strconv.Itoa(int(v.Id))))
if checkRepeatOrAddIdMap(idmap, idMapping, errPrefix, errRecord) {
continue
}
}
// 曲度
for _, v := range xhbz.Qds {
idmap := NewIdMap(did, v.Id, NewCgUid(city, lineId, did, strconv.Itoa(int(v.Id))))
if checkRepeatOrAddIdMap(idmap, idMapping, errPrefix, errRecord) {
continue
}
}
// 紧急停车按钮
for _, v := range xhbz.Emps {
eid := v.Id
if checkFieldEmpty(did, eid, v.Code, errPrefix, "紧急停车按钮编号", errRecord) {
continue
}
if checkFieldEmpty(did, eid, v.PlatformId, errPrefix, "紧急停车按钮关联站台", errRecord) {
continue
}
relIdMap := idMapping.queryByDidAndEid(did, v.PlatformId)
if checkRelationNil(did, eid, v.PlatformId, relIdMap, errPrefix, "紧急停车按钮关联站台", errRecord) {
continue
}
idmap := NewIdMap(did, eid, NewCgUid(city, lineId, relIdMap.Uid.Code(), v.Code))
if checkRepeatOrAddIdMap(idmap, idMapping, errPrefix, errRecord) {
continue
}
}
// 停车倒计时器
for _, v := range xhbz.Tdts {
eid := v.Id
if checkFieldEmpty(did, eid, v.Code, errPrefix, "停车倒计时器编号", errRecord) {
continue
}
if checkFieldEmpty(did, eid, v.PlatformId, errPrefix, "停车倒计时器关联站台", errRecord) {
continue
}
relIdMap := idMapping.queryByDidAndEid(did, v.PlatformId)
if checkRelationNil(did, eid, v.PlatformId, relIdMap, errPrefix, "停车倒计时器关联站台", errRecord) {
continue
}
idmap := NewIdMap(did, eid, NewCgUid(city, lineId, relIdMap.Uid.Code(), v.Code))
if checkRepeatOrAddIdMap(idmap, idMapping, errPrefix, errRecord) {
continue
}
}
// 门控箱
for _, v := range xhbz.Mkxs {
eid := v.Id
if checkFieldEmpty(did, eid, v.Code, errPrefix, "门控箱编号", errRecord) {
continue
}
if checkFieldEmpty(did, eid, v.PsdId, errPrefix, "门控箱关联屏蔽门", errRecord) {
continue
}
relIdMap := idMapping.queryByDidAndEid(did, v.PsdId)
if checkRelationNil(did, eid, v.PsdId, relIdMap, errPrefix, "门控箱关联屏蔽门", errRecord) {
continue
}
idmap := NewIdMap(did, eid, NewCgUid(city, lineId, relIdMap.Uid.Code(), v.Code))
if checkRepeatOrAddIdMap(idmap, idMapping, errPrefix, errRecord) {
continue
}
}
// PSL
for _, v := range xhbz.Psls {
eid := v.Id
if checkFieldEmpty(did, eid, v.Code, errPrefix, "PSL编号", errRecord) {
continue
}
if checkFieldEmpty(did, eid, v.PsdId, errPrefix, "PSL关联屏蔽门", errRecord) {
continue
}
relIdMap := idMapping.queryByDidAndEid(did, v.PsdId)
if checkRelationNil(did, eid, v.PsdId, relIdMap, errPrefix, "PSL关联屏蔽门", errRecord) {
continue
}
idmap := NewIdMap(did, eid, NewCgUid(city, lineId, relIdMap.Uid.Code(), v.Code))
if checkRepeatOrAddIdMap(idmap, idMapping, errPrefix, errRecord) {
continue
}
}
// SPKS
for _, v := range xhbz.Spks {
eid := v.Id
if checkFieldEmpty(did, eid, v.Code, errPrefix, "SPKS编号", errRecord) {
continue
}
if checkFieldEmpty(did, eid, v.PlatformId, errPrefix, "SPKS关联站台", errRecord) {
continue
}
relIdMap := idMapping.queryByDidAndEid(did, v.PlatformId)
if checkRelationNil(did, eid, v.PlatformId, relIdMap, errPrefix, "SPKS关联站台", errRecord) {
continue
}
idmap := NewIdMap(did, eid, NewCgUid(city, lineId, relIdMap.Uid.Code(), v.Code))
if checkRepeatOrAddIdMap(idmap, idMapping, errPrefix, errRecord) {
continue
}
}
}
func checkRelationNil(did string, eid uint32, relId uint32, relIdMap *idMap, prefix string, relTip string, errRecord *ErrorRecord) bool {
if relIdMap == nil {
errRecord.AddError(fmt.Sprintf("%s: %s不存在, 数据id=%s 数据元素id=%v 关联id=%v", prefix, relTip, did, eid, relId))
return true
}
return false
}
// 检查字段是否为空
func checkFieldEmpty(did string, eid uint32, field any, prefix string, tip string, errRecord *ErrorRecord) bool {
if isFieldEmpty(field) {
errRecord.AddError(fmt.Sprintf("%s: %s不能为空, 数据id=%s 数据元素id=%v ", prefix, tip, did, eid))
return true
}
return false
}
func isFieldEmpty(field any) bool {
if field == nil {
return true
}
vf := reflect.ValueOf(field)
switch vf.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64,
reflect.Float32, reflect.Float64:
return field == 0
case reflect.String:
return field == ""
case reflect.Slice, reflect.Map:
return vf.Len() == 0
}
return false
}
// 数据uid/deid重复检查
func checkRepeatOrAddIdMap(idm *idMap, idMapping *IdMapping, prefix string, errRecord *ErrorRecord) (repeated bool) {
if old := idMapping.queryByDeId(idm.DeId()); old != nil {
errRecord.AddError(fmt.Sprintf("%s,数据元素重复: 数据1=%s 数据2=%s,", prefix, old.Debug(), idm.Debug()))
repeated = true
} else if old, _ := idMapping.checkUidAndAdd(idm); old != nil {
errRecord.AddError(fmt.Sprintf("%s,数据重复: 数据1=%s 数据2=%s,", prefix, old.Debug(), idm.Debug()))
repeated = true
}
return
}
func BuildGtIdMapping(gt *dto.GTData) *IdMapping {
panic("未实现")
}

39
repo/manage.go Normal file
View File

@ -0,0 +1,39 @@
package repo
import (
"sync"
"joylink.club/rtsssimulation/repo/dto"
)
type repoManager struct {
repoMap map[string]Repo
lock sync.Mutex
}
var defaultManager = &repoManager{
repoMap: make(map[string]Repo),
}
// 获取或构建模型仓库
func GetOrBuildRepo(id string, dc func(errRecord *ErrorRecord) *dto.Repo) (Repo, *ErrorRecord) {
manager := defaultManager
manager.lock.Lock()
defer manager.lock.Unlock()
r, ok := manager.repoMap[id]
errRecord := NewErrorRecord()
if !ok {
// 所需protobuf数据转换
msgs := dc(errRecord)
// 数据转换出错直接返回
if errRecord.HasError() {
return nil, errRecord
}
// 构建模型Repo
r, errRecord = BuildFrom(msgs)
if r != nil {
manager.repoMap[id] = r
}
}
return r, errRecord
}

57
repo/model/api.go Normal file
View File

@ -0,0 +1,57 @@
package model
type ModelType string
const (
// 车站
MT_Station ModelType = "Station"
// 站台
MT_Stand ModelType = "Stand"
// 屏蔽门
MT_PSD ModelType = "PSD"
// Link
MT_Link ModelType = "Link"
// 区段
MT_Section ModelType = "Section"
// 道岔
MT_Turnout ModelType = "Turnout"
// 信号机
MT_Signal ModelType = "Signal"
// 应答器
MT_Balise ModelType = "Balise"
)
type Uid interface {
Id() string
// 设备原始编号
Code() string
}
// 模型接口
type Model interface {
// Unique id,唯一id
Uid() Uid
// 模型类型
Type() ModelType
}
// Link
type Link interface {
Model
}
// 物理区段(实际检测区段,一般区段两端点,道岔区段为3-4个端点)
type PhysicalSection interface {
Model
Code() string
}
// 道岔
type Turnout interface {
Model
}
// 关联的端口关系
type AssociatedPort interface {
Port() string
}

52
repo/model/impl/link.go Normal file
View File

@ -0,0 +1,52 @@
package impl
import (
"fmt"
"sync/atomic"
"joylink.club/rtsssimulation/repo/dto"
"joylink.club/rtsssimulation/repo/model"
)
// link生成uid基础值
var link_uid_base = atomic.Uint32{}
// 轨道链路
type Link struct {
uid string
apTurnout *Turnout // A端关联道岔,可能为nil
bpTurnout *Turnout // B端关联道岔,可能为nil
apGlb dto.GLB // A端公里标
bpGlb dto.GLB // B端公里标
length int64 // 长度
models []model.Model // 关联的模型,从A到B排序
physicalSections []*PhysicalSection // 关联的物理区段(包含道岔物理区段),从A到B排序
}
func NewLink() *Link {
return &Link{
uid: fmt.Sprintf("%d", link_uid_base.Add(1)),
}
}
func (l *Link) Uid() string {
return l.uid
}
func (l *Link) Type() model.ModelType {
return model.MT_Link
}
// link偏移
type LinkOffset struct {
link *Link
offset int64
}
// link偏移范围
type LinkRange struct {
link *Link
start int64
end int64
}

View File

@ -0,0 +1,33 @@
package impl
import (
"joylink.club/rtsssimulation/repo/model"
)
type PhysicalSection struct {
uid string
code string
apSection model.Model // A端关联区段/道岔
bpSection model.Model // B端关联区段/道岔
linkRanges []*LinkRange // 所属link范围(道岔物理区段为多个)
}
func NewPhysicalSection(uid string, code string) *PhysicalSection {
return &PhysicalSection{
uid: uid,
code: code,
}
}
func (s *PhysicalSection) Uid() string {
return s.uid
}
func (s *PhysicalSection) Type() model.ModelType {
return model.MT_Section
}
func (s *PhysicalSection) Code() string {
return s.code
}

View File

@ -0,0 +1,6 @@
package impl
// 区段分段设备(计轴/绝缘节)
type SectionDividedDevice struct {
uid string
}

View File

@ -0,0 +1,29 @@
package impl
import "joylink.club/rtsssimulation/repo/model"
type Turnout struct {
uid string
code string
// A端关联区段/道岔
apSection model.Model
// B端关联区段/道岔
bpSection model.Model
// C端关联区段/道岔
cpSection model.Model
// A方向Link
adLink *Link
// B方向Link
bdLink *Link
// C方向Link
cdLink *Link
}
func NewTurnout(uid string, code string) *Turnout {
return &Turnout{
uid: uid,
code: code,
}
}

44
repo/repo.go Normal file
View File

@ -0,0 +1,44 @@
package repo
import (
"joylink.club/rtsssimulation/repo/dto"
"joylink.club/rtsssimulation/repo/model"
"joylink.club/rtsssimulation/repo/model/impl"
)
type repo struct {
id string
idMapping *IdMapping // id映射
modelMap map[string]model.Model // 模型map,key为uid
linkMap map[string]*impl.Link // 链路map,key为uid
physicalSectionMap map[string]*impl.PhysicalSection // 物理区段map,key为uid
turnoutMap map[string]*impl.Turnout // 道岔map,key为uid
}
func BuildFrom(msgs *dto.Repo) (Repo, *ErrorRecord) {
errRecord := NewErrorRecord()
idMapping := BuildIdMapping(msgs, errRecord)
if errRecord.HasError() {
return nil, errRecord
}
repo := &repo{
id: msgs.Id,
idMapping: idMapping,
modelMap: make(map[string]model.Model, 1024),
linkMap: make(map[string]*impl.Link, 256),
physicalSectionMap: make(map[string]*impl.PhysicalSection, 256),
turnoutMap: make(map[string]*impl.Turnout, 128),
}
return repo, errRecord
}
// 模型仓库id
func (r *repo) Id() string {
return r.id
}
// 通过uid查询模型对象
func (r *repo) FindByUid(uid string) model.Model {
return nil
}