This commit is contained in:
fan 2023-10-08 10:43:23 +08:00
commit c2a1a2edfe
13 changed files with 324 additions and 33 deletions

View File

@ -111,3 +111,17 @@ export async function getSimulationChannelName(): Promise<string> {
const response = await api.get(`${UriBase}/getDataChannelName`); const response = await api.get(`${UriBase}/getDataChannelName`);
return response.data; return response.data;
} }
/**
*
* @param data
* @returns
*/
export async function setRelayState(data: {
id: string;
mapId: number;
simulationId: string;
td: boolean;
}) {
return await api.post(`${UriBase}/relay/operation`, data);
}

View File

@ -1,10 +1,7 @@
import { type GraphicDataBase } from 'src/drawApp/graphics/GraphicDataBase'; import { type GraphicDataBase } from 'src/drawApp/graphics/GraphicDataBase';
import { IDrawApp, JlGraphic } from 'src/jl-graphic'; import { IDrawApp } from 'src/jl-graphic';
import { useDrawStore } from 'src/stores/draw-store';
import { onMounted, onUnmounted, reactive } from 'vue'; import { onMounted, onUnmounted, reactive } from 'vue';
const drawStore = useDrawStore();
export function useFormData<T extends GraphicDataBase>( export function useFormData<T extends GraphicDataBase>(
source: T, source: T,
app: IDrawApp app: IDrawApp
@ -20,7 +17,7 @@ export function useFormData<T extends GraphicDataBase>(
}); });
function onUpdate() { function onUpdate() {
const graphic = drawStore.selectedGraphic as JlGraphic; const graphic = app.selectedGraphics[0];
if (graphic) { if (graphic) {
app.updateGraphicAndRecord(graphic, data); app.updateGraphicAndRecord(graphic, data);
} }

View File

@ -18,6 +18,9 @@
<section-state <section-state
v-else-if="lineStore.selectedGraphicType === Section.Type" v-else-if="lineStore.selectedGraphicType === Section.Type"
/> />
<relay-state
v-else-if="lineStore.selectedGraphicType === Relay.Type"
/>
</div> </div>
</q-scroll-area> </q-scroll-area>
</template> </template>
@ -38,6 +41,8 @@ import LinkState from './states/LinkState.vue';
import { SectionLink } from 'src/graphics/sectionLink/SectionLink'; import { SectionLink } from 'src/graphics/sectionLink/SectionLink';
import SectionState from './states/SectionState.vue'; import SectionState from './states/SectionState.vue';
import { Section } from 'src/graphics/section/Section'; import { Section } from 'src/graphics/section/Section';
import RelayState from './states/RelayState.vue';
import { Relay } from 'src/graphics/relay/Relay';
const lineStore = useLineStore(); const lineStore = useLineStore();
</script> </script>

View File

@ -241,14 +241,14 @@ function getDeviveName() {
let name = ''; let name = '';
if (devicePort) { if (devicePort) {
dev = turnouts.value.find((item) => { dev = turnouts.value.find((item) => {
return item.datas.index + '' == headDeviceId; return item.datas.id == headDeviceId;
}) as Turnout; }) as Turnout;
if (dev) { if (dev) {
name = `${dev?.datas.code}_${devicePort}`; name = `${dev?.datas.code}_${devicePort}`;
} }
} else { } else {
dev = sections.value.find((item) => { dev = sections.value.find((item) => {
return item.datas.index + '' == headDeviceId; return item.datas.id == headDeviceId;
}) as Section; }) as Section;
if (dev) { if (dev) {
name = `${dev?.datas.code}`; name = `${dev?.datas.code}`;
@ -303,12 +303,12 @@ const sections = ref<Section[]>([]);
function getAllTurnout() { function getAllTurnout() {
turnouts.value = lineStore turnouts.value = lineStore
.getLineApp() .appCurrentScene()
.queryStore.queryByType<Turnout>(Turnout.Type); .queryStore.queryByType<Turnout>(Turnout.Type);
} }
function getAllSection() { function getAllSection() {
sections.value = lineStore sections.value = lineStore
.getLineApp() .appCurrentScene()
.queryStore.queryByType<Section>(Section.Type); .queryStore.queryByType<Section>(Section.Type);
} }
</script> </script>

View File

@ -0,0 +1,98 @@
<template>
<q-card flat bordered>
<q-card-section>
<div class="text-h6">继电器状态</div>
</q-card-section>
<q-separator inset />
<q-form>
<q-input outlined readonly v-model="relayState.id" label="id" hint="" />
<q-input
outlined
readonly
v-model.number="relayState.code"
label="名称"
/>
<q-checkbox v-model="relayState.xh" outlined label="是否吸合" />
</q-form>
<q-card-actions align="center">
<q-btn label="修改" type="submit" color="primary" @click="submitState" />
<q-btn
label="重置"
type="reset"
color="primary"
flat
class="q-ml-sm"
@click="onReset"
/>
</q-card-actions>
</q-card>
</template>
<script setup lang="ts">
import { useLineStore } from 'src/stores/line-store';
import { ref, watch, onMounted } from 'vue';
import { setRelayState } from 'src/api/Simulation';
import { useQuasar } from 'quasar';
import { ApiError } from 'src/boot/axios';
import { Relay } from 'src/graphics/relay/Relay';
const $q = useQuasar();
const lineStore = useLineStore();
const relayState = ref({
id: '',
code: '',
xh: false,
});
watch(
() => lineStore.selectedGraphics,
(val) => {
if (val?.length == 1 && val[0].type == Relay.Type) {
initRelayState(val[0] as Relay);
} else {
relayState.value = {
id: '',
code: '',
xh: false,
};
}
}
);
function initRelayState(relay: Relay) {
relayState.value = {
id: relay.datas.id,
code: relay.datas.code,
xh: relay.states.xh || false,
};
}
function submitState() {
if (lineStore.simulationId) {
setRelayState({
id: relayState.value.id,
mapId: lineStore.mapId as number,
simulationId: lineStore.simulationId,
td: relayState.value.xh,
})
.then(() => {
$q.notify({ type: 'positive', message: '修改继电器状态成功' });
})
.catch((err) => {
const error = err as ApiError;
$q.notify({
type: 'negative',
message: '修改继电器状态失败' + error.title,
});
});
}
}
function onReset() {
if (lineStore.selectedGraphics) {
initRelayState(lineStore.selectedGraphics[0] as Relay);
}
}
onMounted(() => {
if (lineStore.selectedGraphics) {
initRelayState(lineStore.selectedGraphics[0] as Relay);
}
});
</script>

View File

@ -321,11 +321,17 @@ export class TurnoutStates extends GraphicStateBase implements ITurnoutState {
set reverse(reverse: boolean) { set reverse(reverse: boolean) {
this.states.reverse = reverse; this.states.reverse = reverse;
} }
get turning(): boolean { get dw(): boolean {
return this.states.turning; return this.states.dw;
} }
set turning(turning: boolean) { set dw(dw: boolean) {
this.states.turning = turning; this.states.dw = dw;
}
get fw(): boolean {
return this.states.fw;
}
set fw(fw: boolean) {
this.states.fw = fw;
} }
get states(): state.SwitchState { get states(): state.SwitchState {
return this.getState<state.SwitchState>(); return this.getState<state.SwitchState>();

View File

@ -3,6 +3,18 @@ import { IRelayData, IRelayState, Relay } from 'src/graphics/relay/Relay';
import { relayCabinetGraphicData } from 'src/protos/relayCabinetLayoutGraphics'; import { relayCabinetGraphicData } from 'src/protos/relayCabinetLayoutGraphics';
import { GraphicDataBase, GraphicStateBase } from '../graphics/GraphicDataBase'; import { GraphicDataBase, GraphicStateBase } from '../graphics/GraphicDataBase';
import { state } from 'src/protos/device_state'; import { state } from 'src/protos/device_state';
import { useLineStore } from 'src/stores/line-store';
import { MenuItemOptions } from 'src/jl-graphic/ui/Menu';
import { ContextMenu } from 'src/jl-graphic/ui/ContextMenu';
import {
GraphicInteractionPlugin,
IGraphicScene,
JlGraphic,
} from 'src/jl-graphic';
import { DisplayObject, FederatedMouseEvent } from 'pixi.js';
import { setRelayState } from 'src/api/Simulation';
import { ApiError } from 'src/boot/axios';
import { errorNotify, successNotify } from 'src/utils/CommonNotify';
export class RelayData extends GraphicDataBase implements IRelayData { export class RelayData extends GraphicDataBase implements IRelayData {
constructor(data?: relayCabinetGraphicData.Relay) { constructor(data?: relayCabinetGraphicData.Relay) {
@ -76,3 +88,71 @@ export class RelayState extends GraphicStateBase implements IRelayState {
return pb_1.Message.equals(this._state, data._state); return pb_1.Message.equals(this._state, data._state);
} }
} }
const setOpen: MenuItemOptions = { name: '驱动落下' };
const setClose: MenuItemOptions = { name: '驱动吸起' };
const RelayOperationMenu: ContextMenu = ContextMenu.init({
name: 'Turnout操作',
groups: [{ items: [setOpen, setClose] }],
});
export class RelayOperationPlugin extends GraphicInteractionPlugin<Relay> {
static Name = 'relay_operate_menu';
constructor(app: IGraphicScene) {
super(RelayOperationPlugin.Name, app);
app.registerMenu(RelayOperationMenu);
}
static init(app: IGraphicScene) {
return new RelayOperationPlugin(app);
}
filter(...grahpics: JlGraphic[]): Relay[] | undefined {
return grahpics.filter<Relay>((g): g is Relay => g instanceof Relay);
}
bind(g: Relay): void {
g.eventMode = 'static';
g.cursor = 'pointer';
g.selectable = true;
g.on('_leftclick', this.onLeftClick, this);
g.on('rightclick', this.onContextMenu, this);
}
unbind(g: Relay): void {
g.eventMode = 'none';
g.selectable = false;
g.off('_leftclick', this.onLeftClick, this);
g.off('rightclick', this.onContextMenu);
}
onLeftClick() {
useLineStore().stateProCountIncrease();
}
onContextMenu(e: FederatedMouseEvent) {
const target = e.target as DisplayObject;
const relay = target.getGraphic<Relay>();
if (!relay) return;
this.app.updateSelected(relay);
const simulationId = useLineStore().simulationId || '';
const mapId = useLineStore().mapId as number;
const changeRelayState = async (td: boolean) => {
setRelayState({
id: relay.datas.id,
mapId,
simulationId,
td,
})
.then(() => {
successNotify('修改继电器状态成功');
})
.catch((err) => {
const error = err as ApiError;
errorNotify(error.title, error);
});
};
setOpen.handler = () => {
changeRelayState(false);
};
setClose.handler = () => {
changeRelayState(true);
};
RelayOperationMenu.open(e.global);
}
}

View File

@ -12,8 +12,12 @@ import { toUint8Array } from 'js-base64';
import { relayCabinetGraphicData } from 'src/protos/relayCabinetLayoutGraphics'; import { relayCabinetGraphicData } from 'src/protos/relayCabinetLayoutGraphics';
import { RelayCabinetTemplate } from 'src/graphics/relayCabinet/RelayCabinet'; import { RelayCabinetTemplate } from 'src/graphics/relayCabinet/RelayCabinet';
import { RelayCabinetData } from './relayCabinetGraphics/RelayCabinetInteraction'; import { RelayCabinetData } from './relayCabinetGraphics/RelayCabinetInteraction';
import { RelayTemplate } from 'src/graphics/relay/Relay'; import { Relay, RelayTemplate } from 'src/graphics/relay/Relay';
import { RelayData, RelayState } from './relayCabinetGraphics/RelayInteraction'; import {
RelayData,
RelayOperationPlugin,
RelayState,
} from './relayCabinetGraphics/RelayInteraction';
import { PhaseFailureProtectorTemplate } from 'src/graphics/phaseFailureProtector/PhaseFailureProtector'; import { PhaseFailureProtectorTemplate } from 'src/graphics/phaseFailureProtector/PhaseFailureProtector';
import { PhaseFailureProtectorData } from './relayCabinetGraphics/PhaseFailureProtectorInteraction'; import { PhaseFailureProtectorData } from './relayCabinetGraphics/PhaseFailureProtectorInteraction';
import { state } from 'src/protos/device_state'; import { state } from 'src/protos/device_state';
@ -23,11 +27,14 @@ export function initRelayScene(lineApp: IGraphicApp, sceneName: string) {
// 继电器 // 继电器
const relayScene = lineApp.initScene(sceneName, { const relayScene = lineApp.initScene(sceneName, {
dataLoader: loadRelayDatas, dataLoader: loadRelayDatas,
});
relayScene.setOptions({
mouseToolOptions: { mouseToolOptions: {
boxSelect: false, boxSelect: false,
viewportDrag: true, viewportDrag: true,
wheelZoom: true, wheelZoom: true,
}, },
interactiveGraphicTypeIncludes: [Relay.Type],
}); });
const relayGraphicTemplate = [ const relayGraphicTemplate = [
new RelayCabinetTemplate(new RelayCabinetData()), new RelayCabinetTemplate(new RelayCabinetData()),
@ -35,8 +42,24 @@ export function initRelayScene(lineApp: IGraphicApp, sceneName: string) {
new PhaseFailureProtectorTemplate(new PhaseFailureProtectorData()), new PhaseFailureProtectorTemplate(new PhaseFailureProtectorData()),
]; ];
relayScene.registerGraphicTemplates(...relayGraphicTemplate); relayScene.registerGraphicTemplates(...relayGraphicTemplate);
RelayOperationPlugin.init(relayScene);
relayScene.on('postdataloaded', () => { relayScene.on('postdataloaded', () => {
handleSubscribe(relayScene); handleSubscribe(relayScene);
const map = new Map<string, string>();
refRelaysList.forEach((device) => {
device.combinationtypes.forEach((combinationtype) => {
combinationtype.refRelays.forEach((relayId) => {
map.set(relayId, device.code);
});
});
});
const relays = relayScene.queryStore.queryByType<Relay>(Relay.Type);
relays.forEach((relay) => {
relay.refDevice.text = map.get(relay.id) as string;
});
});
relayScene.on('destroy', async () => {
refRelaysList = [];
}); });
} }
@ -91,6 +114,7 @@ function handleSubscribe(relayScene: IGraphicScene) {
}); });
} }
let refRelaysList: relayCabinetGraphicData.DeviceRelateRelay[] = [];
async function loadRelayDatas(): Promise<IGraphicStorage> { async function loadRelayDatas(): Promise<IGraphicStorage> {
const lineStore = useLineStore(); const lineStore = useLineStore();
const mapId = lineStore.mapId; const mapId = lineStore.mapId;
@ -114,6 +138,7 @@ async function loadRelayDatas(): Promise<IGraphicStorage> {
storage.phaseFailureProtectors.forEach((phaseFailureProtector) => { storage.phaseFailureProtectors.forEach((phaseFailureProtector) => {
datas.push(new PhaseFailureProtectorData(phaseFailureProtector)); datas.push(new PhaseFailureProtectorData(phaseFailureProtector));
}); });
refRelaysList = storage.deviceRelateRelayList;
return Promise.resolve({ return Promise.resolve({
canvasProperty: storage.canvas, canvasProperty: storage.canvas,
datas: datas, datas: datas,

View File

@ -63,7 +63,7 @@ export class Relay extends JlGraphic {
} }
this.refDevice.position.set(0, -20); this.refDevice.position.set(0, -20);
const relayGraphic = this.relayGraphic; const relayGraphic = this.relayGraphic;
let relayColor = relayConsts.openColor; let relayColor;
if (this.states?.xh) { if (this.states?.xh) {
relayColor = relayConsts.closeColor; relayColor = relayConsts.closeColor;
} else { } else {

View File

@ -419,9 +419,9 @@ export class Train extends JlGraphic {
const py = startP.y + Math.round(offset * (endP.y - startP.y)); const py = startP.y + Math.round(offset * (endP.y - startP.y));
let angle = Math.atan2(endP.y - startP.y, endP.x - startP.x); let angle = Math.atan2(endP.y - startP.y, endP.x - startP.x);
if (-Math.PI / 2 > angle) { if (-Math.PI / 2 > angle) {
angle = angle + Math.PI; angle = -(angle + Math.PI);
} }
if (Math.PI / 2 < angle) { if (Math.PI / 2 <= angle) {
angle = angle - Math.PI; angle = angle - Math.PI;
} }
const p = (dev as Section).localToCanvasPoint(new Point(px, py)); const p = (dev as Section).localToCanvasPoint(new Point(px, py));

View File

@ -1,5 +1,6 @@
import { Graphics, IPointData } from 'pixi.js'; import { Graphics, IPointData } from 'pixi.js';
import { import {
GraphicAnimation,
GraphicData, GraphicData,
GraphicRelationParam, GraphicRelationParam,
GraphicState, GraphicState,
@ -58,7 +59,8 @@ export interface ITurnoutState extends GraphicState {
id?: string; id?: string;
normal?: boolean; normal?: boolean;
reverse?: boolean; reverse?: boolean;
turning?: boolean; dw?: boolean;
fw?: boolean;
} }
export const TurnoutConsts = { export const TurnoutConsts = {
@ -146,6 +148,7 @@ export class Turnout extends JlGraphic {
sections: [TurnoutSection, TurnoutSection, TurnoutSection]; sections: [TurnoutSection, TurnoutSection, TurnoutSection];
label: VectorText; label: VectorText;
}; };
deltaTime: number;
constructor() { constructor() {
super(Turnout.Type); super(Turnout.Type);
@ -159,6 +162,7 @@ export class Turnout extends JlGraphic {
], ],
label: new VectorText(), label: new VectorText(),
}; };
this.deltaTime = 0;
this.addChild(this.graphics.fork); this.addChild(this.graphics.fork);
// this.addChild(...this.graphics.sections); // this.addChild(...this.graphics.sections);
this.addChild(this.graphics.sections[0]); this.addChild(this.graphics.sections[0]);
@ -195,14 +199,50 @@ export class Turnout extends JlGraphic {
if (this.states.normal) { if (this.states.normal) {
this.graphics.fork.paint(pointB[0]); this.graphics.fork.paint(pointB[0]);
this.graphics.label.style.stroke = TurnoutConsts.normalLabelColor; this.graphics.label.style.stroke = TurnoutConsts.normalLabelColor;
this.graphics.fork.visible = true;
} else if (this.states.reverse) { } else if (this.states.reverse) {
this.graphics.fork.paint(pointC[0]); this.graphics.fork.paint(pointC[0]);
this.graphics.label.style.stroke = TurnoutConsts.reverseLabelColor; this.graphics.label.style.stroke = TurnoutConsts.reverseLabelColor;
this.graphics.fork.visible = true;
} }
this.graphics.sections.forEach((sectionGraphic) => sectionGraphic.paint()); this.graphics.sections.forEach((sectionGraphic) => sectionGraphic.paint());
this.graphics.label.text = this.datas.code; this.graphics.label.text = this.datas.code;
if (!this.states.normal && !this.states.reverse) {
// 失表
this.graphics.fork.visible = false;
}
// if (this.states.split) {
// this.initTurnoutSplit();
// } else {
// this.stopTurnoutSplit();
// }
}
initTurnoutSplit() {
// 道岔失表
const name = `${this.datas.id}_turnout_split`;
let turnoutSplit = this.animation(name);
if (!turnoutSplit) {
turnoutSplit = GraphicAnimation.init({
name: name,
run: (dt: number) => {
this.deltaTime += dt;
this.deltaTime = this.deltaTime % 60;
this.graphics.fork.visible = this.deltaTime > 30;
},
});
this.addAnimation(turnoutSplit);
}
turnoutSplit.resume();
}
stopTurnoutSplit() {
const name = `${this.datas.id}_turnout_split`;
const turnoutSplit = this.animation(name);
if (turnoutSplit) {
turnoutSplit.pause();
this.deltaTime = 0;
}
} }
buildRelation(): void { buildRelation(): void {

View File

@ -220,7 +220,8 @@ export namespace state {
id?: string; id?: string;
normal?: boolean; normal?: boolean;
reverse?: boolean; reverse?: boolean;
turning?: boolean; dw?: boolean;
fw?: boolean;
}) { }) {
super(); super();
pb_1.Message.initialize(this, Array.isArray(data) ? data : [], 0, -1, [], this.#one_of_decls); pb_1.Message.initialize(this, Array.isArray(data) ? data : [], 0, -1, [], this.#one_of_decls);
@ -234,8 +235,11 @@ export namespace state {
if ("reverse" in data && data.reverse != undefined) { if ("reverse" in data && data.reverse != undefined) {
this.reverse = data.reverse; this.reverse = data.reverse;
} }
if ("turning" in data && data.turning != undefined) { if ("dw" in data && data.dw != undefined) {
this.turning = data.turning; this.dw = data.dw;
}
if ("fw" in data && data.fw != undefined) {
this.fw = data.fw;
} }
} }
} }
@ -257,17 +261,24 @@ export namespace state {
set reverse(value: boolean) { set reverse(value: boolean) {
pb_1.Message.setField(this, 3, value); pb_1.Message.setField(this, 3, value);
} }
get turning() { get dw() {
return pb_1.Message.getFieldWithDefault(this, 4, false) as boolean; return pb_1.Message.getFieldWithDefault(this, 4, false) as boolean;
} }
set turning(value: boolean) { set dw(value: boolean) {
pb_1.Message.setField(this, 4, value); pb_1.Message.setField(this, 4, value);
} }
get fw() {
return pb_1.Message.getFieldWithDefault(this, 5, false) as boolean;
}
set fw(value: boolean) {
pb_1.Message.setField(this, 5, value);
}
static fromObject(data: { static fromObject(data: {
id?: string; id?: string;
normal?: boolean; normal?: boolean;
reverse?: boolean; reverse?: boolean;
turning?: boolean; dw?: boolean;
fw?: boolean;
}): SwitchState { }): SwitchState {
const message = new SwitchState({}); const message = new SwitchState({});
if (data.id != null) { if (data.id != null) {
@ -279,8 +290,11 @@ export namespace state {
if (data.reverse != null) { if (data.reverse != null) {
message.reverse = data.reverse; message.reverse = data.reverse;
} }
if (data.turning != null) { if (data.dw != null) {
message.turning = data.turning; message.dw = data.dw;
}
if (data.fw != null) {
message.fw = data.fw;
} }
return message; return message;
} }
@ -289,7 +303,8 @@ export namespace state {
id?: string; id?: string;
normal?: boolean; normal?: boolean;
reverse?: boolean; reverse?: boolean;
turning?: boolean; dw?: boolean;
fw?: boolean;
} = {}; } = {};
if (this.id != null) { if (this.id != null) {
data.id = this.id; data.id = this.id;
@ -300,8 +315,11 @@ export namespace state {
if (this.reverse != null) { if (this.reverse != null) {
data.reverse = this.reverse; data.reverse = this.reverse;
} }
if (this.turning != null) { if (this.dw != null) {
data.turning = this.turning; data.dw = this.dw;
}
if (this.fw != null) {
data.fw = this.fw;
} }
return data; return data;
} }
@ -315,8 +333,10 @@ export namespace state {
writer.writeBool(2, this.normal); writer.writeBool(2, this.normal);
if (this.reverse != false) if (this.reverse != false)
writer.writeBool(3, this.reverse); writer.writeBool(3, this.reverse);
if (this.turning != false) if (this.dw != false)
writer.writeBool(4, this.turning); writer.writeBool(4, this.dw);
if (this.fw != false)
writer.writeBool(5, this.fw);
if (!w) if (!w)
return writer.getResultBuffer(); return writer.getResultBuffer();
} }
@ -336,7 +356,10 @@ export namespace state {
message.reverse = reader.readBool(); message.reverse = reader.readBool();
break; break;
case 4: case 4:
message.turning = reader.readBool(); message.dw = reader.readBool();
break;
case 5:
message.fw = reader.readBool();
break; break;
default: reader.skipField(); default: reader.skipField();
} }

View File

@ -64,6 +64,9 @@ export const useLineStore = defineStore('line', {
}); });
addSceneList(arr); addSceneList(arr);
}, },
appCurrentScene() {
return this.getLineApp().getScene(this.sceneName);
},
destroy() { destroy() {
this.selectedGraphics = null; this.selectedGraphics = null;
destroyLineApp(); destroyLineApp();