-
- Is Global
-
+
+
+ Is Global
+
+
diff --git a/src/app/modeler/role-mode/role-detail/role-detail.component.ts b/src/app/modeler/role-mode/role-detail/role-detail.component.ts
index ef18267..4487718 100644
--- a/src/app/modeler/role-mode/role-detail/role-detail.component.ts
+++ b/src/app/modeler/role-mode/role-detail/role-detail.component.ts
@@ -13,13 +13,13 @@ import {ModelerUtils} from '../../modeler-utils';
@Component({
selector: 'nab-role-detail',
templateUrl: './role-detail.component.html',
- styleUrl: './role-detail.component.scss'
+ styleUrl: './role-detail.component.scss',
})
export class RoleDetailComponent implements OnDestroy {
public role: ChangedRole;
public shouldSave: boolean = false;
- public form: FormControl;
+ public roleIdForm: FormControl;
public constructor(
private _masterService: RoleMasterDetailService,
@@ -27,7 +27,7 @@ export class RoleDetailComponent implements OnDestroy {
private _router: Router,
private _actionMode: ActionsModeService,
private _actionsMasterDetail: ActionsMasterDetailService,
- protected _historyService: HistoryService
+ protected _historyService: HistoryService,
) {
this._masterService.getSelected$().subscribe(item => {
this.saveChange();
@@ -36,9 +36,9 @@ export class RoleDetailComponent implements OnDestroy {
}
this.role = new ChangedRole(item.clone());
});
- this.form = new FormControl('', [
+ this.roleIdForm = new FormControl('', [
Validators.required,
- this.validUnique()
+ this.validUnique(),
]);
}
@@ -76,7 +76,7 @@ export class RoleDetailComponent implements OnDestroy {
}
changeGlobalFlag($event): void {
- this.role.role.global = $event.target.checked;
+ this.role.role.global = $event.checked;
this.shouldSave = true;
}
diff --git a/src/app/modeler/services/history/history.service.ts b/src/app/modeler/services/history/history.service.ts
index b77fbfc..41cfda8 100644
--- a/src/app/modeler/services/history/history.service.ts
+++ b/src/app/modeler/services/history/history.service.ts
@@ -13,15 +13,13 @@ import {RedoTool} from '../../control-panel/modes/redo-tool';
})
export class HistoryService {
- private readonly _history: History
;
- private readonly _historyChange: Subject>;
+ private readonly _histories: Map>;
constructor(
private modelService: ModelService,
private exportService: ExportService,
) {
- this._history = new History();
- this._historyChange = new Subject();
+ this._histories = new Map>();
}
public save(message: string, model?: PetriNet): void {
@@ -30,39 +28,59 @@ export class HistoryService {
this.push(model.clone(), message);
}
- public undo(): void {
- this.reloadModel(this._history.undo(), UndoTool.ID);
+ public undo(id = this.getId()): void {
+ const history = this.findById(id);
+ this.reloadModel(history.undo(), UndoTool.ID);
}
- public redo(): void {
- this.reloadModel(this._history.redo(), RedoTool.ID);
+ public redo(id = this.getId()): void {
+ const history = this.findById(id);
+ this.reloadModel(history.redo(), RedoTool.ID);
}
public reload(model: PetriNet): void {
- this._history.head = this._history.memory.findIndex(value => value.record === model);
+ this.history(model.id).head = this.history(model.id).memory.findIndex(value => value.record === model);
this.reloadModel(model, '');
}
+ public changeId(oldId: string, newId: string): void {
+ this._histories.set(newId, this._histories.get(oldId));
+ this._histories.delete(oldId);
+ }
+
+ private getId(): string {
+ return this.modelService.model.id;
+ }
+
private reloadModel(model: PetriNet, message: string): PetriNet {
if (model === undefined) {
return undefined;
}
- this.historyChange.next(HistoryChange.of(this._history, message));
- this.modelService.model = model.clone();
+ this.historyChange(model.id).next(HistoryChange.of(this.history(model.id), message));
+ const newModel = model.clone();
+ this.modelService.appService.updateModel(this.modelService.model.id, newModel);
+ this.modelService.model = newModel;
return model;
}
private push(model: PetriNet, message: string): void {
- if (!model || model.lastChanged === this.currentModel?.lastChanged || this.history.memory.find(change =>
+ if (!model || model.lastChanged === this.currentModel(model.id)?.lastChanged || this.history(model.id).memory.find(change =>
change.record.lastChanged === model.lastChanged)
) {
return;
}
- const update = this._history.push(model, message);
- this.historyChange.next(update);
+ const update = this.findById(model.id).push(model, message);
+ this.historyChange(model.id).next(update);
this.saveToLocalStorage(model).then();
}
+ private findById(id: string): History {
+ if (!this._histories.has(id)) {
+ this._histories.set(id, new History())
+ }
+ return this._histories.get(id);
+ }
+
async saveToLocalStorage(model: PetriNet): Promise {
localStorage.setItem(ModelerConfig.LOCALSTORAGE.DRAFT_MODEL.KEY, this.exportService.exportXml(model));
localStorage.setItem(ModelerConfig.LOCALSTORAGE.DRAFT_MODEL.TIMESTAMP, new Date().toLocaleString());
@@ -70,15 +88,15 @@ export class HistoryService {
localStorage.setItem(ModelerConfig.LOCALSTORAGE.DRAFT_MODEL.TITLE, `${model.title.value}`);
}
- get historyChange(): Subject> {
- return this._historyChange;
+ public historyChange(id = this.getId()): Subject> {
+ return this.findById(id).change;
}
- get currentModel(): PetriNet {
- return this._history.record;
+ public currentModel(id = this.getId()): PetriNet {
+ return this.findById(id).record;
}
- get history(): History {
- return this._history;
+ public history(id = this.getId()): History {
+ return this.findById(id);
}
}
diff --git a/src/app/modeler/services/history/history.ts b/src/app/modeler/services/history/history.ts
index ba756a0..aa16ff5 100644
--- a/src/app/modeler/services/history/history.ts
+++ b/src/app/modeler/services/history/history.ts
@@ -1,14 +1,18 @@
import {HistoryChange} from './history-change';
+import {Subject} from 'rxjs';
export class History {
+
private _memory: Array>;
private _head: number;
private readonly limit: number;
+ private readonly _change: Subject>;
constructor(limit: number = 100) {
this._memory = new Array>();
- this.limit = limit;
this._head = -1;
+ this.limit = limit;
+ this._change = new Subject();
}
public get record(): T {
@@ -68,4 +72,8 @@ export class History {
get memory(): Array> {
return this._memory;
}
+
+ get change(): Subject> {
+ return this._change;
+ }
}
diff --git a/src/app/modeler/services/model/model.service.ts b/src/app/modeler/services/model/model.service.ts
index 0a5f19d..45f3637 100644
--- a/src/app/modeler/services/model/model.service.ts
+++ b/src/app/modeler/services/model/model.service.ts
@@ -6,6 +6,7 @@ import {
DataType,
DataVariable,
I18nString,
+ ImportUtils,
NodeElement,
PetriNet,
Place,
@@ -98,7 +99,7 @@ export class ModelService {
public newModel(): PetriNet {
const model = new PetriNet();
- model.id = ModelConfig.IDENTIFIER + '-' + this.appService.getAndIncrementModelSequence();
+ model.id = this.appService.nextModelId();
model.version = ModelConfig.VERSION;
model.title = new I18nString(ModelConfig.TITLE);
model.initials = ModelConfig.INITIALS;
@@ -379,6 +380,7 @@ export class ModelService {
const role = this.model.getRole(newRole.id);
role.id = newRole.role.id;
role.title = newRole.role.title;
+ role.global = newRole.role.global;
this.model.removeRole(newRole.id);
this.model.addRole(role);
@@ -497,8 +499,10 @@ export class ModelService {
const referencedData = model.getData(id);
if (referencedData) {
if (referencedData.init.value) {
- return Number(referencedData.init.value);
- // TODO: NAB-326 check if isFinite and >= 0
+ if (ImportUtils.isInitValueNumber(referencedData.init)) {
+ return Number(referencedData.init.value);
+ }
+ return 0;
}
return 0;
}
diff --git a/src/app/modeler/simulation-mode/simulation-mode.service.ts b/src/app/modeler/simulation-mode/simulation-mode.service.ts
index 3a351ff..8d84604 100644
--- a/src/app/modeler/simulation-mode/simulation-mode.service.ts
+++ b/src/app/modeler/simulation-mode/simulation-mode.service.ts
@@ -1,6 +1,6 @@
import {Injectable, Injector} from '@angular/core';
import {BehaviorSubject} from 'rxjs';
-import {Arc, BasicSimulation, PetriNet, Place, Transition} from '@netgrif/petriflow';
+import {Arc, BasicSimulation, ImportUtils, PetriNet, Place, Transition} from '@netgrif/petriflow';
import {TutorialService} from '../../tutorial/tutorial-service';
import {ModelService} from '../services/model/model.service';
import {EventSimulationTool} from './tool/event-simulation.tool';
@@ -88,7 +88,13 @@ export class SimulationModeService extends CanvasModeService {
this.originalModel.subscribe(model => {
if (!model) return;
this.data = new Map(model.getArcs().filter(a => !!a.reference && !!model.getData(a.reference))
- .map(a => [a.reference, Number.parseInt(model.getData(a.reference).init?.value, 10) || 0]));
+ .map(a => {
+ const data = model.getData(a.reference);
+ if (ImportUtils.isInitValueNumber(data.init)) {
+ return [a.reference, Number.parseInt(data.init.value, 10)];
+ }
+ return [a.reference, 0];
+ }));
this.simulation = new BasicSimulation(model, this.data);
this.renderModel(model);
});
diff --git a/src/app/project-builder/application.service.ts b/src/app/project-builder/application.service.ts
index b287e20..9f302df 100644
--- a/src/app/project-builder/application.service.ts
+++ b/src/app/project-builder/application.service.ts
@@ -1,13 +1,15 @@
import {Injectable, OnDestroy} from '@angular/core';
-import {MatDialog} from '@angular/material/dialog';
import {PetriNet} from '@netgrif/petriflow';
import {Subscription} from 'rxjs';
import {filter} from 'rxjs/operators';
-import {DialogDeleteModelComponent} from '../dialogs/dialog-delete-model/dialog-delete-model.component';
import {HistoryService} from '../modeler/services/history/history.service';
import {ModelService} from '../modeler/services/model/model.service';
import Application from './application';
import {SimulationModeService} from "../modeler/simulation-mode/simulation-mode.service";
+import {SequenceGenerator} from '../modeler/services/model/sequence-generator';
+import {ModelConfig} from '../modeler/services/model/model-config';
+import {Router} from '@angular/router';
+import {EditModeService} from '../modeler/edit-mode/edit-mode.service';
@Injectable({
providedIn: 'root',
@@ -16,15 +18,15 @@ export class ApplicationService implements OnDestroy {
private readonly _models: Map;
private _application: Application;
- private _modelIdSequence = 0;
-
+ private _modelIdSequence = new SequenceGenerator(`${ModelConfig.IDENTIFIER}_`);
private _modelSubscription: Subscription;
constructor(
private modelService: ModelService,
private historyService: HistoryService,
- private dialog: MatDialog,
- private simulationModeService: SimulationModeService
+ private simulationModeService: SimulationModeService,
+ private editModeService: EditModeService,
+ private router: Router,
) {
this._models = new Map();
this._modelSubscription = modelService.modelSubject.pipe(
@@ -58,8 +60,16 @@ export class ApplicationService implements OnDestroy {
return this._models;
}
- getAndIncrementModelSequence(): number {
- return this._modelIdSequence++;
+ get modelList(): Array {
+ return Array.from(this._models.values());
+ }
+
+ public nextModelId(): string {
+ const id = this._modelIdSequence.next();
+ if (this.models.has(id)) {
+ return this.nextModelId();
+ }
+ return id;
}
getModel(id: string): PetriNet {
@@ -74,36 +84,26 @@ export class ApplicationService implements OnDestroy {
this._application = Application.getEmpty();
this.addNewEmptyModel();
console.log('New application created', this._application);
+ this._modelIdSequence.reset([]);
return this._application;
}
private deleteModel(processId: string) {
if (this.modelService.model.id === processId) {
- if (this._models.size > 1) {
- this.switchActiveModel(this._models.keys().next().value);
- } else {
+ if (this._models.size <= 1) {
this.addNewEmptyModel(); // nemôže byť aplikácie bez procesu
- this.switchActiveModel(this._models.keys().next().value);
}
+ this.switchActiveModel(this._models.keys().next().value);
}
this._models.delete(processId);
this.updateProcesses();
console.log('Process removed', processId);
}
- removeModel(processId: string, confirmationDialog = true) { // TODO remove cez app edit dialog nefunguje, vymaze iný prvok
- if (!confirmationDialog) {
- this.deleteModel(processId);
- } else {
- const dialogRef = this.dialog.open(DialogDeleteModelComponent);
- dialogRef.afterClosed().subscribe(result => {
- if (result === true) {
- const oldId = this.modelService.model.id;
- this.deleteModel(oldId);
- this.historyService.save(`Model ${oldId} has been deleted.`, this.modelService.model);
- }
- });
- }
+ removeModel(processId: string) {
+ const model = this.getModel(processId);
+ this.deleteModel(processId);
+ this.historyService.save(`Model ${processId} has been deleted.`, model);
}
addModel(net: PetriNet): void {
@@ -113,34 +113,52 @@ export class ApplicationService implements OnDestroy {
console.log('New process added', net.id);
}
- addNewEmptyModel() {
- const newModel = this.modelService.newModel();
- this._models.set(newModel.id, newModel);
+ updateModel(oldId: string, model: PetriNet): void {
+ // TODO: NAB-380 reload model undo/redo
+ const oldModel = this._models.get(oldId);
+ if (!oldModel) {
+ return
+ }
+ if (oldId === model.id) {
+ this._models.set(oldId, model);
+ } else {
+ this._models.delete(oldId);
+ this._models.set(oldId, model);
+ }
this.updateProcesses();
- // this.modelService.model = this.modelService.newModel();
- this.historyService.save(`New model has been created.`, newModel);
- console.log('New process added', newModel.id);
+ }
+
+ addNewEmptyModel(): PetriNet {
+ const newModel = this.modelService.newModel()
+ this.addModel(newModel);
+ return newModel;
}
updateModelId(oldId: string, newId: string) {
- if (!this._models.get(oldId)) return;
+ if (!this._models.get(oldId)) {
+ return;
+ }
this._models.set(newId, this._models.get(oldId));
this._models.delete(oldId);
this.updateProcesses();
+ this.historyService.changeId(oldId, newId);
console.log('Process id updated', oldId, '->', newId);
}
switchActiveModel(processId: string) {
- if (!this._models.get(processId)) return;
+ if (!this._models.get(processId)) {
+ return;
+ }
this.modelService.model = this._models.get(processId);
this.simulationModeService.originalModel.next(this._models.get(processId));
- this.historyService.save(`Model ${this.modelService.model.id} has been changed.`, this._models.get(processId));
+ this.router.navigate(['/modeler']);
+ this.editModeService.renderModel();
console.log('Current process switched', processId);
}
switchToFirst() {
if (this._application.processes.length > 0) {
- this.modelService.model = this._models.get(this._application.processes[0]);
+ this.switchActiveModel(this._application.processes[0]);
}
}
diff --git a/src/app/project-builder/application.ts b/src/app/project-builder/application.ts
index 6c0ca1b..03654bb 100644
--- a/src/app/project-builder/application.ts
+++ b/src/app/project-builder/application.ts
@@ -1,22 +1,22 @@
export default class Application {
// TODO validácie
public appMeta: string = 'application';
- public id: string; // TODO doplniť o identifikátor a tak aby prešiel cez sanitáciu
+ public id: string;
public name: string;
- public description: string = '';
- public version: string = '1.0.0';
+ public description: string;
+ public version: string;
public author: string;
public tags: Array = [];
public processes: Array = [];
- constructor(name: string, description: string = '', version: string = '1.0.0') {
+ constructor(id: string, name: string, description: string, version: string) {
+ this.id = id;
this.name = name;
this.description = description;
this.version = version;
}
public static getEmpty(): Application {
- return new Application('New Application');
+ return new Application('new_application', 'New Application', '', '1.0.0');
}
-
}
diff --git a/src/app/tutorial/tutorial-service.ts b/src/app/tutorial/tutorial-service.ts
index 6515f1d..f0df6fc 100644
--- a/src/app/tutorial/tutorial-service.ts
+++ b/src/app/tutorial/tutorial-service.ts
@@ -1,8 +1,6 @@
import {Injectable} from '@angular/core';
-import {MortgageService} from '../modeler/mortgage.service';
import {Router} from '@angular/router';
import {TutorialStep} from './tutorial-step';
-import {ModelService} from '../modeler/services/model/model.service';
@Injectable({
providedIn: 'root'
@@ -27,24 +25,16 @@ export class TutorialService {
bug: TutorialStep;
steps: Array;
onClose: () => void;
- mortgageLoaded: boolean;
constructor(
- private mortgageService: MortgageService,
private router: Router,
- private modelService: ModelService
) {
this.welcome = TutorialStep.of(
'welcome',
'Welcome to the Netgrif Application Builder',
'Netgrif Application Builder (NAB) is the tool for building process driven applications using Petriflow language. NAB is composed of several modules that help you in different stages of application development.',
() => {
- this.mortgageLoaded = false;
- if (modelService.model.getTransitions().length === 0 && modelService.model.getPlaces().length === 0 && modelService.model.getArcs().length === 0 &&
- modelService.model.getDataSet().length === 0 && modelService.model.getTransactions().length === 0 && modelService.model.getRoles().length === 0) {
- this.mortgageService.loadModel();
- this.mortgageLoaded = true;
- }
+ // TODO: release/3.0 - load mortgage - cyclic dependency through MortgageService
this.router.navigate(['/modeler']);
},
() => {
@@ -235,9 +225,6 @@ export class TutorialService {
];
this.onClose = () => {
this.router.navigate(['/modeler']);
- if (this.mortgageLoaded) {
- this.modelService.model = this.modelService.newModel(); // TODO toto vytvorí nový model ktorý asi nie je treba
- }
};
}
}
diff --git a/src/assets/mortgage_simple.xml b/src/assets/mortgage_simple.xml
index 1a4a2fb..224f6d6 100644
--- a/src/assets/mortgage_simple.xml
+++ b/src/assets/mortgage_simple.xml
@@ -3,6 +3,7 @@
1.0.0
MTG
Mortgage
+ house
true
false
false
diff --git a/src/styles.scss b/src/styles.scss
index 854ae26..97e95e1 100644
--- a/src/styles.scss
+++ b/src/styles.scss
@@ -91,6 +91,7 @@ body {
color: black;
fill: black;
}
+
nab-tool, nab-control-panel-mode, nab-import-tool-button {
.mat-mdc-icon-button .mat-mdc-button-persistent-ripple {
border-radius: 5px;
@@ -110,24 +111,24 @@ nab-tool, nab-control-panel-mode, nab-import-tool-button {
}
.checkbox-background-warn {
- .mdc-checkbox .mdc-checkbox__native-control:enabled:checked~.mdc-checkbox__background, .mdc-checkbox .mdc-checkbox__native-control:enabled:indeterminate~.mdc-checkbox__background, .mdc-checkbox .mdc-checkbox__native-control[data-indeterminate=true]:enabled~.mdc-checkbox__background {
+ .mdc-checkbox .mdc-checkbox__native-control:enabled:checked ~ .mdc-checkbox__background, .mdc-checkbox .mdc-checkbox__native-control:enabled:indeterminate ~ .mdc-checkbox__background, .mdc-checkbox .mdc-checkbox__native-control[data-indeterminate=true]:enabled ~ .mdc-checkbox__background {
border-color: map-get(netgif-theme.$netgrif-warn, 500) !important;
background-color: map-get(netgif-theme.$netgrif-warn, 500) !important;
}
- .mdc-checkbox .mdc-checkbox__native-control:enabled~.mdc-checkbox__background .mdc-checkbox__mixedmark {
+ .mdc-checkbox .mdc-checkbox__native-control:enabled ~ .mdc-checkbox__background .mdc-checkbox__mixedmark {
border-color: white;
color: white;
}
}
.checkbox-background-green {
- .mdc-checkbox .mdc-checkbox__native-control:enabled:checked~.mdc-checkbox__background, .mdc-checkbox .mdc-checkbox__native-control:enabled:indeterminate~.mdc-checkbox__background, .mdc-checkbox .mdc-checkbox__native-control[data-indeterminate=true]:enabled~.mdc-checkbox__background {
- border-color:#008800 !important;
+ .mdc-checkbox .mdc-checkbox__native-control:enabled:checked ~ .mdc-checkbox__background, .mdc-checkbox .mdc-checkbox__native-control:enabled:indeterminate ~ .mdc-checkbox__background, .mdc-checkbox .mdc-checkbox__native-control[data-indeterminate=true]:enabled ~ .mdc-checkbox__background {
+ border-color: #008800 !important;
background-color: #008800 !important;
}
- .mdc-checkbox .mdc-checkbox__native-control:enabled~.mdc-checkbox__background .mdc-checkbox__checkmark {
+ .mdc-checkbox .mdc-checkbox__native-control:enabled ~ .mdc-checkbox__background .mdc-checkbox__checkmark {
color: white;
}
}
@@ -171,7 +172,7 @@ nab-tool, nab-control-panel-mode, nab-import-tool-button {
width: 60% !important;
}
-.mat-mdc-dialog-container .mat-mdc-dialog-title+.mat-mdc-dialog-content {
+.mat-mdc-dialog-container .mat-mdc-dialog-title + .mat-mdc-dialog-content {
padding-top: 8px !important;
}
@@ -185,6 +186,7 @@ nab-simulation-mode {
.builder-field-input {
margin-top: 6px;
+
.mat-mdc-form-field-subscript-wrapper {
position: absolute;
width: auto;
@@ -217,14 +219,17 @@ nab-simulation-mode {
padding-left: 8px;
padding-right: 4px;
}
+
.mat-expansion-panel-body {
padding-left: 8px;
padding-right: 4px;
}
+
.mat-expanded {
margin-bottom: 0;
margin-top: 0;
}
+
.mat-expansion-panel-header.mat-expanded {
height: 40px;
}
@@ -257,3 +262,20 @@ nab-simulation-mode {
background-size: contain;
}
}
+
+.app-tabs {
+ .mat-mdc-tab.mdc-tab {
+ height: 40px !important;
+ padding-left: 8px !important;
+ padding-right: 8px !important;
+ }
+
+ .mdc-tab__content {
+ pointer-events: all !important;
+ }
+
+ .mat-mdc-tab.mat-mdc-tab-disabled {
+ opacity: 1 !important;
+ }
+}
+