From 3850aa64cbc9fd3a60bff6c6907a8aba13a913a7 Mon Sep 17 00:00:00 2001 From: joylink_zhaoerwei Date: Thu, 18 Jan 2024 10:10:20 +0800 Subject: [PATCH] =?UTF-8?q?=E9=81=93=E5=B2=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../packages/Platform/common/JlPlatform.js | 2 +- components/packages/Section/GPSection.d.ts | 4 +- components/packages/Section/GPSection.js | 4 +- .../packages/Section/common/JlSection.d.ts | 74 +++++ .../packages/Section/common/JlSection.js | 240 ++++++++++++++ .../Section/common/SectionDrawAssistant.d.ts | 2 +- .../Section/common/SectionDrawAssistant.js | 2 +- components/packages/Turnout/GPTurnout.d.ts | 24 ++ components/packages/Turnout/GPTurnout.js | 28 ++ components/packages/Turnout/THTurnout.d.ts | 40 +++ components/packages/Turnout/THTurnout.js | 227 +++++++++++++ .../packages/Turnout/common/JlTurnout.d.ts | 14 +- .../packages/Turnout/common/JlTurnout.js | 135 +++++++- .../Turnout/common/TurnoutConfig.d.ts | 28 +- .../packages/Turnout/common/TurnoutConfig.js | 24 +- src/packages/Platform/common/JlPlatform.ts | 2 +- src/packages/Section/GPSection.ts | 4 +- .../common/{Section.ts => JlSection.ts} | 3 +- .../Section/common/SectionDrawAssistant.ts | 2 +- src/packages/Turnout/GPTurnout.ts | 49 +++ src/packages/Turnout/THTurnout.ts | 304 ++++++++++++++++++ src/packages/Turnout/common/JlTurnout.ts | 226 ++++++++++++- src/packages/Turnout/common/TurnoutConfig.ts | 31 +- 23 files changed, 1423 insertions(+), 46 deletions(-) create mode 100644 components/packages/Section/common/JlSection.d.ts create mode 100644 components/packages/Section/common/JlSection.js create mode 100644 components/packages/Turnout/THTurnout.d.ts create mode 100644 components/packages/Turnout/THTurnout.js rename src/packages/Section/common/{Section.ts => JlSection.ts} (99%) create mode 100644 src/packages/Turnout/THTurnout.ts diff --git a/components/packages/Platform/common/JlPlatform.js b/components/packages/Platform/common/JlPlatform.js index 3d88347..4d27f1f 100644 --- a/components/packages/Platform/common/JlPlatform.js +++ b/components/packages/Platform/common/JlPlatform.js @@ -1,6 +1,6 @@ import { calculateMirrorPoint, VectorText, getRectangleCenter, JlGraphic, distance2 } from 'jl-graphic'; import { Container, Graphics, Color, Point, Rectangle } from 'pixi.js'; -import { JlSection } from '../../Section/common/Section.js'; +import { JlSection } from '../../Section/common/JlSection.js'; import { JlStation } from '../../Station/common/JlStation.js'; //子元素--矩形 diff --git a/components/packages/Section/GPSection.d.ts b/components/packages/Section/GPSection.d.ts index 42beef4..10f10f9 100644 --- a/components/packages/Section/GPSection.d.ts +++ b/components/packages/Section/GPSection.d.ts @@ -1,5 +1,5 @@ -import { JlSection as SectionBase } from './common/Section'; +import { JlSection as SectionBase } from './common/JlSection'; export declare class Section extends SectionBase { constructor(); } -export { SectionTemplate } from './common/Section'; +export { SectionTemplate } from './common/JlSection'; diff --git a/components/packages/Section/GPSection.js b/components/packages/Section/GPSection.js index acbab2d..7c1265d 100644 --- a/components/packages/Section/GPSection.js +++ b/components/packages/Section/GPSection.js @@ -1,5 +1,5 @@ -import { JlSection } from './common/Section.js'; -export { SectionTemplate } from './common/Section.js'; +import { JlSection } from './common/JlSection.js'; +export { SectionTemplate } from './common/JlSection.js'; const displayConfig = { lineColor: '#5578b6', diff --git a/components/packages/Section/common/JlSection.d.ts b/components/packages/Section/common/JlSection.d.ts new file mode 100644 index 0000000..0fd3fa8 --- /dev/null +++ b/components/packages/Section/common/JlSection.d.ts @@ -0,0 +1,74 @@ +import { GraphicData, GraphicState, JlGraphic, JlGraphicTemplate, VectorText } from 'jl-graphic'; +import { IPointData } from 'pixi.js'; +import { SectionGraphic } from './SectionGraphic'; +import { DevicePort, IRelatedRef } from 'common/common'; +import { JlTurnout } from 'src/packages/Turnout/common/JlTurnout'; +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; + children?: number[]; + clone(): ISectionData; + copyFrom(data: ISectionData): void; + eq(other: ISectionData): boolean; +} +export interface ISectionState extends GraphicState { + id: number; + occupied: boolean; +} +export declare enum SectionType { + Physical = 0,//物理区段 + Logic = 1,//逻辑区段 + TurnoutPhysical = 2,//道岔物理区段 + Track = 4,//轨道区段 + TrackLogic = 5 +} +export interface SectionDisplayConfig { + lineColor: string; + occupiedColor: string; + lineWidth: number; +} +export declare const defaultDisplayConfig: SectionDisplayConfig; +export declare class JlSection extends JlGraphic { + static Type: string; + lineGraphic: SectionGraphic; + labelGraphic: VectorText; + displayConfig: SectionDisplayConfig; + constructor(); + setDisplayConfig(config: SectionDisplayConfig): void; + getVerticesList(): IPointData[]; + getStartPoint(): IPointData; + getEndPoint(): IPointData; + doRepaint(): void; + get datas(): ISectionData; + get states(): ISectionState; + get linePoints(): IPointData[]; + set linePoints(points: IPointData[]); + getConnectElement(port: DevicePort): { + g: JlTurnout | JlSection; + port: DevicePort; + } | undefined; + /** 获取拆分逻辑区段数据 */ + getSplitPoints(count: number): IPointData[][]; + /** + * * Relation处理考虑按不同区段类型分别处理或交子类处理后导出子类 + * + * + */ + buildRelation(): void; + saveRelations(): void; + loadRelations(): void; +} +export declare class SectionTemplate extends JlGraphicTemplate { + isCurve: boolean; + segmentsCount: number; + constructor(dataTemplate: ISectionData, stateTemplate?: ISectionState); + new(): JlSection; +} diff --git a/components/packages/Section/common/JlSection.js b/components/packages/Section/common/JlSection.js new file mode 100644 index 0000000..3668d76 --- /dev/null +++ b/components/packages/Section/common/JlSection.js @@ -0,0 +1,240 @@ +import { JlGraphic, VectorText, convertToBezierParams, Vector2, splitLineEvenly, distance2, GraphicRelationParam, JlGraphicTemplate } from 'jl-graphic'; +import { SectionGraphic } from './SectionGraphic.js'; +import { DevicePort, IRelatedRef } from '../../../common/common.js'; +import { JlTurnout } from '../../Turnout/common/JlTurnout.js'; +import { AxleCounting } from '../../AxleCounting/AxleCounting.js'; + +const tolerance = 0.01; +var SectionType; +(function (SectionType) { + SectionType[SectionType["Physical"] = 0] = "Physical"; + SectionType[SectionType["Logic"] = 1] = "Logic"; + SectionType[SectionType["TurnoutPhysical"] = 2] = "TurnoutPhysical"; + SectionType[SectionType["Track"] = 4] = "Track"; + SectionType[SectionType["TrackLogic"] = 5] = "TrackLogic"; +})(SectionType || (SectionType = {})); +const defaultDisplayConfig = { + lineColor: '#5578b6', + occupiedColor: '#f00', + lineWidth: 5, +}; +class JlSection extends JlGraphic { + static Type = 'Section'; + lineGraphic; + labelGraphic; + displayConfig = defaultDisplayConfig; + constructor() { + super(JlSection.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) { + this.displayConfig = config; + } + getVerticesList() { + 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() { + return this.datas.points[this.datas.points.length - 1]; + } + doRepaint() { + 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() { + return this.getDatas(); + } + get states() { + return this.getStates(); + } + get linePoints() { + return this.datas.points; + } + set linePoints(points) { + const old = this.datas.clone(); + old.points = points; + this.updateData(old); + } + getConnectElement(port) { + const relation = this.relationManage + .getRelationsOfGraphic(this) + .find((relation) => relation.getRelationParam(this).getParam() === port && + (relation.getOtherGraphic(this) instanceof JlSection || + relation.getOtherGraphic(this) instanceof JlTurnout)); + if (!relation) { + return; + } + return { + g: relation?.getOtherGraphic(this), + port: relation?.getOtherRelationParam(this).getParam(), + }; + } + /** 获取拆分逻辑区段数据 */ + getSplitPoints(count) { + if (this.datas.points.length !== 2) { + let totalLen = 0; + const lengths = []; + 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); + } + } + //TODO + /** + * * Relation处理考虑按不同区段类型分别处理或交子类处理后导出子类 + * + * + */ + buildRelation() { + this.relationManage.deleteRelationOfGraphicAndOtherType(this, JlSection.Type); + if (this.datas.sectionType === SectionType.Physical) { + this.queryStore + .queryByType(JlSection.Type) + .forEach((section) => { + if (section.id === this.id) + return; + let param = []; + 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 JlSection || + relation.getOtherGraphic(this) instanceof JlTurnout)); + 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 JlSection || + relation.getOtherGraphic(this) instanceof JlTurnout)); + 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), 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), 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)); + }); + } + } + } +} +class SectionTemplate extends JlGraphicTemplate { + isCurve = false; + segmentsCount = 10; + constructor(dataTemplate, stateTemplate) { + super(JlSection.Type, { + dataTemplate, + stateTemplate, + }); + } + new() { + const section = new JlSection(); + section.loadData(this.datas); + section.loadState(this.states); + return section; + } +} + +export { JlSection, SectionTemplate, SectionType, defaultDisplayConfig }; diff --git a/components/packages/Section/common/SectionDrawAssistant.d.ts b/components/packages/Section/common/SectionDrawAssistant.d.ts index 42ca07a..7f74d92 100644 --- a/components/packages/Section/common/SectionDrawAssistant.d.ts +++ b/components/packages/Section/common/SectionDrawAssistant.d.ts @@ -1,5 +1,5 @@ import { GraphicDrawAssistant, GraphicInteractionPlugin, IDrawApp, IGraphicApp, JlGraphic, KeyListener, MenuItemOptions } from 'jl-graphic'; -import { ISectionData, SectionTemplate } from './Section'; +import { ISectionData, SectionTemplate } from './JlSection'; import { Point, Graphics, type FederatedMouseEvent, type IHitArea, type DisplayObject } from 'pixi.js'; import { Section } from '../GPSection'; export declare class SectionDraw extends GraphicDrawAssistant { diff --git a/components/packages/Section/common/SectionDrawAssistant.js b/components/packages/Section/common/SectionDrawAssistant.js index c561ab3..e8dee15 100644 --- a/components/packages/Section/common/SectionDrawAssistant.js +++ b/components/packages/Section/common/SectionDrawAssistant.js @@ -1,5 +1,5 @@ import { ContextMenu, GraphicDrawAssistant, KeyListener, calculateMirrorPoint, convertToBezierParams, pointPolygon, linePoint, GraphicInteractionPlugin, getWaypointRangeIndex, addWayPoint, clearWayPoint, PolylineEditPlugin, VectorText, AppConsts, BezierCurveEditPlugin, AbsorbableLine, AbsorbablePoint } from 'jl-graphic'; -import { SectionType, defaultDisplayConfig } from './Section.js'; +import { SectionType, defaultDisplayConfig } from './JlSection.js'; import { Graphics, Point } from 'pixi.js'; import { JlTurnout } from '../../Turnout/common/JlTurnout.js'; import { Section } from '../GPSection.js'; diff --git a/components/packages/Turnout/GPTurnout.d.ts b/components/packages/Turnout/GPTurnout.d.ts index c9c186c..93ca2b7 100644 --- a/components/packages/Turnout/GPTurnout.d.ts +++ b/components/packages/Turnout/GPTurnout.d.ts @@ -1,5 +1,29 @@ +import { GraphicState } from 'jl-graphic'; import { JlTurnout } from './common/JlTurnout'; +export interface IGPTurnoutState extends GraphicState { + id?: number; + normal?: boolean; + reverse?: boolean; + dw?: boolean; + fw?: boolean; + force?: boolean; + sb?: boolean; + dwsb?: boolean; + fwsb?: boolean; + jc?: boolean; + qdc?: boolean; + qfc?: boolean; + qyc?: boolean; + dc?: boolean; + fc?: boolean; + yc?: boolean; + occupied?: boolean; +} export declare class GPTurnout extends JlTurnout { constructor(); + get states(): IGPTurnoutState; doRepaint(): void; + buildRelation(): void; + saveRelations(): void; + loadRelations(): void; } diff --git a/components/packages/Turnout/GPTurnout.js b/components/packages/Turnout/GPTurnout.js index 7717a37..ed28076 100644 --- a/components/packages/Turnout/GPTurnout.js +++ b/components/packages/Turnout/GPTurnout.js @@ -5,8 +5,36 @@ class GPTurnout extends JlTurnout { constructor() { super(GPConsts); } + get states() { + return this.getStates(); + } doRepaint() { + const { pointB, pointC } = this.datas; + if (this.states.dw) { + this.graphics.fork.paint(pointB[0]); + this.graphics.label.style.stroke = GPConsts.normalLabelColor; + } + else if (this.states.fw) { + this.graphics.fork.paint(pointC[0]); + this.graphics.label.style.stroke = GPConsts.reverseLabelColor; + } super.draw(); + if (!this.states.dw && !this.states.fw) { + // 失表 + this.graphics.fork.visible = false; + } + else { + this.graphics.fork.visible = true; + } + } + buildRelation() { + super.buildCommonRelation(); + } + saveRelations() { + super.saveCommonRelations(); + } + loadRelations() { + super.loadCommonRelations(); } } diff --git a/components/packages/Turnout/THTurnout.d.ts b/components/packages/Turnout/THTurnout.d.ts new file mode 100644 index 0000000..6414cf4 --- /dev/null +++ b/components/packages/Turnout/THTurnout.d.ts @@ -0,0 +1,40 @@ +import { Graphics, IPointData } from 'pixi.js'; +import { JlTurnout } from './common/JlTurnout'; +import { GraphicAnimation, GraphicState } from 'jl-graphic'; +export interface ITHTurnoutState extends GraphicState { + ipSingleSwitchStusCiOccupied: boolean; + ipSingleSwitchStusCbtcOccupied: boolean; + ipSingleSwitchStusLocked: boolean; + ipSingleSwitchStusFailLocked: boolean; + ipSingleSwitchStusNormal: boolean; + ipSingleSwitchStusReverse: boolean; + ipSingleSwitchStusBlocked1: boolean; + ipSingleSwitchStusJammed: boolean; + ipSingleSwitchStusCut: boolean; + ipSingleSwitchStusAtcInvalid: boolean; + ipSingleSwitchStusOverlap: boolean; + ipSingleSwitchStusTsrCbtcMain: boolean; + ipSingleSwitchStusTsrCbtcNormal: boolean; + ipSingleSwitchStusTsrCbtcReverse: boolean; + ipSingleSwitchStusTsrBmMain: boolean; + ipSingleSwitchStusTsrBmNormal: boolean; + ipSingleSwitchStusTsrBmReverse: boolean; + ipSingleSwitchStusBlocked2: boolean; + ipSingleSwitchStusLostIndication: boolean; + id: string; + speedLimit: number; + rtuId: number; +} +export declare class THTurnout extends JlTurnout { + labelRect: Graphics; + speedLimit: Graphics; + deltaTime: number; + constructor(); + get states(): ITHTurnoutState; + doRepaint(): void; + getSpeedLimitLinePoints(conf: 'normal' | 'reverse' | 'main'): IPointData[][]; + bindFlashAnimation(gList: Graphics[]): GraphicAnimation; + buildRelation(): void; + saveRelations(): void; + loadRelations(): void; +} diff --git a/components/packages/Turnout/THTurnout.js b/components/packages/Turnout/THTurnout.js new file mode 100644 index 0000000..bc6697e --- /dev/null +++ b/components/packages/Turnout/THTurnout.js @@ -0,0 +1,227 @@ +import { Graphics } from 'pixi.js'; +import { JlTurnout } from './common/JlTurnout.js'; +import { THConsts } from './common/TurnoutConfig.js'; +import { Vector2, getParallelOfPolyline, GraphicAnimation } from 'jl-graphic'; +import { JlSection, SectionType } from '../Section/common/JlSection.js'; +import { THStation } from '../Station/THStation.js'; + +const TurnoutLabelColor = { + GREEN: '#0f0', + YELLOW: '#ff0', + RED: '#f00', + WHITE: '#fff', +}; +//ForkGraphic单独处理 +class THTurnout extends JlTurnout { + labelRect; + speedLimit; + deltaTime; + constructor() { + super(THConsts); + this.name = 'turnout'; + this.labelRect = new Graphics(); + this.speedLimit = new Graphics(); + this.addChild(this.labelRect); + this.addChild(this.speedLimit); + this.deltaTime = 0; + } + get states() { + return this.getStates(); + } + doRepaint() { + //线条颜色 + const station = this.queryStore.queryByCodeAndType(this.states.rtuId > 9 ? '' + this.states.rtuId : '0' + this.states.rtuId, THStation.Type); + if (station?.states.ipRtuStusDown) { + this.graphics.sections.forEach((g) => (g.stateFillColor = THConsts.blueShowColor)); + } + else if (this.states.ipSingleSwitchStusCbtcOccupied) { + this.graphics.sections.forEach((g) => (g.stateFillColor = THConsts.cbtcOccupiedColor)); + } + else if (this.states.ipSingleSwitchStusCiOccupied) { + this.graphics.sections.forEach((g) => (g.stateFillColor = THConsts.ciOccupiedColor)); + } + else if (this.states.ipSingleSwitchStusLocked || + this.states.ipSingleSwitchStusFailLocked) { + this.graphics.sections.forEach((g) => (g.stateFillColor = THConsts.lockedColor)); + } + else if (this.states.ipSingleSwitchStusAtcInvalid) { + this.graphics.sections.forEach((g) => (g.stateFillColor = THConsts.atcInvalidColor)); + } + else if (this.states.ipSingleSwitchStusOverlap) { + this.graphics.sections.forEach((g) => (g.stateFillColor = THConsts.overlapColor)); + } + else { + this.graphics.sections.forEach((g) => (g.stateFillColor = THConsts.idleColor)); + } + super.draw(); + const { pointB, pointC } = this.datas; + this.graphics.fork.paint(pointB[0]); + this.graphics.fork.paint(pointC[0]); + this.graphics.label.text = this.datas.code; + //文字颜色 + if (this.states.ipSingleSwitchStusBlocked1) { + this.graphics.label.style.fill = TurnoutLabelColor.RED; + } + else if (this.states.ipSingleSwitchStusNormal) { + this.graphics.label.style.fill = TurnoutLabelColor.GREEN; + } + else if (this.states.ipSingleSwitchStusReverse) { + this.graphics.label.style.fill = TurnoutLabelColor.YELLOW; + } + else if (this.states.ipSingleSwitchStusJammed || + this.states.ipSingleSwitchStusLostIndication) { + this.graphics.label.style.fill = TurnoutLabelColor.WHITE; + } + super.draw(); + this.labelRect.clear(); + this.speedLimit.clear(); + this.graphics.fork.visible = true; + this.removeAnimation('flash'); + //文字框 + if (this.states.ipSingleSwitchStusBlocked2) { + this.labelRect.clear().lineStyle(1, '#f00'); + const { width, height } = this.graphics.label.getLocalBounds(); + const { x, y } = this.graphics.label.transform.position; + this.labelRect.drawRect(x - width / 2, y - height / 2, width, height); + } + if ((this.states.speedLimit && this.states.speedLimit > 0) || + this.states.ipSingleSwitchStusTsrBmMain || + this.states.ipSingleSwitchStusTsrBmNormal || + this.states.ipSingleSwitchStusTsrBmReverse || + this.states.ipSingleSwitchStusTsrCbtcMain || + this.states.ipSingleSwitchStusTsrCbtcNormal || + this.states.ipSingleSwitchStusTsrCbtcReverse) { + let limitConf; + if (this.states.ipSingleSwitchStusTsrBmReverse || + this.states.ipSingleSwitchStusTsrCbtcReverse) { + limitConf = 'reverse'; + } + else if (this.states.ipSingleSwitchStusTsrBmNormal || + this.states.ipSingleSwitchStusTsrCbtcNormal) { + limitConf = 'normal'; + } + else { + limitConf = 'main'; + } + const points = this.getSpeedLimitLinePoints(limitConf); + this.speedLimit.lineStyle(1, '#ff0'); + points.forEach((ps) => { + this.speedLimit.moveTo(ps[0].x, ps[0].y); + for (let i = 1; i < ps.length; i++) { + this.speedLimit.lineTo(ps[i].x, ps[i].y); + } + }); + } + if (this.states.ipSingleSwitchStusCut) { + if (this.states.ipSingleSwitchStusNormal) { + this.bindFlashAnimation([ + this.graphics.fork, + this.graphics.sections[0], + this.graphics.sections[1], + ]); + } + else if (this.states.ipSingleSwitchStusReverse) { + this.bindFlashAnimation([ + this.graphics.fork, + this.graphics.sections[0], + this.graphics.sections[2], + ]); + } + this.animation('flash')?.resume(); + } + } + getSpeedLimitLinePoints(conf) { + const [pa, pb, pc] = [ + this.datas.pointA[0], + this.datas.pointB[0], + this.datas.pointC[0], + ]; + const [va, vb, vc] = [ + new Vector2([pa.x, pa.y]), + new Vector2([pb.x, pb.y]), + new Vector2([pc.x, pc.y]), + ]; + const [vab, vbc] = [vb.subtract(va), vc.subtract(vb)]; + vab.x * vbc.y - vab.y * vbc.x; + const offset = 10; + if (conf === 'main') { + return [ + getParallelOfPolyline([ + ...this.datas.pointA.map((p) => ({ x: p.x, y: p.y })).reverse(), + { x: 0, y: 0 }, + ], offset, 'L'), + getParallelOfPolyline([ + ...this.datas.pointA.map((p) => ({ x: p.x, y: p.y })).reverse(), + { x: 0, y: 0 }, + ], offset, 'R'), + ]; + } + else if (conf === 'normal') { + return [ + getParallelOfPolyline([ + ...this.datas.pointA.map((p) => ({ x: p.x, y: p.y })).reverse(), + { x: 0, y: 0 }, + ...this.datas.pointB.map((p) => ({ x: p.x, y: p.y })), + ], offset, 'L'), + getParallelOfPolyline([ + ...this.datas.pointA.map((p) => ({ x: p.x, y: p.y })).reverse(), + { x: 0, y: 0 }, + ...this.datas.pointB.map((p) => ({ x: p.x, y: p.y })), + ], offset, 'R'), + ]; + } + else { + return [ + getParallelOfPolyline([ + ...this.datas.pointA.map((p) => ({ x: p.x, y: p.y })).reverse(), + { x: 0, y: 0 }, + ...this.datas.pointC.map((p) => ({ x: p.x, y: p.y })), + ], offset, 'L'), + getParallelOfPolyline([ + ...this.datas.pointA.map((p) => ({ x: p.x, y: p.y })).reverse(), + { x: 0, y: 0 }, + ...this.datas.pointC.map((p) => ({ x: p.x, y: p.y })), + ], offset, 'R'), + ]; + } + } + bindFlashAnimation(gList) { + const flashAnimation = GraphicAnimation.init({ + name: 'flash', + run: (dt) => { + this.deltaTime += dt; + if (this.deltaTime > 60) { + this.deltaTime = 0; + gList.forEach((g) => (g.visible = true)); + } + else if (this.deltaTime > 30) { + gList.forEach((g) => (g.visible = false)); + } + }, + }); + this.addAnimation(flashAnimation); + return flashAnimation; + } + buildRelation() { + super.buildCommonRelation(); + this.queryStore + .queryByType(JlSection.Type) + .forEach((section) => { + if (section.datas.sectionType === SectionType.TurnoutPhysical) { + if (section.datas.children && + section.datas.children.includes(this.datas.id)) { + this.relationManage.addRelation(this, section); + } + return; + } + }); + } + saveRelations() { + super.saveCommonRelations(); + } + loadRelations() { + super.loadCommonRelations(); + } +} + +export { THTurnout }; diff --git a/components/packages/Turnout/common/JlTurnout.d.ts b/components/packages/Turnout/common/JlTurnout.d.ts index d02ea48..1244644 100644 --- a/components/packages/Turnout/common/JlTurnout.d.ts +++ b/components/packages/Turnout/common/JlTurnout.d.ts @@ -2,8 +2,9 @@ import { JlGraphic, VectorText } from 'jl-graphic'; import { IPointData, Graphics } from 'pixi.js'; import { DevicePort } from 'common/common'; import { ITurnoutData, TurnoutConstsConfig } from './TurnoutConfig'; +import { JlSection } from 'src/packages/Section/common/JlSection'; export declare function getForkPoint(r: number, p: IPointData): IPointData; -export declare class TurnoutSection extends Graphics { +declare class TurnoutSection extends Graphics { turnoutConsts: TurnoutConstsConfig; turnout: JlTurnout; port: DevicePort; @@ -26,9 +27,18 @@ export declare abstract class JlTurnout extends JlGraphic { label: VectorText; }; constructor(turnoutConsts: TurnoutConstsConfig); - draw(): void; get datas(): ITurnoutData; + get code(): string; + set code(code: string); + draw(): void; getPortPoints(): IPointData[][]; getGraphicOfPort(port: DevicePort): JlGraphic[]; + getConnectElement(port: DevicePort): { + g: JlTurnout | JlSection; + port: DevicePort; + } | undefined; + buildCommonRelation(): void; + saveCommonRelations(): void; + loadCommonRelations(): void; } export {}; diff --git a/components/packages/Turnout/common/JlTurnout.js b/components/packages/Turnout/common/JlTurnout.js index 4eb569a..2e3fc67 100644 --- a/components/packages/Turnout/common/JlTurnout.js +++ b/components/packages/Turnout/common/JlTurnout.js @@ -1,7 +1,9 @@ -import { JlGraphic, VectorText } from 'jl-graphic'; +import { JlGraphic, VectorText, distance2, GraphicRelationParam, angleOfIncludedAngle } from 'jl-graphic'; import { Graphics } from 'pixi.js'; -import { DevicePort } from '../../../common/common.js'; +import { DevicePort, IRelatedRef } from '../../../common/common.js'; +import { JlSection, SectionType } from '../../Section/common/JlSection.js'; +const tolerance = 0.01; function getForkPoint(r, p) { if (r === 0) return { x: 0, y: 0 }; @@ -64,7 +66,7 @@ class ForkGraphic extends Graphics { } } class JlTurnout extends JlGraphic { - static Type = 'Turnout'; + static Type = 'JlTurnout'; graphics; constructor(turnoutConsts) { super(JlTurnout.Type); @@ -90,16 +92,23 @@ class JlTurnout extends JlGraphic { this.graphics.label.name = 'label'; this.addChild(this.graphics.label); } + get datas() { + return this.getDatas(); + } + get code() { + return this.datas.code; + } + set code(code) { + this.datas.code = code; + } draw() { this.graphics.label.text = this.datas.code; this.graphics.sections.forEach((sectionGraphic) => sectionGraphic.paint()); } - get datas() { - return this.getDatas(); - } getPortPoints() { return [this.datas.pointA, this.datas.pointB, this.datas.pointC]; } + //端口关联的设备 getGraphicOfPort(port) { return this.relationManage .getRelationsOfGraphic(this) @@ -108,6 +117,118 @@ class JlTurnout extends JlGraphic { return relation.getOtherGraphic(this); }); } + //端口关联的区段或道岔 + getConnectElement(port) { + const relation = this.relationManage + .getRelationsOfGraphic(this) + .find((relation) => relation.getRelationParam(this).getParam() === port && + (relation.getOtherGraphic(this) instanceof JlSection || + relation.getOtherGraphic(this) instanceof JlTurnout)); + if (!relation) { + return; + } + return { + g: relation?.getOtherGraphic(this), + port: relation?.getOtherRelationParam(this).getParam(), + }; + } + buildCommonRelation() { + this.relationManage.deleteRelationOfGraphic(this); + /** 道岔和区段 */ + this.queryStore + .queryByType(JlSection.Type) + .forEach((section) => { + if (section.datas.sectionType !== SectionType.Physical) + return; + this.getPortPoints().forEach((port, i) => { + if (distance2(section.localToCanvasPoint(section.getStartPoint()), this.localToCanvasPoint(port[port.length - 1])) <= tolerance) { + this.relationManage.addRelation(new GraphicRelationParam(this, [DevicePort.A, DevicePort.B, DevicePort.C][i]), new GraphicRelationParam(section, DevicePort.A)); + } + if (distance2(section.localToCanvasPoint(section.getEndPoint()), this.localToCanvasPoint(port[port.length - 1])) <= tolerance) { + this.relationManage.addRelation(new GraphicRelationParam(this, [DevicePort.A, DevicePort.B, DevicePort.C][i]), new GraphicRelationParam(section, DevicePort.B)); + } + }); + }); + /** 道岔和道岔 */ + this.getPortPoints().forEach((thisPort, i) => { + let params = [], deflection = 180; + this.queryStore + .queryByType(JlTurnout.Type) + .forEach((turnout) => { + if (turnout.id === this.id) + return; + turnout.getPortPoints().forEach((otherPort, j) => { + if (distance2(this.localToCanvasPoint(thisPort[thisPort.length - 1]), turnout.localToCanvasPoint(otherPort[otherPort.length - 1])) <= tolerance) { + const angle = angleOfIncludedAngle(this.localToCanvasPoint(thisPort[thisPort.length - 1]) /* 交点 */, thisPort[thisPort.length - 2] + ? this.localToCanvasPoint(thisPort[thisPort.length - 2]) + : this.position, otherPort[otherPort.length - 2] + ? turnout.localToCanvasPoint(otherPort[otherPort.length - 2]) + : turnout.position); + if (180 - Math.abs(angle) <= deflection) { + deflection = 180 - Math.abs(angle); + params = [ + new GraphicRelationParam(this, [DevicePort.A, DevicePort.B, DevicePort.C][i]), + new GraphicRelationParam(turnout, [DevicePort.A, DevicePort.B, DevicePort.C][j]), + ]; + } + } + }); + }); + if (params.length === 2) { + this.relationManage.addRelation(params[0], params[1]); + } + }); + } + saveCommonRelations() { + const paRelation = this.relationManage + .getRelationsOfGraphic(this) + .find((relation) => relation.getRelationParam(this).param === DevicePort.A && + (relation.getOtherGraphic(this) instanceof JlSection || + relation.getOtherGraphic(this) instanceof JlTurnout)); + 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 JlSection || + relation.getOtherGraphic(this) instanceof JlTurnout)); + const pbDevice = pbRelation?.getOtherGraphic(this); + if (pbDevice) { + this.datas.pbRef = IRelatedRef.create(pbDevice.type, pbDevice.id, pbRelation.getOtherRelationParam(this).getParam()); + } + else { + this.datas.pbRef = undefined; + } + const pcRelation = this.relationManage + .getRelationsOfGraphic(this) + .find((relation) => relation.getRelationParam(this).param === DevicePort.C && + !(relation.getOtherGraphic(this) instanceof JlSection && + relation.getOtherGraphic(this).datas.sectionType !== + SectionType.TurnoutPhysical)); + const pcDevice = pcRelation?.getOtherGraphic(this); + if (pcDevice) { + this.datas.pcRef = IRelatedRef.create(pcDevice.type, pcDevice.id, pcRelation.getOtherRelationParam(this).getParam()); + } + else { + this.datas.pcRef = undefined; + } + } + loadCommonRelations() { + 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.pcRef?.id) { + this.relationManage.addRelation(new GraphicRelationParam(this, DevicePort.C), new GraphicRelationParam(this.queryStore.queryById(this.datas.pcRef.id), DevicePort[this.datas.pcRef.devicePort])); + } + } } -export { JlTurnout, TurnoutSection, getForkPoint }; +export { JlTurnout, getForkPoint }; diff --git a/components/packages/Turnout/common/TurnoutConfig.d.ts b/components/packages/Turnout/common/TurnoutConfig.d.ts index 2f18a18..73712fe 100644 --- a/components/packages/Turnout/common/TurnoutConfig.d.ts +++ b/components/packages/Turnout/common/TurnoutConfig.d.ts @@ -2,23 +2,38 @@ import { IRelatedRef, KilometerSystem } from 'common/common'; import { GraphicData } from 'jl-graphic'; import { IPointData } from 'pixi.js'; export interface TurnoutConstsConfig { - lineColor: string; - occupiedColor: string; lineWidth: number; + lineColor: string; forkLenth: number; labelFontSize: number; - normalLabelColor: string; - reverseLabelColor: string; } export declare const GPConsts: { - lineColor: string; - occupiedColor: string; lineWidth: number; + lineColor: string; forkLenth: number; labelFontSize: number; + occupiedColor: string; normalLabelColor: string; reverseLabelColor: string; }; +export declare const THConsts: { + lineWidth: number; + lineColor: string; + forkLenth: number; + labelFontSize: number; + idleColor: string; + jammedLineColor: string; + ciOccupiedColor: string; + cbtcOccupiedColor: string; + lockedColor: string; + atcInvalidColor: string; + overlapColor: string; + blueShowColor: string; + labelGREEN: string; + labelYELLOW: string; + labelRED: string; + labelWHITE: string; +}; export declare enum SwitchMachineType { Unknown = 0, ZDJ9_Single = 1, @@ -38,6 +53,7 @@ export interface ITurnoutData extends GraphicData { pcTrackSectionId?: number; switchMachineType?: SwitchMachineType; centralizedStations?: number[]; + centralizedStation?: number[]; clone(): ITurnoutData; copyFrom(data: ITurnoutData): void; eq(other: ITurnoutData): boolean; diff --git a/components/packages/Turnout/common/TurnoutConfig.js b/components/packages/Turnout/common/TurnoutConfig.js index f494a2e..30abeec 100644 --- a/components/packages/Turnout/common/TurnoutConfig.js +++ b/components/packages/Turnout/common/TurnoutConfig.js @@ -1,12 +1,30 @@ const GPConsts = { - lineColor: '#5578b6', - occupiedColor: '#f00', lineWidth: 5, + lineColor: '#5578b6', forkLenth: 20, labelFontSize: 12, + occupiedColor: '#f00', normalLabelColor: '#0f0', reverseLabelColor: '#ff0', }; +const THConsts = { + lineWidth: 5, + lineColor: '#5578b6', + forkLenth: 20, + labelFontSize: 12, + idleColor: '#888', //空闲 + jammedLineColor: '#f00', //挤岔 + ciOccupiedColor: '#f00', //非通信车占用 + cbtcOccupiedColor: '#f49', //通信车占用 + lockedColor: '#fff', //锁闭 && 故障锁闭 + atcInvalidColor: '#954', //atc报告失效 + overlapColor: '#ff0', //overlap + blueShowColor: '0x3149c3', + labelGREEN: '#0f0', + labelYELLOW: '#ff0', + labelRED: '#f00', + labelWHITE: '#fff', +}; var SwitchMachineType; (function (SwitchMachineType) { SwitchMachineType[SwitchMachineType["Unknown"] = 0] = "Unknown"; @@ -14,4 +32,4 @@ var SwitchMachineType; SwitchMachineType[SwitchMachineType["ZDJ9_Double"] = 2] = "ZDJ9_Double"; })(SwitchMachineType || (SwitchMachineType = {})); -export { GPConsts, SwitchMachineType }; +export { GPConsts, SwitchMachineType, THConsts }; diff --git a/src/packages/Platform/common/JlPlatform.ts b/src/packages/Platform/common/JlPlatform.ts index 6d9b227..f3250d9 100644 --- a/src/packages/Platform/common/JlPlatform.ts +++ b/src/packages/Platform/common/JlPlatform.ts @@ -19,7 +19,7 @@ import { LozengeConstsConfig, PlatformConstsConfig, } from './PlatformConfig'; -import { JlSection } from '../../Section/common/Section'; +import { JlSection } from '../../Section/common/JlSection'; import { JlStation } from '../../Station/common/JlStation'; //子元素--矩形 diff --git a/src/packages/Section/GPSection.ts b/src/packages/Section/GPSection.ts index e706754..2a74ec0 100644 --- a/src/packages/Section/GPSection.ts +++ b/src/packages/Section/GPSection.ts @@ -1,7 +1,7 @@ import { JlSection as SectionBase, SectionDisplayConfig, -} from './common/Section'; +} from './common/JlSection'; const displayConfig: SectionDisplayConfig = { lineColor: '#5578b6', @@ -16,4 +16,4 @@ export class Section extends SectionBase { } } -export { SectionTemplate } from './common/Section'; +export { SectionTemplate } from './common/JlSection'; diff --git a/src/packages/Section/common/Section.ts b/src/packages/Section/common/JlSection.ts similarity index 99% rename from src/packages/Section/common/Section.ts rename to src/packages/Section/common/JlSection.ts index b558a4e..7d3a67e 100644 --- a/src/packages/Section/common/Section.ts +++ b/src/packages/Section/common/JlSection.ts @@ -12,7 +12,7 @@ import { } from 'jl-graphic'; import { IPointData } from 'pixi.js'; import { SectionGraphic } from './SectionGraphic'; -import { DevicePort, DeviceType, IRelatedRef } from 'common/common'; +import { DevicePort, IRelatedRef } from 'common/common'; import { JlTurnout } from 'src/packages/Turnout/common/JlTurnout'; import { AxleCounting } from 'src/packages/AxleCounting/AxleCounting'; @@ -29,6 +29,7 @@ export interface ISectionData extends GraphicData { axleCountings?: number[]; centralizedStations?: number[]; trackSectionId?: number; + children?: number[]; clone(): ISectionData; copyFrom(data: ISectionData): void; eq(other: ISectionData): boolean; diff --git a/src/packages/Section/common/SectionDrawAssistant.ts b/src/packages/Section/common/SectionDrawAssistant.ts index 6d8e9bc..13cd84f 100644 --- a/src/packages/Section/common/SectionDrawAssistant.ts +++ b/src/packages/Section/common/SectionDrawAssistant.ts @@ -34,7 +34,7 @@ import { SectionTemplate, SectionType, defaultDisplayConfig, -} from './Section'; +} from './JlSection'; import { Point, Graphics, diff --git a/src/packages/Turnout/GPTurnout.ts b/src/packages/Turnout/GPTurnout.ts index 60ca061..10e62a4 100644 --- a/src/packages/Turnout/GPTurnout.ts +++ b/src/packages/Turnout/GPTurnout.ts @@ -1,11 +1,60 @@ +import { GraphicState } from 'jl-graphic'; import { JlTurnout } from './common/JlTurnout'; import { GPConsts } from './common/TurnoutConfig'; +export interface IGPTurnoutState extends GraphicState { + id?: number; + normal?: boolean; + reverse?: boolean; + dw?: boolean; + fw?: boolean; + force?: boolean; + sb?: boolean; + dwsb?: boolean; + fwsb?: boolean; + jc?: boolean; + qdc?: boolean; + qfc?: boolean; + qyc?: boolean; + dc?: boolean; + fc?: boolean; + yc?: boolean; + occupied?: boolean; +} + export class GPTurnout extends JlTurnout { constructor() { super(GPConsts); } + get states(): IGPTurnoutState { + return this.getStates(); + } doRepaint(): void { + const { pointB, pointC } = this.datas; + if (this.states.dw) { + this.graphics.fork.paint(pointB[0]); + this.graphics.label.style.stroke = GPConsts.normalLabelColor; + } else if (this.states.fw) { + this.graphics.fork.paint(pointC[0]); + this.graphics.label.style.stroke = GPConsts.reverseLabelColor; + } + super.draw(); + + if (!this.states.dw && !this.states.fw) { + // 失表 + this.graphics.fork.visible = false; + } else { + this.graphics.fork.visible = true; + } + } + buildRelation() { + super.buildCommonRelation(); + } + saveRelations() { + super.saveCommonRelations(); + } + loadRelations() { + super.loadCommonRelations(); } } diff --git a/src/packages/Turnout/THTurnout.ts b/src/packages/Turnout/THTurnout.ts new file mode 100644 index 0000000..f7ed871 --- /dev/null +++ b/src/packages/Turnout/THTurnout.ts @@ -0,0 +1,304 @@ +import { Graphics, IPointData } from 'pixi.js'; +import { JlTurnout } from './common/JlTurnout'; +import { THConsts } from './common/TurnoutConfig'; +import { + GraphicAnimation, + GraphicState, + Vector2, + getParallelOfPolyline, +} from 'jl-graphic'; +import { JlSection, SectionType } from '../Section/common/JlSection'; +import { THStation } from '../Station/THStation'; + +const TurnoutLabelColor = { + GREEN: '#0f0', + YELLOW: '#ff0', + RED: '#f00', + WHITE: '#fff', +}; + +export interface ITHTurnoutState extends GraphicState { + ipSingleSwitchStusCiOccupied: boolean; + ipSingleSwitchStusCbtcOccupied: boolean; + ipSingleSwitchStusLocked: boolean; + ipSingleSwitchStusFailLocked: boolean; + ipSingleSwitchStusNormal: boolean; + ipSingleSwitchStusReverse: boolean; + ipSingleSwitchStusBlocked1: boolean; + ipSingleSwitchStusJammed: boolean; + ipSingleSwitchStusCut: boolean; + ipSingleSwitchStusAtcInvalid: boolean; + ipSingleSwitchStusOverlap: boolean; + ipSingleSwitchStusTsrCbtcMain: boolean; + ipSingleSwitchStusTsrCbtcNormal: boolean; + ipSingleSwitchStusTsrCbtcReverse: boolean; + ipSingleSwitchStusTsrBmMain: boolean; + ipSingleSwitchStusTsrBmNormal: boolean; + ipSingleSwitchStusTsrBmReverse: boolean; + ipSingleSwitchStusBlocked2: boolean; + ipSingleSwitchStusLostIndication: boolean; + id: string; + speedLimit: number; + rtuId: number; +} + +//ForkGraphic单独处理 +export class THTurnout extends JlTurnout { + labelRect: Graphics; + speedLimit: Graphics; + deltaTime: number; + constructor() { + super(THConsts); + this.name = 'turnout'; + this.labelRect = new Graphics(); + this.speedLimit = new Graphics(); + this.addChild(this.labelRect); + this.addChild(this.speedLimit); + this.deltaTime = 0; + } + get states(): ITHTurnoutState { + return this.getStates(); + } + doRepaint(): void { + //线条颜色 + const station = this.queryStore.queryByCodeAndType( + this.states.rtuId > 9 ? '' + this.states.rtuId : '0' + this.states.rtuId, + THStation.Type, + ); + if (station?.states.ipRtuStusDown) { + this.graphics.sections.forEach( + (g) => (g.stateFillColor = THConsts.blueShowColor), + ); + } else if (this.states.ipSingleSwitchStusCbtcOccupied) { + this.graphics.sections.forEach( + (g) => (g.stateFillColor = THConsts.cbtcOccupiedColor), + ); + } else if (this.states.ipSingleSwitchStusCiOccupied) { + this.graphics.sections.forEach( + (g) => (g.stateFillColor = THConsts.ciOccupiedColor), + ); + } else if ( + this.states.ipSingleSwitchStusLocked || + this.states.ipSingleSwitchStusFailLocked + ) { + this.graphics.sections.forEach( + (g) => (g.stateFillColor = THConsts.lockedColor), + ); + } else if (this.states.ipSingleSwitchStusAtcInvalid) { + this.graphics.sections.forEach( + (g) => (g.stateFillColor = THConsts.atcInvalidColor), + ); + } else if (this.states.ipSingleSwitchStusOverlap) { + this.graphics.sections.forEach( + (g) => (g.stateFillColor = THConsts.overlapColor), + ); + } else { + this.graphics.sections.forEach( + (g) => (g.stateFillColor = THConsts.idleColor), + ); + } + + super.draw(); + const { pointB, pointC } = this.datas; + this.graphics.fork.paint(pointB[0]); + this.graphics.fork.paint(pointC[0]); + + this.graphics.label.text = this.datas.code; + + //文字颜色 + if (this.states.ipSingleSwitchStusBlocked1) { + this.graphics.label.style.fill = TurnoutLabelColor.RED; + } else if (this.states.ipSingleSwitchStusNormal) { + this.graphics.label.style.fill = TurnoutLabelColor.GREEN; + } else if (this.states.ipSingleSwitchStusReverse) { + this.graphics.label.style.fill = TurnoutLabelColor.YELLOW; + } else if ( + this.states.ipSingleSwitchStusJammed || + this.states.ipSingleSwitchStusLostIndication + ) { + this.graphics.label.style.fill = TurnoutLabelColor.WHITE; + } + + super.draw(); + this.labelRect.clear(); + this.speedLimit.clear(); + this.graphics.fork.visible = true; + this.removeAnimation('flash'); + + //文字框 + if (this.states.ipSingleSwitchStusBlocked2) { + this.labelRect.clear().lineStyle(1, '#f00'); + const { width, height } = this.graphics.label.getLocalBounds(); + const { x, y } = this.graphics.label.transform.position; + this.labelRect.drawRect(x - width / 2, y - height / 2, width, height); + } + + if ( + (this.states.speedLimit && this.states.speedLimit > 0) || + this.states.ipSingleSwitchStusTsrBmMain || + this.states.ipSingleSwitchStusTsrBmNormal || + this.states.ipSingleSwitchStusTsrBmReverse || + this.states.ipSingleSwitchStusTsrCbtcMain || + this.states.ipSingleSwitchStusTsrCbtcNormal || + this.states.ipSingleSwitchStusTsrCbtcReverse + ) { + let limitConf: Parameters< + typeof THTurnout.prototype.getSpeedLimitLinePoints + >[0]; + if ( + this.states.ipSingleSwitchStusTsrBmReverse || + this.states.ipSingleSwitchStusTsrCbtcReverse + ) { + limitConf = 'reverse'; + } else if ( + this.states.ipSingleSwitchStusTsrBmNormal || + this.states.ipSingleSwitchStusTsrCbtcNormal + ) { + limitConf = 'normal'; + } else { + limitConf = 'main'; + } + const points = this.getSpeedLimitLinePoints(limitConf); + this.speedLimit.lineStyle(1, '#ff0'); + points.forEach((ps) => { + this.speedLimit.moveTo(ps[0].x, ps[0].y); + for (let i = 1; i < ps.length; i++) { + this.speedLimit.lineTo(ps[i].x, ps[i].y); + } + }); + } + + if (this.states.ipSingleSwitchStusCut) { + if (this.states.ipSingleSwitchStusNormal) { + this.bindFlashAnimation([ + this.graphics.fork, + this.graphics.sections[0], + this.graphics.sections[1], + ]); + } else if (this.states.ipSingleSwitchStusReverse) { + this.bindFlashAnimation([ + this.graphics.fork, + this.graphics.sections[0], + this.graphics.sections[2], + ]); + } + this.animation('flash')?.resume(); + } + } + getSpeedLimitLinePoints(conf: 'normal' | 'reverse' | 'main'): IPointData[][] { + const [pa, pb, pc] = [ + this.datas.pointA[0], + this.datas.pointB[0], + this.datas.pointC[0], + ]; + const [va, vb, vc] = [ + new Vector2([pa.x, pa.y]), + new Vector2([pb.x, pb.y]), + new Vector2([pc.x, pc.y]), + ]; + const [vab, vbc] = [vb.subtract(va), vc.subtract(vb)]; + const cpd = vab.x * vbc.y - vab.y * vbc.x; + const side = cpd > 0 ? 'L' : 'R'; //计算三个分支的手性 + const offset = 10; + if (conf === 'main') { + return [ + getParallelOfPolyline( + [ + ...this.datas.pointA.map((p) => ({ x: p.x, y: p.y })).reverse(), + { x: 0, y: 0 }, + ], + offset, + 'L', + ), + getParallelOfPolyline( + [ + ...this.datas.pointA.map((p) => ({ x: p.x, y: p.y })).reverse(), + { x: 0, y: 0 }, + ], + offset, + 'R', + ), + ]; + } else if (conf === 'normal') { + return [ + getParallelOfPolyline( + [ + ...this.datas.pointA.map((p) => ({ x: p.x, y: p.y })).reverse(), + { x: 0, y: 0 }, + ...this.datas.pointB.map((p) => ({ x: p.x, y: p.y })), + ], + offset, + 'L', + ), + getParallelOfPolyline( + [ + ...this.datas.pointA.map((p) => ({ x: p.x, y: p.y })).reverse(), + { x: 0, y: 0 }, + ...this.datas.pointB.map((p) => ({ x: p.x, y: p.y })), + ], + offset, + 'R', + ), + ]; + } else { + return [ + getParallelOfPolyline( + [ + ...this.datas.pointA.map((p) => ({ x: p.x, y: p.y })).reverse(), + { x: 0, y: 0 }, + ...this.datas.pointC.map((p) => ({ x: p.x, y: p.y })), + ], + offset, + 'L', + ), + getParallelOfPolyline( + [ + ...this.datas.pointA.map((p) => ({ x: p.x, y: p.y })).reverse(), + { x: 0, y: 0 }, + ...this.datas.pointC.map((p) => ({ x: p.x, y: p.y })), + ], + offset, + 'R', + ), + ]; + } + } + bindFlashAnimation(gList: Graphics[]) { + const flashAnimation = GraphicAnimation.init({ + name: 'flash', + run: (dt: number) => { + this.deltaTime += dt; + if (this.deltaTime > 60) { + this.deltaTime = 0; + gList.forEach((g) => (g.visible = true)); + } else if (this.deltaTime > 30) { + gList.forEach((g) => (g.visible = false)); + } + }, + }); + this.addAnimation(flashAnimation); + return flashAnimation; + } + buildRelation() { + super.buildCommonRelation(); + this.queryStore + .queryByType(JlSection.Type) + .forEach((section) => { + if (section.datas.sectionType === SectionType.TurnoutPhysical) { + if ( + section.datas.children && + section.datas.children.includes(this.datas.id) + ) { + this.relationManage.addRelation(this, section); + } + return; + } + }); + } + saveRelations() { + super.saveCommonRelations(); + } + loadRelations() { + super.loadCommonRelations(); + } +} diff --git a/src/packages/Turnout/common/JlTurnout.ts b/src/packages/Turnout/common/JlTurnout.ts index de78985..69499be 100644 --- a/src/packages/Turnout/common/JlTurnout.ts +++ b/src/packages/Turnout/common/JlTurnout.ts @@ -1,7 +1,16 @@ -import { JlGraphic, VectorText } from 'jl-graphic'; +import { + GraphicRelationParam, + JlGraphic, + VectorText, + angleOfIncludedAngle, + distance2, +} from 'jl-graphic'; import { IPointData, Graphics } from 'pixi.js'; -import { DevicePort } from 'common/common'; +import { DevicePort, IRelatedRef } from 'common/common'; import { ITurnoutData, TurnoutConstsConfig } from './TurnoutConfig'; +import { JlSection, SectionType } from 'src/packages/Section/common/JlSection'; + +const tolerance = 0.01; export function getForkPoint(r: number, p: IPointData): IPointData { if (r === 0) return { x: 0, y: 0 }; @@ -10,7 +19,7 @@ export function getForkPoint(r: number, p: IPointData): IPointData { return { x: scale * p.x, y: scale * p.y }; } -export class TurnoutSection extends Graphics { +class TurnoutSection extends Graphics { turnoutConsts: TurnoutConstsConfig; turnout: JlTurnout; port: DevicePort; @@ -73,7 +82,7 @@ class ForkGraphic extends Graphics { } export abstract class JlTurnout extends JlGraphic { - static Type = 'Turnout'; + static Type = 'JlTurnout'; graphics: { fork: ForkGraphic; sections: [TurnoutSection, TurnoutSection, TurnoutSection]; @@ -104,18 +113,23 @@ export abstract class JlTurnout extends JlGraphic { this.graphics.label.name = 'label'; this.addChild(this.graphics.label); } + get datas(): ITurnoutData { + return this.getDatas(); + } + get code(): string { + return this.datas.code; + } + set code(code: string) { + this.datas.code = code; + } draw(): void { this.graphics.label.text = this.datas.code; this.graphics.sections.forEach((sectionGraphic) => sectionGraphic.paint()); } - get datas(): ITurnoutData { - return this.getDatas(); - } - getPortPoints() { return [this.datas.pointA, this.datas.pointB, this.datas.pointC]; } - + //端口关联的设备 getGraphicOfPort(port: DevicePort) { return this.relationManage .getRelationsOfGraphic(this) @@ -127,4 +141,198 @@ export abstract class JlTurnout extends JlGraphic { return relation.getOtherGraphic(this); }); } + //端口关联的区段或道岔 + getConnectElement(port: DevicePort) { + const relation = this.relationManage + .getRelationsOfGraphic(this) + .find( + (relation) => + relation.getRelationParam(this).getParam() === port && + (relation.getOtherGraphic(this) instanceof JlSection || + relation.getOtherGraphic(this) instanceof JlTurnout), + ); + if (!relation) { + return; + } + return { + g: relation?.getOtherGraphic(this) as JlSection | JlTurnout, + port: relation?.getOtherRelationParam(this).getParam(), + }; + } + buildCommonRelation(): void { + this.relationManage.deleteRelationOfGraphic(this); + + /** 道岔和区段 */ + this.queryStore + .queryByType(JlSection.Type) + .forEach((section) => { + if (section.datas.sectionType !== SectionType.Physical) return; + this.getPortPoints().forEach((port, i) => { + if ( + distance2( + section.localToCanvasPoint(section.getStartPoint()), + this.localToCanvasPoint(port[port.length - 1]), + ) <= tolerance + ) { + this.relationManage.addRelation( + new GraphicRelationParam( + this, + [DevicePort.A, DevicePort.B, DevicePort.C][i], + ), + new GraphicRelationParam(section, DevicePort.A), + ); + } + if ( + distance2( + section.localToCanvasPoint(section.getEndPoint()), + this.localToCanvasPoint(port[port.length - 1]), + ) <= tolerance + ) { + this.relationManage.addRelation( + new GraphicRelationParam( + this, + [DevicePort.A, DevicePort.B, DevicePort.C][i], + ), + new GraphicRelationParam(section, DevicePort.B), + ); + } + }); + }); + + /** 道岔和道岔 */ + this.getPortPoints().forEach((thisPort, i) => { + let params: GraphicRelationParam[] = [], + deflection = 180; + this.queryStore + .queryByType(JlTurnout.Type) + .forEach((turnout) => { + if (turnout.id === this.id) return; + turnout.getPortPoints().forEach((otherPort, j) => { + if ( + distance2( + this.localToCanvasPoint(thisPort[thisPort.length - 1]), + turnout.localToCanvasPoint(otherPort[otherPort.length - 1]), + ) <= tolerance + ) { + const angle = angleOfIncludedAngle( + this.localToCanvasPoint( + thisPort[thisPort.length - 1], + ) /* 交点 */, + thisPort[thisPort.length - 2] + ? this.localToCanvasPoint(thisPort[thisPort.length - 2]) + : this.position, + otherPort[otherPort.length - 2] + ? turnout.localToCanvasPoint(otherPort[otherPort.length - 2]) + : turnout.position, + ); + if (180 - Math.abs(angle) <= deflection) { + deflection = 180 - Math.abs(angle); + params = [ + new GraphicRelationParam( + this, + [DevicePort.A, DevicePort.B, DevicePort.C][i], + ), + new GraphicRelationParam( + turnout, + [DevicePort.A, DevicePort.B, DevicePort.C][j], + ), + ]; + } + } + }); + }); + if (params.length === 2) { + this.relationManage.addRelation(params[0], params[1]); + } + }); + } + saveCommonRelations() { + const paRelation = this.relationManage + .getRelationsOfGraphic(this) + .find( + (relation) => + relation.getRelationParam(this).param === DevicePort.A && + (relation.getOtherGraphic(this) instanceof JlSection || + relation.getOtherGraphic(this) instanceof JlTurnout), + ); + 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 JlSection || + relation.getOtherGraphic(this) instanceof JlTurnout), + ); + const pbDevice = pbRelation?.getOtherGraphic(this); + if (pbDevice) { + this.datas.pbRef = IRelatedRef.create( + pbDevice.type, + pbDevice.id, + pbRelation!.getOtherRelationParam(this).getParam(), + ); + } else { + this.datas.pbRef = undefined; + } + + const pcRelation = this.relationManage + .getRelationsOfGraphic(this) + .find( + (relation) => + relation.getRelationParam(this).param === DevicePort.C && + !( + relation.getOtherGraphic(this) instanceof JlSection && + relation.getOtherGraphic(this).datas.sectionType !== + SectionType.TurnoutPhysical + ), + ); + const pcDevice = pcRelation?.getOtherGraphic(this); + if (pcDevice) { + this.datas.pcRef = IRelatedRef.create( + pcDevice.type, + pcDevice.id, + pcRelation!.getOtherRelationParam(this).getParam(), + ); + } else { + this.datas.pcRef = undefined; + } + } + loadCommonRelations() { + 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.pcRef?.id) { + this.relationManage.addRelation( + new GraphicRelationParam(this, DevicePort.C), + new GraphicRelationParam( + this.queryStore.queryById(this.datas.pcRef.id), + DevicePort[this.datas.pcRef.devicePort], + ), + ); + } + } } diff --git a/src/packages/Turnout/common/TurnoutConfig.ts b/src/packages/Turnout/common/TurnoutConfig.ts index 323a798..de68959 100644 --- a/src/packages/Turnout/common/TurnoutConfig.ts +++ b/src/packages/Turnout/common/TurnoutConfig.ts @@ -3,25 +3,41 @@ import { GraphicData } from 'jl-graphic'; import { IPointData } from 'pixi.js'; export interface TurnoutConstsConfig { - lineColor: string; - occupiedColor: string; lineWidth: number; + lineColor: string; forkLenth: number; labelFontSize: number; - normalLabelColor: string; - reverseLabelColor: string; } export const GPConsts = { - lineColor: '#5578b6', - occupiedColor: '#f00', lineWidth: 5, + lineColor: '#5578b6', forkLenth: 20, labelFontSize: 12, + occupiedColor: '#f00', normalLabelColor: '#0f0', reverseLabelColor: '#ff0', }; +export const THConsts = { + lineWidth: 5, + lineColor: '#5578b6', + forkLenth: 20, + labelFontSize: 12, + idleColor: '#888', //空闲 + jammedLineColor: '#f00', //挤岔 + ciOccupiedColor: '#f00', //非通信车占用 + cbtcOccupiedColor: '#f49', //通信车占用 + lockedColor: '#fff', //锁闭 && 故障锁闭 + atcInvalidColor: '#954', //atc报告失效 + overlapColor: '#ff0', //overlap + blueShowColor: '0x3149c3', + labelGREEN: '#0f0', + labelYELLOW: '#ff0', + labelRED: '#f00', + labelWHITE: '#fff', +}; + export enum SwitchMachineType { Unknown = 0, ZDJ9_Single = 1, @@ -40,8 +56,9 @@ export interface ITurnoutData extends GraphicData { paTrackSectionId?: number; // A端轨道区段id pbTrackSectionId?: number; pcTrackSectionId?: number; - switchMachineType?: SwitchMachineType; // 转辙机类型 + switchMachineType?: SwitchMachineType; // 转辙机类型--北京 centralizedStations?: number[]; // 集中站Id列表--北京 + centralizedStation?: number[]; // 所属集中站--西安 clone(): ITurnoutData; copyFrom(data: ITurnoutData): void; eq(other: ITurnoutData): boolean;