Skip to content
Open
Show file tree
Hide file tree
Changes from 9 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"
},
"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,255 @@
import {AfterViewInit, Component, Inject, Input, Optional} 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';
import {AbstractDataFieldComponent} from '../models/abstract-data-field-component';
import {NAE_INFORM_ABOUT_INVALID_DATA} from '../models/invalid-data-policy-token';

@Component({
selector: 'ncc-abstract-case-ref-field',
template: ''
})
export abstract class AbstractCaseRefFieldComponent extends AbstractDataFieldComponent implements AfterViewInit {

@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, @Optional() @Inject(NAE_INFORM_ABOUT_INVALID_DATA) informAboutInvalidData: boolean | null) {
super(informAboutInvalidData);
}

ngAfterViewInit(): void {
this.formControl.valueChanges.subscribe(value => {
if (value?.length > 0) {
this._petriNetResourceService.getNetByCaseId(value[0]).subscribe(net => {
this.createNet(net);
}, 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.setEmptyEvents(t.canvasElement.element);
})
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.setEmptyEvents(p.canvasElement.element);
p.canvasElement.markingTokens.forEach(markingToken => {
this.setEmptyEvents(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.setEmptyEvents(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');
}
});
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) {
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 setEmptyEvents(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<Array<string>> {

public static FIELD_HEIGHT: number = 75;

constructor(stringId: string, title: string, value: Array<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'
}
Loading