This commit is contained in:
Yuan 2024-01-03 16:24:10 +08:00
parent 574bce4b31
commit a354c9f136
6 changed files with 457 additions and 30 deletions

56
src/common/common.ts Normal file
View File

@ -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,
};
}
}

View File

@ -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<IAxleCountingData>();
}
}

View File

@ -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')
}
}

View File

@ -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)
}
}

View File

@ -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<ISectionData>();
}
get states(): ISectionState {
return this.getStates<ISectionState>();
}
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<DevicePort>() === 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<DevicePort>(),
};
}
/** 获取拆分逻辑区段数据 */
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>(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<Section | Turnout>(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<Section | Turnout>(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<AxleCounting>(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<Section>(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<AxleCounting>(id),
);
});
}
}
}
}
export class SectionTemplate extends JlGraphicTemplate<Section> {
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;
}
}