From a354c9f13653e88e61ce9da37170cc70d6771002 Mon Sep 17 00:00:00 2001 From: Yuan Date: Wed, 3 Jan 2024 16:24:10 +0800 Subject: [PATCH] Section --- src/common/common.ts | 56 +++ src/packages/AxleCounting/AxleCounting.ts | 32 ++ src/packages/Section/Section.ts | 30 -- src/packages/Section/bjrtss/Section.ts | 14 + src/packages/Section/common/Section.ts | 355 ++++++++++++++++++ .../Section/{ => common}/SectionGraphic.ts | 0 6 files changed, 457 insertions(+), 30 deletions(-) create mode 100644 src/common/common.ts create mode 100644 src/packages/AxleCounting/AxleCounting.ts delete mode 100644 src/packages/Section/Section.ts create mode 100644 src/packages/Section/bjrtss/Section.ts create mode 100644 src/packages/Section/common/Section.ts rename src/packages/Section/{ => common}/SectionGraphic.ts (100%) diff --git a/src/common/common.ts b/src/common/common.ts new file mode 100644 index 0000000..22b84ee --- /dev/null +++ b/src/common/common.ts @@ -0,0 +1,56 @@ +export enum DevicePort { + A = 0, + B = 1, + C = 2, +} + +export enum DeviceType { + Section = 0, + Turnout = 1, + TrainWindow = 2, + AxleCounting = 3, + SectionLink = 4, + Signal = 5, + Station = 6, + ScreenDoor = 7, + SignalFaultAlarm = 8, + Breakers = 9, + PowerScreen = 10, +} + +export enum Direction { + LEFT = 0, + RIGHT = 1, +} + +export interface KilometerSystem { + get coordinateSystem(): string; + set coordinateSystem(v: string); + get kilometer(): number; + set kilometer(v: number); + get direction(): Direction; + set direction(v: Direction); +} + +export interface IRelatedRef { + deviceType: DeviceType; //关联的设备类型 + id: number; //关联的设备ID + devicePort: DevicePort; //关联的设备端口 +} + +export namespace IRelatedRef { + export function create( + type: string, + id: number, + port: DevicePort, + ): IRelatedRef { + const typeNum = Object.keys(DeviceType).indexOf(type); + if (typeNum < 0) throw Error('Invalid device type'); + typeNum as DeviceType; + return { + deviceType: typeNum, + id, + devicePort: port, + }; + } +} diff --git a/src/packages/AxleCounting/AxleCounting.ts b/src/packages/AxleCounting/AxleCounting.ts new file mode 100644 index 0000000..8db3a0d --- /dev/null +++ b/src/packages/AxleCounting/AxleCounting.ts @@ -0,0 +1,32 @@ +import { GraphicData, JlGraphic } from 'jl-graphic'; +import { IRelatedRef, KilometerSystem } from 'src/common/common'; + +enum TypeDetectionPoint { + AxleCounting = 0, + SectionBoundary = 1, +} + +export interface IAxleCountingData extends GraphicData { + code: string; + kilometerSystem: KilometerSystem; + axleCountingRef: IRelatedRef[]; + type: TypeDetectionPoint; + centralizedStations: number[]; + clone(): IAxleCountingData; + copyFrom(data: IAxleCountingData): void; + eq(other: IAxleCountingData): boolean; +} + +export class AxleCounting extends JlGraphic { + static Type = 'axleCounting'; + + constructor() { + super(AxleCounting.Type); + } + + doRepaint(): void {} + + get datas(): IAxleCountingData { + return this.getDatas(); + } +} diff --git a/src/packages/Section/Section.ts b/src/packages/Section/Section.ts deleted file mode 100644 index a5c266d..0000000 --- a/src/packages/Section/Section.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { GraphicData, JlGraphic } from 'jl-graphic' -import { IPointData } from 'pixi.js' -import { SectionGraphic } from './SectionGraphic' - -export interface ISectionData extends GraphicData { - code: string - points: IPointData[] - -} - -export interface SectionDisplayConfig { - lineColor: string; - lineWidth: number; -} - -export class Section extends JlGraphic { - static Type = 'Section' - lineGraphic: SectionGraphic - - constructor() { - super(Section.Type) - this.lineGraphic = new SectionGraphic(); - // this.transformSave = true; - this.addChild(this.lineGraphic); - } - - doRepaint(): void { - console.log('repaint') - } -} diff --git a/src/packages/Section/bjrtss/Section.ts b/src/packages/Section/bjrtss/Section.ts new file mode 100644 index 0000000..bc79d61 --- /dev/null +++ b/src/packages/Section/bjrtss/Section.ts @@ -0,0 +1,14 @@ +import { Section as SectionBase, SectionDisplayConfig } from "../common/Section"; + +const displayConfig: SectionDisplayConfig = { + lineColor: '#5578b6', + occupiedColor: '#f00', + lineWidth: 5 +} + +export class Section extends SectionBase { + constructor() { + super(); + this.setDisplayConfig(displayConfig) + } +} diff --git a/src/packages/Section/common/Section.ts b/src/packages/Section/common/Section.ts new file mode 100644 index 0000000..33341e2 --- /dev/null +++ b/src/packages/Section/common/Section.ts @@ -0,0 +1,355 @@ +import { + GraphicData, + GraphicRelationParam, + GraphicState, + JlGraphic, + JlGraphicTemplate, + Vector2, + VectorText, + convertToBezierParams, + distance2, + splitLineEvenly, +} from 'jl-graphic'; +import { IPointData } from 'pixi.js'; +import { SectionGraphic } from './SectionGraphic'; +import { DevicePort, DeviceType, IRelatedRef } from 'src/common/common'; +import { Turnout } from 'src/packages/Turnout/Turnout'; +import { AxleCounting } from 'src/packages/AxleCounting/AxleCounting'; + +const tolerance = 0.01; + +export interface ISectionData extends GraphicData { + code: string; + isCurve: boolean; + segmentsCount: number; + points: IPointData[]; + sectionType: SectionType; + paRef?: IRelatedRef; + pbRef?: IRelatedRef; + axleCountings?: number[]; + centralizedStations?: number[]; + trackSectionId?: number; + clone(): ISectionData; + copyFrom(data: ISectionData): void; + eq(other: ISectionData): boolean; +} + +export interface ISectionState extends GraphicState { + id: number; + occupied: boolean; +} + +export enum SectionType { + Physical = 0, //物理区段 + Logic = 1, //逻辑区段 + TurnoutPhysical = 2, //道岔物理区段 + Track = 4, //轨道区段 + TrackLogic = 5, //轨道逻辑区段 +} + +export interface SectionDisplayConfig { + lineColor: string; + occupiedColor: string; + lineWidth: number; +} + +const defaultDisplayConfig: SectionDisplayConfig = { + lineColor: '#5578b6', + occupiedColor: '#f00', + lineWidth: 5, +}; + +export class Section extends JlGraphic { + static Type = 'Section'; + lineGraphic: SectionGraphic; + labelGraphic: VectorText; + displayConfig = defaultDisplayConfig; + + constructor() { + super(Section.Type); + this.lineGraphic = new SectionGraphic(); + this.labelGraphic = new VectorText(''); + this.labelGraphic.setVectorFontSize(14); + this.labelGraphic.anchor.set(0.5); + this.labelGraphic.style.fill = '#0f0'; + this.labelGraphic.transformSave = true; + this.labelGraphic.name = 'label'; + this.transformSave = true; + this.addChild(this.lineGraphic); + this.addChild(this.labelGraphic); + } + + setDisplayConfig(config: SectionDisplayConfig) { + this.displayConfig = config; + } + + getVerticesList(): IPointData[] { + if (this.datas.isCurve) { + return [ + this.datas.points[0], + ...convertToBezierParams(this.datas.points).map((param) => param.p2), + ]; + } else { + return this.datas.points; + } + } + getStartPoint() { + return this.datas.points[0]; + } + getEndPoint(): IPointData { + return this.datas.points[this.datas.points.length - 1]; + } + + doRepaint(): void { + this.lineGraphic.clear(); + if (this.datas.sectionType === SectionType.TurnoutPhysical) { + return; + } + this.lineGraphic.isCurve = this.datas.isCurve; + if (this.lineGraphic.isCurve) { + this.lineGraphic.segmentsCount = this.datas.segmentsCount; + } + this.lineGraphic.points = this.datas.points; + this.lineGraphic.lineStyle( + this.displayConfig.lineWidth, + this.states.occupied + ? this.displayConfig.occupiedColor + : this.displayConfig.lineColor, + ); + this.labelGraphic.text = this.datas.code; + const labelPosition = this.datas.childTransforms?.find( + (t) => t.name === this.labelGraphic.name, + )?.transform.position; + if (labelPosition) { + this.labelGraphic.position.set(labelPosition.x, labelPosition.y); + } else { + this.labelGraphic.position.set( + this.datas.points[0].x, + this.datas.points[0].y + 20, + ); + } + } + get datas(): ISectionData { + return this.getDatas(); + } + get states(): ISectionState { + return this.getStates(); + } + get linePoints(): IPointData[] { + return this.datas.points; + } + set linePoints(points: IPointData[]) { + const old = this.datas.clone(); + old.points = points; + this.updateData(old); + } + + getConnectElement(port: DevicePort) { + const relation = this.relationManage + .getRelationsOfGraphic(this) + .find( + (relation) => + relation.getRelationParam(this).getParam() === port && + (relation.getOtherGraphic(this) instanceof Section || + relation.getOtherGraphic(this) instanceof Turnout), + ); + if (!relation) { + return; + } + return { + g: relation?.getOtherGraphic(this) as Section | Turnout, + port: relation?.getOtherRelationParam(this).getParam(), + }; + } + + /** 获取拆分逻辑区段数据 */ + getSplitPoints(count: number): IPointData[][] { + if (this.datas.points.length !== 2) { + let totalLen = 0; + const lengths: number[] = []; + for (let i = 1; i < this.datas.points.length; i++) { + const { x: x1, y: y1 } = this.datas.points[i - 1], + { x: x2, y: y2 } = this.datas.points[i]; + const len = new Vector2([x2 - x1, y2 - y1]).length(); + totalLen += len; + lengths.push(len); + } + const counts = lengths.map((length) => + Math.round((count * length) / totalLen), + ); + if (counts.reduce((p, c) => p + c, 0) !== count) { + const intersection = counts.reduce((p, c) => p + c, 0) - count; + let maxCountIndex = 0, + maxCount = 0; + counts.forEach((c, i) => { + if (c > maxCount) { + maxCount = c; + maxCountIndex = i; + } + }); + counts[maxCountIndex] + intersection; + } + return counts + .map((count, i) => { + return splitLineEvenly( + this.localToCanvasPoint(this.datas.points[i]), + this.localToCanvasPoint(this.datas.points[i + 1]), + count, + ); + }) + .flat(); + } else { + return splitLineEvenly( + this.localToCanvasPoint(this.datas.points[0]), + this.localToCanvasPoint( + this.datas.points[this.datas.points.length - 1], + ), + count, + ); + } + } + + buildRelation() { + this.relationManage.deleteRelationOfGraphicAndOtherType(this, Section.Type); + + if (this.datas.sectionType === SectionType.Physical) { + this.queryStore.queryByType
(Section.Type).forEach((section) => { + if (section.id === this.id) return; + + let param: DevicePort[] = []; + if ( + distance2( + this.localToCanvasPoint(this.getStartPoint()), + section.localToCanvasPoint(section.getStartPoint()), + ) <= tolerance + ) { + param = [DevicePort.A, DevicePort.A]; + } + if ( + distance2( + this.localToCanvasPoint(this.getEndPoint()), + section.localToCanvasPoint(section.getStartPoint()), + ) <= tolerance + ) { + param = [DevicePort.B, DevicePort.A]; + } + if ( + distance2( + this.localToCanvasPoint(this.getStartPoint()), + section.localToCanvasPoint(section.getEndPoint()), + ) <= tolerance + ) { + param = [DevicePort.A, DevicePort.B]; + } + if ( + distance2( + this.localToCanvasPoint(this.getEndPoint()), + section.localToCanvasPoint(section.getEndPoint()), + ) <= tolerance + ) { + param = [DevicePort.B, DevicePort.B]; + } + if (param.length) { + this.relationManage.addRelation( + new GraphicRelationParam(this, param[0]), + new GraphicRelationParam(section, param[1]), + ); + } + }); + } + } + + saveRelations() { + const paRelation = this.relationManage + .getRelationsOfGraphic(this) + .find( + (relation) => + relation.getRelationParam(this).param === DevicePort.A && + (relation.getOtherGraphic(this) instanceof Section || + relation.getOtherGraphic(this) instanceof Turnout), + ); + const paDevice = paRelation?.getOtherGraphic
(this); + if (paDevice) { + this.datas.paRef = IRelatedRef.create( + paDevice.type, + paDevice.id, + paRelation!.getOtherRelationParam(this).getParam(), + ); + } else { + this.datas.paRef = undefined; + } + const pbRelation = this.relationManage + .getRelationsOfGraphic(this) + .find( + (relation) => + relation.getRelationParam(this).param === DevicePort.B && + (relation.getOtherGraphic(this) instanceof Section || + relation.getOtherGraphic(this) instanceof Turnout), + ); + const pbDevice = pbRelation?.getOtherGraphic
(this); + if (pbDevice) { + this.datas.pbRef = IRelatedRef.create( + pbDevice.type, + pbDevice.id, + pbRelation?.getOtherRelationParam(this).param, + ); + } else { + this.datas.pbRef = undefined; + } + this.datas.axleCountings = this.relationManage + .getRelationsOfGraphicAndOtherType(this, AxleCounting.Type) + .map((relation) => relation.getOtherGraphic(this).datas.id); + } + + loadRelations() { + if (this.datas?.paRef?.id) { + this.relationManage.addRelation( + new GraphicRelationParam(this, DevicePort.A), + new GraphicRelationParam( + this.queryStore.queryById(this.datas.paRef.id), + DevicePort[this.datas.paRef.devicePort], + ), + ); + } + if (this.datas?.pbRef?.id) { + this.relationManage.addRelation( + new GraphicRelationParam(this, DevicePort.B), + new GraphicRelationParam( + this.queryStore.queryById(this.datas.pbRef.id), + DevicePort[this.datas.pbRef.devicePort], + ), + ); + } + if (this.datas.trackSectionId) { + this.relationManage.addRelation( + this, + this.queryStore.queryById
(this.datas.trackSectionId), + ); + } + if (this.datas.sectionType === SectionType.TurnoutPhysical) { + if (this.datas.axleCountings) { + this.datas.axleCountings.forEach((id) => { + this.relationManage.addRelation( + this, + this.queryStore.queryById(id), + ); + }); + } + } + } +} + +export class SectionTemplate extends JlGraphicTemplate
{ + constructor(dataTemplate: ISectionData, stateTemplate?: ISectionState) { + super(Section.Type, { + dataTemplate, + stateTemplate, + }); + } + new(): Section { + const section = new Section(); + section.loadData(this.datas); + section.loadState(this.states); + return section; + } +} diff --git a/src/packages/Section/SectionGraphic.ts b/src/packages/Section/common/SectionGraphic.ts similarity index 100% rename from src/packages/Section/SectionGraphic.ts rename to src/packages/Section/common/SectionGraphic.ts