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