rts-sim-module/repo/idmapping_build.go
2023-12-27 09:15:26 +08:00

485 lines
14 KiB
Go

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("未实现")
}