Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[NAE-1684] Frontend component for data field caseRef #112

Open
wants to merge 13 commits into
base: release/6.4.0
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,10 @@
"rxjs": "~6.5.4",
"showdown": "^2.0.3",
"tslib": "^2.0.0",
"zone.js": "~0.11.4"
"zone.js": "~0.11.4",
"panzoom": "~9.4.3",
"@netgrif/petriflow.svg": "~1.0.0",
"@netgrif/petri.svg": "~1.0.0"
},
"devDependencies": {
"@angular-devkit/build-angular": "~13.3.1",
Expand Down
3 changes: 2 additions & 1 deletion projects/netgrif-components-core/src/assets/i18n/de.json
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,8 @@
"maxFilesExceeded": "Die maximale Anzahl hochladbarer Dateien ist überschritten: ",
"maxFilesSizeExceeded": "Die maximale Größe der hochgeladenen Dateien wird überschritten: ",
"notSelectedUser": "Es wurde kein Benutzer ausgewählt",
"userAssigned": "Benutzer {{userName}} wurde zugewiesen"
"userAssigned": "Benutzer {{userName}} wurde zugewiesen",
"caseNetGetFailed": "Das Abrufen des Fallnetzes ist fehlgeschlagen"
},
"values": {
"boolean": {
Expand Down
3 changes: 2 additions & 1 deletion projects/netgrif-components-core/src/assets/i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,8 @@
"maxFilesExceeded": "Maximum number of uploadable files exceeded: ",
"maxFilesSizeExceeded": "The maximum size of uploaded files is exceeded: ",
"notSelectedUser": "No user has been selected",
"userAssigned": "User {{userName}} was assigned"
"userAssigned": "User {{userName}} was assigned",
"caseNetGetFailed": "Getting case Net failed"
minop marked this conversation as resolved.
Show resolved Hide resolved
},
"values": {
"boolean": {
Expand Down
3 changes: 2 additions & 1 deletion projects/netgrif-components-core/src/assets/i18n/sk.json
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,8 @@
"maxFilesExceeded": "Je presiahnutý maximálny počet nahratých súborov: ",
"maxFilesSizeExceeded": "Je presiahnutá maximálna veľkosť nahrávaných súborov: ",
"notSelectedUser": "Nebol vybraný žiadny používateľ",
"userAssigned": "Používateľ {{userName}} bol pridelený"
"userAssigned": "Používateľ {{userName}} bol pridelený",
"caseNetGetFailed": "Načítanie siete podľa prípadu zlyhalo"
},
"values": {
"boolean": {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,250 @@
import {AfterViewInit, Component, Input} from '@angular/core';
import {
PetriflowArc,
PetriflowCanvasConfigurationService,
PetriflowCanvasFactoryService,
PetriflowCanvasService,
PetriflowInhibitorArc,
PetriflowPlace,
PetriflowPlaceTransitionArc, PetriflowReadArc,
PetriflowResetArc,
PetriflowTransition, PetriflowTransitionPlaceArc
} from '@netgrif/petriflow.svg';
import {
Arc,
InhibitorArc, Place,
ReadArc,
RegularPlaceTransitionArc,
RegularTransitionPlaceArc,
ResetArc
} from '@netgrif/petri.svg';
import {CaseRefField} from './models/case-ref-field';
import {PetriNetResourceService} from '../../resources/engine-endpoint/petri-net-resource.service';
import {CaseResourceService} from '../../resources/engine-endpoint/case-resource.service';
import {TransitionImport} from '../../resources/interface/transition-import';
import {PlaceImport} from '../../resources/interface/place-import';
import {ArcImport} from '../../resources/interface/arc-import';
import {ArcType} from '../../resources/interface/arc-type';
import {PetriNetImport} from '../../resources/interface/petri-net-import';
import {LoggerService} from '../../logger/services/logger.service';
import {SnackBarService} from '../../snack-bar/services/snack-bar.service';
import {TranslateService} from '@ngx-translate/core';

@Component({
selector: 'ncc-abstract-case-ref-field',
template: ''
})
export abstract class AbstractCaseRefFieldComponent implements AfterViewInit {
minop marked this conversation as resolved.
Show resolved Hide resolved

@Input() public dataField: CaseRefField;

constructor(protected _petriflowCanvasService: PetriflowCanvasService, protected _petriflowFactoryService: PetriflowCanvasFactoryService,
protected _petriflowConfigService: PetriflowCanvasConfigurationService, protected _caseResourceService: CaseResourceService,
protected _petriNetResourceService: PetriNetResourceService, protected _log: LoggerService, protected _snackBar: SnackBarService,
protected _translate: TranslateService) {
}

ngAfterViewInit(): void {
this._petriNetResourceService.getNetByCaseId(this.dataField.value).subscribe(net => {
minop marked this conversation as resolved.
Show resolved Hide resolved
if (net) {
this.createNet(net);
}
minop marked this conversation as resolved.
Show resolved Hide resolved
}, error => {
this._log.error('Getting net by Case ID failed in field ['+ this.dataField.stringId + ']', error);
this._snackBar.openErrorSnackBar(this._translate.instant('dataField.snackBar.caseNetGetFailed'));
});
}

protected createNet(net: PetriNetImport) {
const trans: Array<PetriflowTransition> = [];
const places: Array<PetriflowPlace> = [];
const arcs: Array<PetriflowArc<any>> = [];
let minX: number = Number.MAX_SAFE_INTEGER;
let minY: number = Number.MAX_SAFE_INTEGER;
net.transitions.forEach((value) => {
const t = this.createTransitions(value)
trans.push(t);
minX = Math.min(minX, value.position.x);
minY = Math.min(minY, value.position.y);
this.setPlaceActions(t.canvasElement.element);
minop marked this conversation as resolved.
Show resolved Hide resolved
})
net.places.forEach((value) => {
const p = this.createPlace(value)
places.push(p);
minX = Math.min(minX, value.position.x);
minY = Math.min(minY, value.position.y);
this.setPlaceActions(p.canvasElement.element);
p.canvasElement.markingTokens.forEach(markingToken => {
this.setPlaceActions(markingToken);
});
})
net.arcs.forEach((arc) => {
const a = this.createArcs(trans, places, arc, net)
arcs.push(a);
arc.breakpoints?.forEach(value => {
minX = Math.min(minX, value.x);
minY = Math.min(minY, value.y);
});
this.setPlaceActions(a.element.arcLine);
});
trans.forEach(value => {
if (net.assignedTasks.includes(value.canvasElement.label.textContent)) {
value.select();
}
if (this.isEnabled(value, places, arcs)) {
value.canvasElement.element.classList.add('svg-transition-enabled');
value.canvasElement.element.setAttributeNS(null, 'fill', 'yellowgreen');
value.canvasElement.element.setAttributeNS(null, 'stroke', 'green');
minop marked this conversation as resolved.
Show resolved Hide resolved
}
});
this._petriflowCanvasService.panzoom?.moveTo(-minX + 20, -minY + 20);
if (this.dataField.component?.properties?.lock === 'true') {
setTimeout(() => {
this._petriflowCanvasService.panzoom?.pause();
})
}
}

protected createTransitions(value: TransitionImport): PetriflowTransition {
const transition = this._petriflowFactoryService.createTransition(new DOMPoint(value.position.x, value.position.y));
transition.changeId(value.stringId);
this._petriflowConfigService.addTransitionEvents(transition);
return transition;
}

protected createPlace(value: PlaceImport): PetriflowPlace {
const place = this._petriflowFactoryService.createPlace(value.tokens, new DOMPoint(value.position.x, value.position.y));
place.changeId(value.stringId);
this._petriflowConfigService.addPlaceEvents(place);
return place;
}

protected createArcs(trans: Array<PetriflowTransition>, places: Array<PetriflowPlace>, arc: ArcImport, net: PetriNetImport) {
let source: PetriflowPlace | PetriflowTransition = trans.find(value => value.canvasElement.label.textContent === arc.sourceId);
let destination: PetriflowPlace | PetriflowTransition;
let activable: boolean = false;
if (source === undefined) {
source = places.find(value => value.canvasElement.label.textContent === arc.sourceId);
destination = trans.find(value => value.canvasElement.label.textContent === arc.destinationId);
if (net.assignedTasks.includes(destination.canvasElement.label.textContent)) {
source.select();
destination.select();
activable = true;
}
} else {
destination = places.find(value => value.canvasElement.label.textContent === arc.destinationId);
if (net.finishedTasks.includes(source.canvasElement.label.textContent)) {
source.select();
destination.select();
activable = true;
}
}
if (source === undefined || destination === undefined) {
this._log.error("Can't find source or destination for arc [" + arc.importId + "]");
} else {
const newArc: Arc = this.createArc(arc, source, destination);
const petriflowArc: PetriflowArc<Arc> = this.createPetriflowArc(arc, newArc, source);
if (activable) {
petriflowArc.select();
}
this._petriflowCanvasService.canvas.container.appendChild(newArc.container);
this._petriflowCanvasService.petriflowElementsCollection.arcs.push(petriflowArc);
return petriflowArc;
}
return undefined
}

protected createArc(arc: ArcImport, source: PetriflowTransition | PetriflowPlace, destination: PetriflowPlace | PetriflowTransition) {
if (source instanceof PetriflowPlace) {
switch (arc.type) {
case ArcType.ARC: {
return this._petriflowFactoryService.createArc(RegularPlaceTransitionArc, source.canvasElement, destination.canvasElement, arc.breakpoints, arc.multiplicity);
}
case ArcType.RESET: {
return this._petriflowFactoryService.createArc(ResetArc, source.canvasElement, destination.canvasElement, arc.breakpoints, arc.multiplicity);
}
case ArcType.INHIBITOR: {
return this._petriflowFactoryService.createArc(InhibitorArc, source.canvasElement, destination.canvasElement, arc.breakpoints, arc.multiplicity);
}
case ArcType.READ: {
return this._petriflowFactoryService.createArc(ReadArc, source.canvasElement, destination.canvasElement, arc.breakpoints, arc.multiplicity);
}
default: {
return undefined;
}
}
} else if (arc.type === ArcType.ARC) {
return this._petriflowFactoryService.createArc(RegularTransitionPlaceArc, source.canvasElement, destination.canvasElement, arc.breakpoints, arc.multiplicity);
} else {
return undefined;
}
}

protected createPetriflowArc(arc: ArcImport, newArc: Arc, source: PetriflowTransition | PetriflowPlace) {
if (source instanceof PetriflowPlace) {
switch (arc.type) {
case ArcType.ARC: {
return this._petriflowFactoryService.createArc(PetriflowPlaceTransitionArc, newArc);
}
case ArcType.RESET: {
return this._petriflowFactoryService.createArc(PetriflowResetArc, newArc);
}
case ArcType.INHIBITOR: {
return this._petriflowFactoryService.createArc(PetriflowInhibitorArc, newArc);
}
case ArcType.READ: {
return this._petriflowFactoryService.createArc(PetriflowReadArc, newArc);
}
default: {
return undefined;
}
}
} else if (arc.type === ArcType.ARC) {
return this._petriflowFactoryService.createArc(PetriflowTransitionPlaceArc, newArc);
} else {
return undefined;
}
}

protected isEnabled(t: PetriflowTransition, places: Array<PetriflowPlace>, arcs: Array<PetriflowArc<any>>): boolean {
const testMarking: Map<string, number> = new Map();

for (const place of places) {
testMarking.set(place.canvasElement.id, place.canvasElement.tokensCount);
}
for (const arc of arcs) {
if (arc.element.end.id === t.canvasElement.id && (arc instanceof PetriflowInhibitorArc) && testMarking.get((arc.element.start as Place).id) >= parseInt(arc.element.multiplicity.data, 10)) {
return false;
}
}
for (const arc of arcs) {
if (arc.element.end.id === t.canvasElement.id && (arc instanceof PetriflowReadArc) && testMarking.get((arc.element.start as Place).id) < parseInt(arc.element.multiplicity.data, 10)) {
return false;
}
}
for (const arc of arcs) {
if (arc.element.end.id === t.canvasElement.id && arc instanceof PetriflowPlaceTransitionArc) {
const place = testMarking.get((arc.element.start as Place).id)
testMarking.set((arc.element.start as Place).id, place - parseInt(arc.element.multiplicity.data, 10));
}
}
for (const place of testMarking.values()) {
if (place < 0) {
minop marked this conversation as resolved.
Show resolved Hide resolved
return false;
}
}

return true;
}

public getHeight() {
return (this.dataField.layout && this.dataField.layout.rows && this.dataField.layout.rows) > 1 ?
this.dataField.layout.rows * CaseRefField.FIELD_HEIGHT : CaseRefField.FIELD_HEIGHT;
}

protected setPlaceActions(svgElement: SVGElement) {
svgElement.onmouseenter = () => {};
svgElement.onmouseleave = () => {};
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import {Behavior} from '../../models/behavior';
import {Layout} from '../../models/layout';
import {Validation} from '../../models/validation';
import {Component} from '../../models/component';
import {DataField} from '../../models/abstract-data-field';

export class CaseRefField extends DataField<string> {

public static FIELD_HEIGHT: number = 75;

constructor(stringId: string, title: string, value: string, behavior: Behavior, placeholder?: string,
description?: string, layout?: Layout, validations?: Array<Validation>, _component?: Component, parentTaskId?: string) {
super(stringId, title, value, behavior, placeholder, description, layout, validations, _component, parentTaskId);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {NgxMatDatetimePickerModule} from '@angular-material-components/datetime-
import {NgxMatMomentModule} from '@angular-material-components/moment-adapter';
import {TranslateLibModule} from '../translate/translate-lib.module';
import {DateAdapter} from '@angular/material/core';
import {PetriflowCanvasModule} from '@netgrif/petriflow.svg';

@NgModule({
imports: [
Expand All @@ -23,7 +24,8 @@ import {DateAdapter} from '@angular/material/core';
ReactiveFormsModule,
NgxMatDatetimePickerModule,
NgxMatMomentModule,
TranslateLibModule
TranslateLibModule,
PetriflowCanvasModule
],
providers: [
{provide: DateAdapter, useClass: CustomDateAdapter}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ export * from './i18n-field/abstract-i18n-field.component';
export * from './i18n-field/i18n-text-field/abstract-i18n-text-field.component';
export * from './i18n-field/i18n-divider-field/abstract-i18n-divider-field.component';
export * from './i18n-field/abstract-i18n-errors.component';
export * from './case-ref-field/abstract-case-ref-field.component';
export * from './user-list-field/abstract-user-list-field.component';

/* Class */
Expand All @@ -60,6 +61,7 @@ export * from './task-ref-field/model/task-ref-field';
export * from './enumeration-field/models/dynamic-enumeration-field';
export * from './filter-field/models/filter-field';
export * from './i18n-field/models/i18n-field';
export * from './case-ref-field/models/case-ref-field';
export * from './user-list-field/models/user-list-field';

/* Interfaces */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {Page} from '../interface/page';
import {processMessageResponse} from '../../utility/process-message-response';
import {AbstractResourceService} from '../abstract-endpoint/abstract-resource.service';
import RolesAndPermissions from '../../process/rolesAndPermissions';
import {PetriNetImport} from '../interface/petri-net-import';

@Injectable({
providedIn: 'root'
Expand Down Expand Up @@ -181,4 +182,16 @@ export class PetriNetResourceService extends AbstractResourceService {
return this._resourceProvider.delete$<MessageResource>('petrinet/' + netId, this.SERVER_URL)
.pipe(switchMap(processMessageResponse));
}

/**
* get One Net by case ID
*
* **Request Type:** GET
*
* **Request URL:** {{baseUrl}}/api/petrinet/case/{id}
*/
public getNetByCaseId(caseId: string, params?: Params): Observable<PetriNetImport> {
return this._resourceProvider.get$('petrinet/case/' + caseId, this.SERVER_URL, params)
.pipe(map(r => this.changeType(r, undefined)));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import {Position} from './position';
import {ArcType} from './arc-type';

export interface ArcImport {
destinationId: string;
sourceId: string;
importId: string;
multiplicity: number;
stringId: string;
breakpoints: Array<Position>;
type: ArcType;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export enum ArcType {
ARC = 'arc',
RESET = 'reset',
INHIBITOR = 'inhibitor',
READ = 'read'
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import {TransitionImport} from './transition-import';
import {PlaceImport} from './place-import';
import {ArcImport} from './arc-import';

export interface PetriNetImport {
transitions: Array<TransitionImport>;
places: Array<PlaceImport>;
arcs: Array<ArcImport>;
assignedTasks: Array<string>;
finishedTasks: Array<string>;
}
Loading