├── EXECUTABLE ├── Zip Archiv └── readme.md ├── Resources ├── Resources.md ├── Logo.png ├── Export.png ├── editor.png ├── update.png ├── Library.png ├── PLC_Logo.png ├── SaveToXML.png ├── createPOU.png ├── homepage.png ├── languages.png ├── variables.png ├── NewProject.png ├── OpenProject.png ├── addVariable.png ├── connections.png ├── filesystem.PNG ├── pouOverview.png ├── use_case_1.PNG ├── use_case_2.PNG ├── use_case_3.PNG ├── NavigationBar.png ├── System Model.png ├── navigationtree.png ├── plcopeneditor.PNG ├── Component Model.png ├── Architectural Model.png ├── project_structure.png └── case_overview_diagram.png ├── SOURCE ├── src │ ├── assets │ │ ├── .gitkeep │ │ ├── emptyXML.xml │ │ ├── node.jpg │ │ ├── PLC_Logo.png │ │ ├── corruptedXML.xml │ │ ├── plc.xml │ │ └── plc_valid.xml │ ├── app │ │ ├── app.component.css │ │ ├── about │ │ │ ├── about.component.css │ │ │ ├── about.component.ts │ │ │ ├── about.component.spec.ts │ │ │ └── about.component.html │ │ ├── footer │ │ │ ├── footer.component.css │ │ │ ├── footer.component.ts │ │ │ ├── footer.component.html │ │ │ └── footer.component.spec.ts │ │ ├── graph │ │ │ ├── graph.component.css │ │ │ └── graph.component.spec.ts │ │ ├── app.component.html │ │ ├── app.component.ts │ │ ├── navigation │ │ │ ├── navigation.component.css │ │ │ ├── navigation.component.ts │ │ │ ├── navigation.component.spec.ts │ │ │ └── navigation.component.html │ │ ├── user-manual │ │ │ ├── user-manual.component.ts │ │ │ ├── user-manual.component.css │ │ │ └── user-manual.component.spec.ts │ │ ├── services │ │ │ ├── editor.service.spec.ts │ │ │ ├── import.service.spec.ts │ │ │ ├── project.service.spec.ts │ │ │ ├── import.service.ts │ │ │ └── project.service.ts │ │ ├── editor │ │ │ ├── editor.component.ts │ │ │ ├── editor.component.css │ │ │ ├── editor.component.spec.ts │ │ │ └── editor.component.html │ │ ├── library │ │ │ ├── library.component.css │ │ │ ├── library.component.spec.ts │ │ │ ├── library.component.html │ │ │ └── library.component.ts │ │ ├── homepage │ │ │ ├── homepage.component.css │ │ │ ├── homepage.component.spec.ts │ │ │ ├── homepage.component.ts │ │ │ └── homepage.component.html │ │ ├── variables-list │ │ │ ├── variables-list.component.css │ │ │ ├── variables-list.component.spec.ts │ │ │ ├── variables-list.component.html │ │ │ └── variables-list.component.ts │ │ ├── project-overview │ │ │ ├── project-overview.component.spec.ts │ │ │ ├── project-overview.component.css │ │ │ ├── project-overview.component.ts │ │ │ └── project-overview.component.html │ │ ├── app-routing.module.ts │ │ ├── models │ │ │ ├── PLCNode.ts │ │ │ ├── fbdObjects │ │ │ │ ├── fbdLabel.ts │ │ │ │ ├── fbdReturn.ts │ │ │ │ ├── fbdJump.ts │ │ │ │ ├── fbdOutVariable.ts │ │ │ │ └── fbdInVariable.ts │ │ │ ├── commonObjects │ │ │ │ ├── commonError.ts │ │ │ │ ├── commonComment.ts │ │ │ │ ├── commonContinuation.ts │ │ │ │ ├── commonConnector.ts │ │ │ │ └── commonActionBlock.ts │ │ │ ├── ldObjects │ │ │ │ ├── ldLeftPowerRail.ts │ │ │ │ ├── ldRightPowerRail.ts │ │ │ │ ├── ldCoil.ts │ │ │ │ └── ldContact.ts │ │ │ ├── sfcObjects │ │ │ │ ├── sfcJumpStep.ts │ │ │ │ ├── sfcStep.ts │ │ │ │ ├── sfcMacroStep.ts │ │ │ │ ├── sfcSelectionDivergence.ts │ │ │ │ ├── sfcSelectionConvergence.ts │ │ │ │ ├── sfcSimultaneousConvergence.ts │ │ │ │ └── sfcSimultaneousDivergence.ts │ │ │ └── variable.ts │ │ ├── app.component.spec.ts │ │ └── app.module.ts │ ├── environments │ │ ├── environment.prod.ts │ │ └── environment.ts │ ├── favicon.ico │ ├── main.ts │ ├── styles.css │ ├── index.html │ ├── test.ts │ └── polyfills.ts ├── e2e │ ├── src │ │ ├── app.po.ts │ │ └── app.e2e-spec.ts │ ├── tsconfig.json │ └── protractor.conf.js ├── .editorconfig ├── tsconfig.app.json ├── tsconfig.spec.json ├── tsconfig.json ├── .browserslistrc ├── .gitignore ├── README.md ├── karma.conf.js ├── package.json ├── tslint.json └── angular.json ├── Meeting Minutes ├── MeetingMinutes.docx └── MeetingMinutes.pdf ├── PROJECT ├── CRS │ ├── TINF19C_CRS_Team_1_0v1.pdf │ └── TINF19C_CRS_Team_1_0v1.docx ├── SRS.md ├── SAS.md ├── Business Case │ ├── TINF19C_BC_Team_1_1v0.docx │ └── TINF19C_BC_Team_1_1v0.pdf ├── Project Plan │ ├── TINF19C_PM_Team_1_0v1.docx │ ├── TINF19C_PM_Team_1_0v1.pdf │ └── GANTT-Chart │ │ └── TINF19C_GanttChart_Team_1_0v1.xlsx ├── TINF19C_3.SemesterPräsentation_Team_1_1v0.pptx └── TINF19C_4.SemesterPräsentation_Team_1_2v0.pptx ├── TINF19C_4.SemesterPräsentation_Team_1_2v0.pptx ├── TINF19C_4.SemesterPräsentation_Team_1_2v0.pptx ├── LICENSE └── README.md /EXECUTABLE/Zip Archiv: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /Resources/Resources.md: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /SOURCE/src/assets/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /SOURCE/src/app/app.component.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /SOURCE/src/assets/emptyXML.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /SOURCE/src/app/about/about.component.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /SOURCE/src/app/footer/footer.component.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /SOURCE/src/app/graph/graph.component.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /SOURCE/src/app/app.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /Resources/Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elian15122000/TINF19C-PLCOpen-Editor/HEAD/Resources/Logo.png -------------------------------------------------------------------------------- /SOURCE/src/environments/environment.prod.ts: -------------------------------------------------------------------------------- 1 | export const environment = { 2 | production: true 3 | }; 4 | -------------------------------------------------------------------------------- /Resources/Export.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elian15122000/TINF19C-PLCOpen-Editor/HEAD/Resources/Export.png -------------------------------------------------------------------------------- /Resources/editor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elian15122000/TINF19C-PLCOpen-Editor/HEAD/Resources/editor.png -------------------------------------------------------------------------------- /Resources/update.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elian15122000/TINF19C-PLCOpen-Editor/HEAD/Resources/update.png -------------------------------------------------------------------------------- /Resources/Library.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elian15122000/TINF19C-PLCOpen-Editor/HEAD/Resources/Library.png -------------------------------------------------------------------------------- /Resources/PLC_Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elian15122000/TINF19C-PLCOpen-Editor/HEAD/Resources/PLC_Logo.png -------------------------------------------------------------------------------- /Resources/SaveToXML.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elian15122000/TINF19C-PLCOpen-Editor/HEAD/Resources/SaveToXML.png -------------------------------------------------------------------------------- /Resources/createPOU.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elian15122000/TINF19C-PLCOpen-Editor/HEAD/Resources/createPOU.png -------------------------------------------------------------------------------- /Resources/homepage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elian15122000/TINF19C-PLCOpen-Editor/HEAD/Resources/homepage.png -------------------------------------------------------------------------------- /Resources/languages.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elian15122000/TINF19C-PLCOpen-Editor/HEAD/Resources/languages.png -------------------------------------------------------------------------------- /Resources/variables.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elian15122000/TINF19C-PLCOpen-Editor/HEAD/Resources/variables.png -------------------------------------------------------------------------------- /SOURCE/src/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elian15122000/TINF19C-PLCOpen-Editor/HEAD/SOURCE/src/favicon.ico -------------------------------------------------------------------------------- /Resources/NewProject.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elian15122000/TINF19C-PLCOpen-Editor/HEAD/Resources/NewProject.png -------------------------------------------------------------------------------- /Resources/OpenProject.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elian15122000/TINF19C-PLCOpen-Editor/HEAD/Resources/OpenProject.png -------------------------------------------------------------------------------- /Resources/addVariable.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elian15122000/TINF19C-PLCOpen-Editor/HEAD/Resources/addVariable.png -------------------------------------------------------------------------------- /Resources/connections.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elian15122000/TINF19C-PLCOpen-Editor/HEAD/Resources/connections.png -------------------------------------------------------------------------------- /Resources/filesystem.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elian15122000/TINF19C-PLCOpen-Editor/HEAD/Resources/filesystem.PNG -------------------------------------------------------------------------------- /Resources/pouOverview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elian15122000/TINF19C-PLCOpen-Editor/HEAD/Resources/pouOverview.png -------------------------------------------------------------------------------- /Resources/use_case_1.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elian15122000/TINF19C-PLCOpen-Editor/HEAD/Resources/use_case_1.PNG -------------------------------------------------------------------------------- /Resources/use_case_2.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elian15122000/TINF19C-PLCOpen-Editor/HEAD/Resources/use_case_2.PNG -------------------------------------------------------------------------------- /Resources/use_case_3.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elian15122000/TINF19C-PLCOpen-Editor/HEAD/Resources/use_case_3.PNG -------------------------------------------------------------------------------- /Resources/NavigationBar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elian15122000/TINF19C-PLCOpen-Editor/HEAD/Resources/NavigationBar.png -------------------------------------------------------------------------------- /Resources/System Model.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elian15122000/TINF19C-PLCOpen-Editor/HEAD/Resources/System Model.png -------------------------------------------------------------------------------- /Resources/navigationtree.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elian15122000/TINF19C-PLCOpen-Editor/HEAD/Resources/navigationtree.png -------------------------------------------------------------------------------- /Resources/plcopeneditor.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elian15122000/TINF19C-PLCOpen-Editor/HEAD/Resources/plcopeneditor.PNG -------------------------------------------------------------------------------- /SOURCE/src/assets/node.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elian15122000/TINF19C-PLCOpen-Editor/HEAD/SOURCE/src/assets/node.jpg -------------------------------------------------------------------------------- /Resources/Component Model.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elian15122000/TINF19C-PLCOpen-Editor/HEAD/Resources/Component Model.png -------------------------------------------------------------------------------- /SOURCE/src/assets/PLC_Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elian15122000/TINF19C-PLCOpen-Editor/HEAD/SOURCE/src/assets/PLC_Logo.png -------------------------------------------------------------------------------- /Resources/Architectural Model.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elian15122000/TINF19C-PLCOpen-Editor/HEAD/Resources/Architectural Model.png -------------------------------------------------------------------------------- /Resources/project_structure.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elian15122000/TINF19C-PLCOpen-Editor/HEAD/Resources/project_structure.png -------------------------------------------------------------------------------- /Meeting Minutes/MeetingMinutes.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elian15122000/TINF19C-PLCOpen-Editor/HEAD/Meeting Minutes/MeetingMinutes.docx -------------------------------------------------------------------------------- /Meeting Minutes/MeetingMinutes.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elian15122000/TINF19C-PLCOpen-Editor/HEAD/Meeting Minutes/MeetingMinutes.pdf -------------------------------------------------------------------------------- /Resources/case_overview_diagram.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elian15122000/TINF19C-PLCOpen-Editor/HEAD/Resources/case_overview_diagram.png -------------------------------------------------------------------------------- /PROJECT/CRS/TINF19C_CRS_Team_1_0v1.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elian15122000/TINF19C-PLCOpen-Editor/HEAD/PROJECT/CRS/TINF19C_CRS_Team_1_0v1.pdf -------------------------------------------------------------------------------- /PROJECT/CRS/TINF19C_CRS_Team_1_0v1.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elian15122000/TINF19C-PLCOpen-Editor/HEAD/PROJECT/CRS/TINF19C_CRS_Team_1_0v1.docx -------------------------------------------------------------------------------- /PROJECT/SRS.md: -------------------------------------------------------------------------------- 1 | Link to Wiki: [System Requirement System](https://github.com/elian15122000/TINF19C-PLCOpen-Editor/wiki/1-System-Requirements-Specification) 2 | -------------------------------------------------------------------------------- /PROJECT/SAS.md: -------------------------------------------------------------------------------- 1 | Link to Wiki: [System Architecture System](https://github.com/elian15122000/TINF19C-PLCOpen-Editor/wiki/2-System-Architecture-Specification) 2 | -------------------------------------------------------------------------------- /PROJECT/Business Case/TINF19C_BC_Team_1_1v0.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elian15122000/TINF19C-PLCOpen-Editor/HEAD/PROJECT/Business Case/TINF19C_BC_Team_1_1v0.docx -------------------------------------------------------------------------------- /PROJECT/Business Case/TINF19C_BC_Team_1_1v0.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elian15122000/TINF19C-PLCOpen-Editor/HEAD/PROJECT/Business Case/TINF19C_BC_Team_1_1v0.pdf -------------------------------------------------------------------------------- /PROJECT/Project Plan/TINF19C_PM_Team_1_0v1.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elian15122000/TINF19C-PLCOpen-Editor/HEAD/PROJECT/Project Plan/TINF19C_PM_Team_1_0v1.docx -------------------------------------------------------------------------------- /PROJECT/Project Plan/TINF19C_PM_Team_1_0v1.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elian15122000/TINF19C-PLCOpen-Editor/HEAD/PROJECT/Project Plan/TINF19C_PM_Team_1_0v1.pdf -------------------------------------------------------------------------------- /TINF19C_4.SemesterPräsentation_Team_1_2v0.pptx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elian15122000/TINF19C-PLCOpen-Editor/HEAD/TINF19C_4.SemesterPräsentation_Team_1_2v0.pptx -------------------------------------------------------------------------------- /TINF19C_4.SemesterPräsentation_Team_1_2v0.pptx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elian15122000/TINF19C-PLCOpen-Editor/HEAD/TINF19C_4.SemesterPräsentation_Team_1_2v0.pptx -------------------------------------------------------------------------------- /PROJECT/TINF19C_3.SemesterPräsentation_Team_1_1v0.pptx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elian15122000/TINF19C-PLCOpen-Editor/HEAD/PROJECT/TINF19C_3.SemesterPräsentation_Team_1_1v0.pptx -------------------------------------------------------------------------------- /PROJECT/TINF19C_4.SemesterPräsentation_Team_1_2v0.pptx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elian15122000/TINF19C-PLCOpen-Editor/HEAD/PROJECT/TINF19C_4.SemesterPräsentation_Team_1_2v0.pptx -------------------------------------------------------------------------------- /PROJECT/Project Plan/GANTT-Chart/TINF19C_GanttChart_Team_1_0v1.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elian15122000/TINF19C-PLCOpen-Editor/HEAD/PROJECT/Project Plan/GANTT-Chart/TINF19C_GanttChart_Team_1_0v1.xlsx -------------------------------------------------------------------------------- /SOURCE/src/app/app.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-root', 5 | templateUrl: './app.component.html', 6 | styleUrls: ['./app.component.css'] 7 | }) 8 | export class AppComponent { 9 | 10 | } 11 | -------------------------------------------------------------------------------- /SOURCE/e2e/src/app.po.ts: -------------------------------------------------------------------------------- 1 | import { browser, by, element } from 'protractor'; 2 | 3 | export class AppPage { 4 | async navigateTo(): Promise { 5 | return browser.get(browser.baseUrl); 6 | } 7 | 8 | async getTitleText(): Promise { 9 | return element(by.css('app-root .content span')).getText(); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /SOURCE/e2e/tsconfig.json: -------------------------------------------------------------------------------- 1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */ 2 | { 3 | "extends": "../tsconfig.json", 4 | "compilerOptions": { 5 | "outDir": "../out-tsc/e2e", 6 | "module": "commonjs", 7 | "target": "es2018", 8 | "types": [ 9 | "jasmine", 10 | "node" 11 | ] 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /SOURCE/.editorconfig: -------------------------------------------------------------------------------- 1 | # Editor configuration, see https://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | indent_style = space 7 | indent_size = 2 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.ts] 12 | quote_type = single 13 | 14 | [*.md] 15 | max_line_length = off 16 | trim_trailing_whitespace = false 17 | -------------------------------------------------------------------------------- /SOURCE/src/app/about/about.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-about', 5 | templateUrl: './about.component.html', 6 | styleUrls: ['./about.component.css'] 7 | }) 8 | export class AboutComponent implements OnInit { 9 | 10 | constructor() { } 11 | 12 | ngOnInit(): void { 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /SOURCE/tsconfig.app.json: -------------------------------------------------------------------------------- 1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */ 2 | { 3 | "extends": "./tsconfig.json", 4 | "compilerOptions": { 5 | "outDir": "./out-tsc/app", 6 | "types": [] 7 | }, 8 | "files": [ 9 | "src/main.ts", 10 | "src/polyfills.ts" 11 | ], 12 | "include": [ 13 | "src/**/*.d.ts" 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /SOURCE/src/app/footer/footer.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-footer', 5 | templateUrl: './footer.component.html', 6 | styleUrls: ['./footer.component.css'] 7 | }) 8 | export class FooterComponent implements OnInit { 9 | 10 | constructor() { } 11 | 12 | ngOnInit(): void { 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /SOURCE/src/app/navigation/navigation.component.css: -------------------------------------------------------------------------------- 1 | /** 2 | 3 | @filename : navigation.component.css 4 | @author : Elian Yildirim 5 | @Last_Modified : 13.05.2021 6 | */ 7 | 8 | #mobilebar { 9 | display: none; 10 | } 11 | 12 | @media only screen and (max-width: 800px) { 13 | #mobilebar { 14 | display: block; 15 | } 16 | #desktopbar { 17 | display: none; 18 | } 19 | } -------------------------------------------------------------------------------- /SOURCE/src/app/navigation/navigation.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-navigation', 5 | templateUrl: './navigation.component.html', 6 | styleUrls: ['./navigation.component.css'] 7 | }) 8 | export class NavigationComponent implements OnInit { 9 | 10 | constructor() { } 11 | 12 | ngOnInit(): void { 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /SOURCE/src/app/user-manual/user-manual.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-user-manual', 5 | templateUrl: './user-manual.component.html', 6 | styleUrls: ['./user-manual.component.css'] 7 | }) 8 | export class UserManualComponent implements OnInit { 9 | 10 | constructor() { } 11 | 12 | ngOnInit(): void { 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /SOURCE/src/main.ts: -------------------------------------------------------------------------------- 1 | import { enableProdMode } from '@angular/core'; 2 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; 3 | 4 | import { AppModule } from './app/app.module'; 5 | import { environment } from './environments/environment'; 6 | 7 | if (environment.production) { 8 | enableProdMode(); 9 | } 10 | 11 | platformBrowserDynamic().bootstrapModule(AppModule) 12 | .catch(err => console.error(err)); 13 | -------------------------------------------------------------------------------- /SOURCE/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */ 2 | { 3 | "extends": "./tsconfig.json", 4 | "compilerOptions": { 5 | "outDir": "./out-tsc/spec", 6 | "types": [ 7 | "jasmine" 8 | ] 9 | }, 10 | "files": [ 11 | "src/test.ts", 12 | "src/polyfills.ts" 13 | ], 14 | "include": [ 15 | "src/**/*.spec.ts", 16 | "src/**/*.d.ts" 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /SOURCE/src/app/services/editor.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed } from '@angular/core/testing'; 2 | 3 | import { EditorService } from './editor.service'; 4 | 5 | describe('EditorService', () => { 6 | let service: EditorService; 7 | 8 | beforeEach(() => { 9 | TestBed.configureTestingModule({}); 10 | service = TestBed.inject(EditorService); 11 | }); 12 | 13 | it('should be created', () => { 14 | expect(service).toBeTruthy(); 15 | }); 16 | }); 17 | -------------------------------------------------------------------------------- /SOURCE/src/app/services/import.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed } from '@angular/core/testing'; 2 | 3 | import { ImportService } from './import.service'; 4 | 5 | describe('ImportService', () => { 6 | let service: ImportService; 7 | 8 | beforeEach(() => { 9 | TestBed.configureTestingModule({}); 10 | service = TestBed.inject(ImportService); 11 | }); 12 | 13 | it('should be created', () => { 14 | expect(service).toBeTruthy(); 15 | }); 16 | }); 17 | -------------------------------------------------------------------------------- /SOURCE/src/app/services/project.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed } from '@angular/core/testing'; 2 | 3 | import { ProjectService } from './project.service'; 4 | 5 | describe('ProjectService', () => { 6 | let service: ProjectService; 7 | 8 | beforeEach(() => { 9 | TestBed.configureTestingModule({}); 10 | service = TestBed.inject(ProjectService); 11 | }); 12 | 13 | it('should be created', () => { 14 | expect(service).toBeTruthy(); 15 | }); 16 | }); 17 | -------------------------------------------------------------------------------- /SOURCE/tsconfig.json: -------------------------------------------------------------------------------- 1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */ 2 | { 3 | "compileOnSave": false, 4 | "compilerOptions": { 5 | "baseUrl": "./", 6 | "outDir": "./dist/out-tsc", 7 | "sourceMap": true, 8 | "declaration": false, 9 | "downlevelIteration": true, 10 | "experimentalDecorators": true, 11 | "moduleResolution": "node", 12 | "importHelpers": true, 13 | "target": "es2015", 14 | "module": "es2020", 15 | "lib": [ 16 | "es2018", 17 | "dom" 18 | ] 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /SOURCE/src/styles.css: -------------------------------------------------------------------------------- 1 | /* You can add global styles to this file, and also import other style files */ 2 | 3 | @import '~bootstrap/dist/css/bootstrap.min.css'; 4 | 5 | .modal-backdrop { 6 | /* bug fix - no overlay */ 7 | display: none; 8 | } 9 | 10 | html { 11 | scroll-behavior: smooth; 12 | } 13 | 14 | /* Hide scrollbar for Chrome, Safari and Opera */ 15 | body::-webkit-scrollbar { 16 | display: none; 17 | } 18 | 19 | 20 | body { 21 | -ms-overflow-style: none; /* IE and Edge */ 22 | scrollbar-width: none; /* Firefox */ 23 | } -------------------------------------------------------------------------------- /SOURCE/src/app/footer/footer.component.html: -------------------------------------------------------------------------------- 1 | 6 | 7 |
8 |
9 | 10 | 11 |
12 | 13 |
14 |
15 | 16 | 17 | © 2020 - 2021 18 |
19 |
20 | 21 |
22 |
23 |
-------------------------------------------------------------------------------- /SOURCE/src/app/editor/editor.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | 3 | 4 | @Component({ 5 | selector: 'app-editor', 6 | templateUrl: './editor.component.html', 7 | styleUrls: ['./editor.component.css'] 8 | }) 9 | 10 | 11 | export class EditorComponent implements OnInit { 12 | 13 | 14 | constructor() { 15 | 16 | } 17 | 18 | ngOnInit(): void { 19 | 20 | window.addEventListener('beforeunload', (event) => { 21 | event.preventDefault(); 22 | event.returnValue = 'Unsaved modifications'; 23 | return event; 24 | }); 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /SOURCE/src/app/user-manual/user-manual.component.css: -------------------------------------------------------------------------------- 1 | .backButton { 2 | background-color: black; 3 | border: none; 4 | color: white; 5 | padding: 10px 20px; 6 | text-align: center; 7 | text-decoration: none; 8 | display: inline-block; 9 | margin: 8px 2px; 10 | cursor: pointer; 11 | border-radius: 16px; 12 | } 13 | a{ 14 | color: white; 15 | font-size: small; 16 | } 17 | a:link { 18 | text-decoration: none; 19 | } 20 | 21 | a:visited { 22 | text-decoration: none; 23 | } 24 | 25 | a:hover { 26 | text-decoration: underline; 27 | } 28 | .userManualText { 29 | color: red; 30 | } 31 | -------------------------------------------------------------------------------- /SOURCE/src/app/library/library.component.css: -------------------------------------------------------------------------------- 1 | 2 | 3 | .libraryList { 4 | box-sizing: border-box; 5 | margin-top: 20px; 6 | padding: 20px; 7 | max-height: 1000px; 8 | overflow-y: scroll !important; 9 | border-top: solid black 1px; 10 | } 11 | 12 | .lib-el { 13 | position: relative; 14 | display: inline-block; 15 | margin-top: 20px; 16 | width: 200px; 17 | } 18 | 19 | .lib-el[title]:hover::after { 20 | background:white; 21 | border: black 1px solid; 22 | content: attr(title); 23 | position: absolute; 24 | width: 200px; 25 | left: 50%; 26 | top: 50%; 27 | } 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /SOURCE/src/app/editor/editor.component.css: -------------------------------------------------------------------------------- 1 | /** 2 | 3 | @filename : editor.component.css 4 | @author : Elian Yildirim 5 | @Last_Modified : 13.05.2021 6 | 7 | */ 8 | 9 | .variablesList { 10 | height: 250px; 11 | overflow: auto; 12 | } 13 | 14 | .library { 15 | background-color: white; 16 | 17 | } 18 | .graph { 19 | z-index: 1; 20 | } 21 | 22 | .editor { 23 | overflow: hidden; 24 | } 25 | 26 | ::-webkit-scrollbar { 27 | display: none; 28 | } 29 | 30 | .editor { 31 | -ms-overflow-style: none; 32 | scrollbar-width: none; 33 | } 34 | 35 | .library { 36 | height: 300px; 37 | } 38 | 39 | -------------------------------------------------------------------------------- /EXECUTABLE/readme.md: -------------------------------------------------------------------------------- 1 | # Executables - PLC Editor on Angular 2 | PLC Editor on Angular wird mit Angular entwickelt. Da Angular keine Executables erzeugt muss man folgendes machen, um den Projekt zu starten 3 | 4 | * NodeJs, Angular und Npm müssen installiert sein 5 | 6 | * In der Terminal in den Verzeichnis `SOURCE` gehen. 7 | 8 | * Führe `npm i`, um alle Packages zu installieren 9 | 10 | * Führe `ng serve`, um die Angular-Umgebung zu starten. 11 | 12 | * gehe auf `localhost://4200` im Browser. Verwende dafür **Firefox** oder **Safari**. 13 | 14 | Den User-Manual in der WIKI hier https://github.com/elian15122000/TINF19C-PLCOpen-Editor/wiki/User-Manual 15 | -------------------------------------------------------------------------------- /SOURCE/src/environments/environment.ts: -------------------------------------------------------------------------------- 1 | // This file can be replaced during build by using the `fileReplacements` array. 2 | // `ng build --prod` replaces `environment.ts` with `environment.prod.ts`. 3 | // The list of file replacements can be found in `angular.json`. 4 | 5 | export const environment = { 6 | production: false 7 | }; 8 | 9 | /* 10 | * For easier debugging in development mode, you can import the following file 11 | * to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`. 12 | * 13 | * This import should be commented out in production mode because it will have a negative impact 14 | * on performance if an error is thrown. 15 | */ 16 | // import 'zone.js/dist/zone-error'; // Included with Angular CLI. 17 | -------------------------------------------------------------------------------- /SOURCE/src/app/about/about.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { AboutComponent } from './about.component'; 4 | 5 | describe('AboutComponent', () => { 6 | let component: AboutComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async () => { 10 | await TestBed.configureTestingModule({ 11 | declarations: [ AboutComponent ] 12 | }) 13 | .compileComponents(); 14 | }); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(AboutComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /SOURCE/src/app/graph/graph.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { GraphComponent } from './graph.component'; 4 | 5 | describe('GraphComponent', () => { 6 | let component: GraphComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async () => { 10 | await TestBed.configureTestingModule({ 11 | declarations: [ GraphComponent ] 12 | }) 13 | .compileComponents(); 14 | }); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(GraphComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /SOURCE/src/app/homepage/homepage.component.css: -------------------------------------------------------------------------------- 1 | /** 2 | 3 | @filename : homepage.component.css 4 | @author : Elian Yildirim 5 | @Last_Modified : 13.05.2021 6 | */ 7 | 8 | 9 | .card { 10 | height: 20%; 11 | transition: all .3s ease-in-out; 12 | background-color: white; 13 | } 14 | 15 | .card:hover { 16 | transform: scale(1.1); 17 | background-color: #d3d3d3; 18 | } 19 | 20 | .modal-backdrop { 21 | /* bug fix - no overlay */ 22 | display: block; 23 | } 24 | 25 | #mobilebar { 26 | display: none; 27 | } 28 | 29 | .modal { 30 | background-color: #333b3f; 31 | } 32 | 33 | @media only screen and (max-width: 800px) { 34 | #mobilebar { 35 | display: block; 36 | } 37 | #desktopbar { 38 | display: none; 39 | } 40 | } 41 | 42 | -------------------------------------------------------------------------------- /SOURCE/src/app/editor/editor.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { EditorComponent } from './editor.component'; 4 | 5 | describe('EditorComponent', () => { 6 | let component: EditorComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async () => { 10 | await TestBed.configureTestingModule({ 11 | declarations: [ EditorComponent ] 12 | }) 13 | .compileComponents(); 14 | }); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(EditorComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /SOURCE/src/app/footer/footer.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { FooterComponent } from './footer.component'; 4 | 5 | describe('FooterComponent', () => { 6 | let component: FooterComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async () => { 10 | await TestBed.configureTestingModule({ 11 | declarations: [ FooterComponent ] 12 | }) 13 | .compileComponents(); 14 | }); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(FooterComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /SOURCE/src/app/library/library.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { LibraryComponent } from './library.component'; 4 | 5 | describe('LibraryComponent', () => { 6 | let component: LibraryComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async () => { 10 | await TestBed.configureTestingModule({ 11 | declarations: [ LibraryComponent ] 12 | }) 13 | .compileComponents(); 14 | }); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(LibraryComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /SOURCE/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | OpenPLCEditor 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 17 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /SOURCE/.browserslistrc: -------------------------------------------------------------------------------- 1 | # This file is used by the build system to adjust CSS and JS output to support the specified browsers below. 2 | # For additional information regarding the format and rule options, please see: 3 | # https://github.com/browserslist/browserslist#queries 4 | 5 | # For the full list of supported browsers by the Angular framework, please see: 6 | # https://angular.io/guide/browser-support 7 | 8 | # You can see what browsers were selected by your queries by running: 9 | # npx browserslist 10 | 11 | last 1 Chrome version 12 | last 1 Firefox version 13 | last 2 Edge major versions 14 | last 2 Safari major versions 15 | last 2 iOS major versions 16 | Firefox ESR 17 | not IE 11 # Angular supports IE 11 only as an opt-in. To opt-in, remove the 'not' prefix on this line. 18 | -------------------------------------------------------------------------------- /SOURCE/e2e/src/app.e2e-spec.ts: -------------------------------------------------------------------------------- 1 | import { AppPage } from './app.po'; 2 | import { browser, logging } from 'protractor'; 3 | 4 | describe('workspace-project App', () => { 5 | let page: AppPage; 6 | 7 | beforeEach(() => { 8 | page = new AppPage(); 9 | }); 10 | 11 | it('should display welcome message', async () => { 12 | await page.navigateTo(); 13 | expect(await page.getTitleText()).toEqual('OpenPLC-Editor app is running!'); 14 | }); 15 | 16 | afterEach(async () => { 17 | // Assert that there are no errors emitted from the browser 18 | const logs = await browser.manage().logs().get(logging.Type.BROWSER); 19 | expect(logs).not.toContain(jasmine.objectContaining({ 20 | level: logging.Level.SEVERE, 21 | } as logging.Entry)); 22 | }); 23 | }); 24 | -------------------------------------------------------------------------------- /SOURCE/src/app/homepage/homepage.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { HomepageComponent } from './homepage.component'; 4 | 5 | describe('HomepageComponent', () => { 6 | let component: HomepageComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async () => { 10 | await TestBed.configureTestingModule({ 11 | declarations: [ HomepageComponent ] 12 | }) 13 | .compileComponents(); 14 | }); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(HomepageComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /SOURCE/src/app/editor/editor.component.html: -------------------------------------------------------------------------------- 1 | 6 | 11 | 12 |
13 |
14 |
15 | 16 |
17 |
18 | 19 |
20 |
21 |
22 |
23 | 24 |
25 |
26 |
-------------------------------------------------------------------------------- /SOURCE/src/app/variables-list/variables-list.component.css: -------------------------------------------------------------------------------- 1 | /* .variablesList { 2 | position: absolute; 3 | top: 0; 4 | left: 0; 5 | width: 78%; 6 | height: 20%; 7 | border: black solid 2px; 8 | padding-left: 10px; 9 | overflow: auto; 10 | margin: 5px; 11 | } 12 | table, th, td { 13 | border: 1px solid black; 14 | } 15 | table { 16 | border-collapse: collapse; 17 | width: 96%; 18 | margin: 10px 2%; 19 | } 20 | th { 21 | background-color: lavender; 22 | } 23 | .newVariable { 24 | margin: 10px 0 5px 2%; 25 | } 26 | input { 27 | border: none; 28 | } 29 | 30 | select { 31 | border: none; 32 | } 33 | */ 34 | 35 | .header { 36 | position: sticky; 37 | top:0; 38 | background-color: white; 39 | border-bottom: 1px solid gray; 40 | z-index: 100; 41 | } 42 | 43 | -------------------------------------------------------------------------------- /SOURCE/src/app/navigation/navigation.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { NavigationComponent } from './navigation.component'; 4 | 5 | describe('NavigationComponent', () => { 6 | let component: NavigationComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async () => { 10 | await TestBed.configureTestingModule({ 11 | declarations: [ NavigationComponent ] 12 | }) 13 | .compileComponents(); 14 | }); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(NavigationComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /SOURCE/src/app/user-manual/user-manual.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { UserManualComponent } from './user-manual.component'; 4 | 5 | describe('UserManualComponent', () => { 6 | let component: UserManualComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async () => { 10 | await TestBed.configureTestingModule({ 11 | declarations: [ UserManualComponent ] 12 | }) 13 | .compileComponents(); 14 | }); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(UserManualComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /SOURCE/src/app/variables-list/variables-list.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { VariablesListComponent } from './variables-list.component'; 4 | 5 | describe('VariablesListComponent', () => { 6 | let component: VariablesListComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async () => { 10 | await TestBed.configureTestingModule({ 11 | declarations: [ VariablesListComponent ] 12 | }) 13 | .compileComponents(); 14 | }); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(VariablesListComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /SOURCE/.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # compiled output 4 | /dist 5 | /tmp 6 | /out-tsc 7 | # Only exists if Bazel was run 8 | /bazel-out 9 | 10 | # dependencies 11 | /node_modules 12 | 13 | # profiling files 14 | chrome-profiler-events*.json 15 | speed-measure-plugin*.json 16 | 17 | # IDEs and editors 18 | /.idea 19 | .project 20 | .classpath 21 | .c9/ 22 | *.launch 23 | .settings/ 24 | *.sublime-workspace 25 | 26 | # IDE - VSCode 27 | .vscode/* 28 | !.vscode/settings.json 29 | !.vscode/tasks.json 30 | !.vscode/launch.json 31 | !.vscode/extensions.json 32 | .history/* 33 | 34 | # misc 35 | /.sass-cache 36 | /connect.lock 37 | /coverage 38 | /libpeerconnection.log 39 | npm-debug.log 40 | yarn-error.log 41 | testem.log 42 | /typings 43 | 44 | # System Files 45 | .DS_Store 46 | Thumbs.db 47 | -------------------------------------------------------------------------------- /SOURCE/src/app/project-overview/project-overview.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { ProjectOverviewComponent } from './project-overview.component'; 4 | 5 | describe('ProjectOverviewComponent', () => { 6 | let component: ProjectOverviewComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async () => { 10 | await TestBed.configureTestingModule({ 11 | declarations: [ ProjectOverviewComponent ] 12 | }) 13 | .compileComponents(); 14 | }); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(ProjectOverviewComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /SOURCE/src/test.ts: -------------------------------------------------------------------------------- 1 | // This file is required by karma.conf.js and loads recursively all the .spec and framework files 2 | 3 | import 'zone.js/dist/zone-testing'; 4 | import { getTestBed } from '@angular/core/testing'; 5 | import { 6 | BrowserDynamicTestingModule, 7 | platformBrowserDynamicTesting 8 | } from '@angular/platform-browser-dynamic/testing'; 9 | 10 | declare const require: { 11 | context(path: string, deep?: boolean, filter?: RegExp): { 12 | keys(): string[]; 13 | (id: string): T; 14 | }; 15 | }; 16 | 17 | // First, initialize the Angular testing environment. 18 | getTestBed().initTestEnvironment( 19 | BrowserDynamicTestingModule, 20 | platformBrowserDynamicTesting() 21 | ); 22 | // Then we find all the tests. 23 | const context = require.context('./', true, /\.spec\.ts$/); 24 | // And load the modules. 25 | context.keys().map(context); 26 | -------------------------------------------------------------------------------- /SOURCE/src/app/app-routing.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { Routes, RouterModule } from '@angular/router'; 3 | import { HomepageComponent} from './homepage/homepage.component'; 4 | import { EditorComponent } from './editor/editor.component'; 5 | import { ProjectOverviewComponent } from './project-overview/project-overview.component'; 6 | import { UserManualComponent } from './user-manual/user-manual.component'; 7 | import { AboutComponent } from './about/about.component'; 8 | 9 | const routes: Routes = [ 10 | {path: '', component: HomepageComponent}, 11 | {path: 'home', component: HomepageComponent}, 12 | {path: 'editor/:pouName', component: EditorComponent}, 13 | {path: 'projectOverview', component: ProjectOverviewComponent}, 14 | {path: 'userManual', component: UserManualComponent}, 15 | {path: 'about', component: AboutComponent } 16 | ]; 17 | 18 | @NgModule({ 19 | imports: [RouterModule.forRoot(routes)], 20 | exports: [RouterModule] 21 | }) 22 | export class AppRoutingModule { } 23 | -------------------------------------------------------------------------------- /SOURCE/e2e/protractor.conf.js: -------------------------------------------------------------------------------- 1 | // @ts-check 2 | // Protractor configuration file, see link for more information 3 | // https://github.com/angular/protractor/blob/master/lib/config.ts 4 | 5 | const { SpecReporter, StacktraceOption } = require('jasmine-spec-reporter'); 6 | 7 | /** 8 | * @type { import("protractor").Config } 9 | */ 10 | exports.config = { 11 | allScriptsTimeout: 11000, 12 | specs: [ 13 | './src/**/*.e2e-spec.ts' 14 | ], 15 | capabilities: { 16 | browserName: 'chrome' 17 | }, 18 | directConnect: true, 19 | SELENIUM_PROMISE_MANAGER: false, 20 | baseUrl: 'http://localhost:4200/', 21 | framework: 'jasmine', 22 | jasmineNodeOpts: { 23 | showColors: true, 24 | defaultTimeoutInterval: 30000, 25 | print: function() {} 26 | }, 27 | onPrepare() { 28 | require('ts-node').register({ 29 | project: require('path').join(__dirname, './tsconfig.json') 30 | }); 31 | jasmine.getEnv().addReporter(new SpecReporter({ 32 | spec: { 33 | displayStacktrace: StacktraceOption.PRETTY 34 | } 35 | })); 36 | } 37 | }; -------------------------------------------------------------------------------- /SOURCE/src/app/models/PLCNode.ts: -------------------------------------------------------------------------------- 1 | import { Node } from '@swimlane/ngx-graph'; 2 | 3 | export interface ConnectionPoint { 4 | type: string; 5 | sourcePoint?: string; 6 | targetPoint?: string; 7 | sourceId?: string; 8 | sourceName?: string; 9 | targetId?: string; 10 | targetName?: string; 11 | edgeId?: string; 12 | relPosition?: Vector2; 13 | connectionPath?: Vector2; 14 | } 15 | 16 | export interface Vector2{ 17 | x: number; 18 | y: number; 19 | } 20 | 21 | 22 | 23 | export interface PLCNode extends Node { 24 | type: string; 25 | connectionPoints: ConnectionPoint[]; 26 | } 27 | 28 | export interface ConnectionPointIn { 29 | sourcePoint?: string; 30 | targetPoint?: string; 31 | sourceId?: string; 32 | sourceName?: string; 33 | targetId?: string; 34 | targetName?: string; 35 | edgeId?: string; 36 | relPosition?: Vector2; 37 | } 38 | 39 | export interface ConnectionPointOut { 40 | sourcePoint?: string; 41 | sourceId?: string; 42 | sourceName?: string; 43 | edgeId?: string; 44 | relPosition?: Vector2; 45 | } -------------------------------------------------------------------------------- /SOURCE/README.md: -------------------------------------------------------------------------------- 1 | # OpenPLCEditor 2 | 3 | This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 11.0.2. 4 | 5 | ## Development server 6 | 7 | Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files. 8 | 9 | ## Code scaffolding 10 | 11 | Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`. 12 | 13 | ## Build 14 | 15 | Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. Use the `--prod` flag for a production build. 16 | 17 | ## Running unit tests 18 | 19 | Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io). 20 | 21 | ## Running end-to-end tests 22 | 23 | Run `ng e2e` to execute the end-to-end tests via [Protractor](http://www.protractortest.org/). 24 | 25 | ## Further help 26 | 27 | To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI Overview and Command Reference](https://angular.io/cli) page. 28 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | Copyright (c) 2020 Franziska Kopp, Leonie de Santis, Mouaz Tabboush, Elian Yildirim 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the 7 | "Software"), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /SOURCE/src/app/app.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed } from '@angular/core/testing'; 2 | import { RouterTestingModule } from '@angular/router/testing'; 3 | import { AppComponent } from './app.component'; 4 | 5 | describe('AppComponent', () => { 6 | beforeEach(async () => { 7 | await TestBed.configureTestingModule({ 8 | imports: [ 9 | RouterTestingModule 10 | ], 11 | declarations: [ 12 | AppComponent 13 | ], 14 | }).compileComponents(); 15 | }); 16 | 17 | it('should create the app', () => { 18 | const fixture = TestBed.createComponent(AppComponent); 19 | const app = fixture.componentInstance; 20 | expect(app).toBeTruthy(); 21 | }); 22 | 23 | it(`should have as title 'OpenPLC-Editor'`, () => { 24 | const fixture = TestBed.createComponent(AppComponent); 25 | const app = fixture.componentInstance; 26 | expect(app.title).toEqual('OpenPLC-Editor'); 27 | }); 28 | 29 | it('should render title', () => { 30 | const fixture = TestBed.createComponent(AppComponent); 31 | fixture.detectChanges(); 32 | const compiled = fixture.nativeElement; 33 | expect(compiled.querySelector('.content span').textContent).toContain('OpenPLC-Editor app is running!'); 34 | }); 35 | }); 36 | -------------------------------------------------------------------------------- /SOURCE/karma.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration file, see link for more information 2 | // https://karma-runner.github.io/1.0/config/configuration-file.html 3 | 4 | module.exports = function (config) { 5 | config.set({ 6 | basePath: '', 7 | frameworks: ['jasmine', '@angular-devkit/build-angular'], 8 | plugins: [ 9 | require('karma-jasmine'), 10 | require('karma-chrome-launcher'), 11 | require('karma-jasmine-html-reporter'), 12 | require('karma-coverage'), 13 | require('@angular-devkit/build-angular/plugins/karma') 14 | ], 15 | client: { 16 | clearContext: false // leave Jasmine Spec Runner output visible in browser 17 | }, 18 | jasmineHtmlReporter: { 19 | suppressAll: true // removes the duplicated traces 20 | }, 21 | coverageReporter: { 22 | dir: require('path').join(__dirname, './coverage/OpenPLC-Editor'), 23 | subdir: '.', 24 | reporters: [ 25 | { type: 'html' }, 26 | { type: 'text-summary' } 27 | ] 28 | }, 29 | reporters: ['progress', 'kjhtml'], 30 | port: 9876, 31 | colors: true, 32 | logLevel: config.LOG_INFO, 33 | autoWatch: true, 34 | browsers: ['Chrome'], 35 | singleRun: false, 36 | restartOnFileChange: true 37 | }); 38 | }; 39 | -------------------------------------------------------------------------------- /SOURCE/src/app/homepage/homepage.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import {ImportService} from '../services/import.service'; 3 | import {ProjectService} from '../services/project.service'; 4 | 5 | @Component({ 6 | selector: 'app-homepage', 7 | templateUrl: './homepage.component.html', 8 | styleUrls: ['./homepage.component.css'] 9 | }) 10 | export class HomepageComponent implements OnInit { 11 | public title = 'PLCopen-Editor'; 12 | 13 | constructor(private importService: ImportService, private projectService: ProjectService) { } 14 | 15 | ngOnInit(): void { 16 | } 17 | /* public openProjectModal(): void { 18 | // @ts-ignore 19 | document.getElementById('openProjectModal').style.display = 'block'; 20 | } 21 | 22 | public newProjectModal(): void { 23 | document.getElementById('newProjectModal').style.display = 'block'; 24 | } 25 | 26 | public closeNewProject(): void { 27 | document.getElementById('newProjectModal').style.display = 'none'; 28 | } */ 29 | 30 | public closeProjectModal(): void { 31 | // @ts-ignore 32 | document.getElementById('openProjectModal').style.display = 'none'; 33 | } 34 | 35 | fileUpload(event: Event): void { 36 | this.importService.fileUpload(event); 37 | } 38 | 39 | createProject(data: any): void { 40 | this.projectService.createNewProject(data.projectName); 41 | } 42 | 43 | 44 | } 45 | -------------------------------------------------------------------------------- /SOURCE/src/app/library/library.component.html: -------------------------------------------------------------------------------- 1 | 6 | 7 |
8 |

Library

9 | 10 | 14 | 15 |
16 |
17 | 18 | 19 | 30 | 33 | 34 |
20 |
21 | 22 | 23 | 24 | {{fb.getAttribute('name')}} 25 | 26 | 27 |
28 | 29 |
31 | 32 |
35 |
36 |
37 |
38 | 39 | -------------------------------------------------------------------------------- /SOURCE/src/app/navigation/navigation.component.html: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | 9 | 10 |
11 |
12 |
PLCOpen-Editor 1.0
13 | 17 |
18 |
19 | 20 | 21 | 26 | 27 |
28 | 35 |
36 | -------------------------------------------------------------------------------- /SOURCE/src/app/project-overview/project-overview.component.css: -------------------------------------------------------------------------------- 1 | .projectOverview { 2 | margin-left: 20px; 3 | } 4 | 5 | .actionButton { 6 | background-color: black; 7 | border: none; 8 | color: white; 9 | padding: 10px 20px; 10 | text-align: center; 11 | text-decoration: none; 12 | display: inline-block; 13 | margin: 8px 2px; 14 | cursor: pointer; 15 | border-radius: 16px; 16 | font-size: small; 17 | } 18 | 19 | .dropbtn { 20 | border: none; 21 | padding: 10px 20px; 22 | text-decoration: none; 23 | margin: 8px 2px; 24 | color: black; 25 | font-size: 18px; 26 | } 27 | 28 | /* .dropdown-content { 29 | display: none; 30 | } */ 31 | 32 | .dropdown:hover .dropdown-content { 33 | display: block; 34 | } 35 | 36 | .dropdown:hover .dropbtn { 37 | color: red; 38 | } 39 | 40 | .projectModal { 41 | display: none; /* Hidden by default */ 42 | position: fixed; /* Stay in place */ 43 | z-index: 1; /* Sit on top */ 44 | left: 0; 45 | top: 0; 46 | width: 100%; 47 | height: 100%; 48 | overflow: auto; 49 | background-color: rgb(0,0,0); 50 | background-color: rgba(0,0,0,0.4); 51 | } 52 | 53 | .modalBox { 54 | position: absolute; 55 | top: 20%; 56 | left: 50%; 57 | text-align: left; 58 | transform: translate(-50%,-50%); 59 | background-color: white; 60 | width: fit-content; 61 | height: fit-content; 62 | padding: 15px; 63 | } 64 | .form { 65 | padding: 10px 30px; 66 | } 67 | 68 | 69 | a:hover { 70 | color: gray; 71 | } 72 | 73 | .modal { 74 | background-color: #333b3f; 75 | } 76 | 77 | 78 | -------------------------------------------------------------------------------- /SOURCE/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "open-plc-editor", 3 | "version": "0.0.0", 4 | "scripts": { 5 | "ng": "ng", 6 | "start": "ng serve", 7 | "build": "ng build", 8 | "test": "ng test", 9 | "lint": "ng lint", 10 | "e2e": "ng e2e" 11 | }, 12 | "private": true, 13 | "dependencies": { 14 | "@angular/animations": "~11.0.1", 15 | "@angular/cdk": "^11.2.12", 16 | "@angular/common": "~11.0.1", 17 | "@angular/compiler": "~11.0.1", 18 | "@angular/core": "~11.0.1", 19 | "@angular/forms": "~11.0.1", 20 | "@angular/platform-browser": "~11.0.1", 21 | "@angular/platform-browser-dynamic": "~11.0.1", 22 | "@angular/router": "~11.0.1", 23 | "@swimlane/ngx-graph": "^7.2.0", 24 | "bootstrap": "^4.6.0", 25 | "file-saver": "^2.0.5", 26 | "readable-stream": "^3.6.0", 27 | "rxjs": "^6.6.7", 28 | "timers": "^0.1.1", 29 | "tslib": "^2.2.0", 30 | "zone.js": "~0.10.2" 31 | }, 32 | "devDependencies": { 33 | "@angular-devkit/build-angular": "^0.1100.7", 34 | "@angular/cli": "~11.0.2", 35 | "@angular/compiler-cli": "~11.0.1", 36 | "@types/jasmine": "^3.6.11", 37 | "@types/node": "^12.20.12", 38 | "codelyzer": "^6.0.2", 39 | "jasmine-core": "~3.6.0", 40 | "jasmine-spec-reporter": "~5.0.0", 41 | "karma": "~5.1.0", 42 | "karma-chrome-launcher": "~3.1.0", 43 | "karma-coverage": "~2.0.3", 44 | "karma-jasmine": "~4.0.0", 45 | "karma-jasmine-html-reporter": "^1.6.0", 46 | "protractor": "~7.0.0", 47 | "ts-node": "~8.3.0", 48 | "tslint": "~6.1.0", 49 | "typescript": "^4.0.7" 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /SOURCE/src/app/about/about.component.html: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | 9 |
10 | 11 |

About PLCOpen-Editor

12 | 13 |

What is PLC?

14 |

PLC stands for Programable logic controllers. 15 | A simple way to understand it would be to think of PLC as a small computer that performs a list of instructions. 16 | The list of instructions contains logic that helps determine which course of action to take on a given set of circumstances. 17 | PLC is used in alot of areas inside and outside the industrial work. Usually where automation of tasks is the goal. 18 | The PLC editor itself helps user create programs without the need to know the syntax of the used programming lanugage for the PLC. All what the user needs to do is to determine the logic for the controller. 19 | The OpenPLC Reference page has a nice tutorial to follow along and learn about PLC

20 | 21 |

Why are we doing the project?

22 |

We want to develop a web version of the OpenPLC Editor to allow users to work on their programs remotely without requiring installation. 23 | You can find more information about the project by visiting the wiki and reading the CRS, SRS and SAS documents.

24 | 25 |

What technologies are we using to make the application?

26 |

The Application runs is written in Angular 8 and runs solely as a front end application. There is no backedend envolved

27 |
28 | -------------------------------------------------------------------------------- /SOURCE/src/app/app.module.ts: -------------------------------------------------------------------------------- 1 | import { BrowserModule } from '@angular/platform-browser'; 2 | import { NgModule } from '@angular/core'; 3 | 4 | import { AppRoutingModule } from './app-routing.module'; 5 | import { AppComponent } from './app.component'; 6 | import { HomepageComponent } from './homepage/homepage.component'; 7 | import { EditorComponent } from './editor/editor.component'; 8 | import { VariablesListComponent } from './variables-list/variables-list.component'; 9 | import { FormsModule} from '@angular/forms'; 10 | import { ProjectOverviewComponent } from './project-overview/project-overview.component'; 11 | import { UserManualComponent } from './user-manual/user-manual.component'; 12 | import { LibraryComponent } from './library/library.component'; 13 | import { GraphComponent } from './graph/graph.component'; 14 | import {NgxGraphModule} from '@swimlane/ngx-graph'; 15 | import { NavigationComponent } from './navigation/navigation.component'; 16 | import { FooterComponent } from './footer/footer.component'; 17 | import { HttpClientModule} from '@angular/common/http'; 18 | import { AboutComponent } from './about/about.component'; 19 | 20 | @NgModule({ 21 | declarations: [ 22 | AppComponent, 23 | HomepageComponent, 24 | EditorComponent, 25 | VariablesListComponent, 26 | ProjectOverviewComponent, 27 | UserManualComponent, 28 | LibraryComponent, 29 | GraphComponent, 30 | NavigationComponent, 31 | FooterComponent, 32 | AboutComponent, 33 | ], 34 | imports: [ 35 | NgxGraphModule, 36 | BrowserModule, 37 | AppRoutingModule, 38 | FormsModule, 39 | HttpClientModule 40 | ], 41 | providers: [], 42 | bootstrap: [AppComponent] 43 | }) 44 | export class AppModule { } 45 | -------------------------------------------------------------------------------- /SOURCE/src/app/library/library.component.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @Filename : library.service.ts 3 | * 4 | * @Author : Leonie de Santis 5 | * 6 | * @Last_Modified : 13.05.2021 7 | * 8 | */ 9 | 10 | import { Component, OnInit } from '@angular/core'; 11 | import { HttpClient, HttpHeaders} from '@angular/common/http'; 12 | import { EditorService} from '../services/editor.service'; 13 | 14 | @Component({ 15 | selector: 'app-library', 16 | templateUrl: './library.component.html', 17 | styleUrls: ['./library.component.css'] 18 | }) 19 | export class LibraryComponent implements OnInit { 20 | public functionBlocks: any[] = []; 21 | 22 | constructor(private http: HttpClient, private editorService: EditorService) { 23 | } 24 | 25 | ngOnInit(): void { 26 | this.loadFunctionBlocks('Standard_Function_Blocks.xml'); 27 | } 28 | 29 | // load the xml File of the according function blocks 30 | // add the function blocks of the xml to the list of function blocks 31 | // to show them in the library 32 | loadFunctionBlocks(blockUrl: string): void { 33 | this.functionBlocks = []; 34 | const httpHeaders = new HttpHeaders({ 'Content-Type': 'text/xml' }); 35 | httpHeaders.append('Accept', 'text/xml'); 36 | httpHeaders.append('Content-Type', 'text/xml'); 37 | this.http.get('../assets/xmlResources/' + blockUrl, 38 | {headers: httpHeaders, responseType: 'text'}).subscribe((data) => { 39 | const parser = new DOMParser(); 40 | const dom = parser.parseFromString(data, 'application/xml'); 41 | const i = dom.documentElement.getElementsByTagName('pous')[0].childElementCount; 42 | for (let j = 0; j < i; j++){ 43 | const pou = dom.documentElement.getElementsByTagName('pou')[j]; 44 | this.functionBlocks.push(pou); 45 | } 46 | }); 47 | } 48 | 49 | // addFB -> the editorService create a new function block 50 | addFB(fbXml: any): void{ 51 | this.editorService.addFB(fbXml); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /SOURCE/src/app/project-overview/project-overview.component.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @Filename : project-overview.component.ts 3 | * 4 | * @Author : Leonie de Santis 5 | * 6 | * @Last_Modified : 13.05.2021 7 | * 8 | */ 9 | 10 | import { Component, OnInit } from '@angular/core'; 11 | import {ProjectService} from '../services/project.service'; 12 | import {ImportService} from '../services/import.service'; 13 | 14 | @Component({ 15 | selector: 'app-project-overview', 16 | templateUrl: './project-overview.component.html', 17 | styleUrls: ['./project-overview.component.css'] 18 | }) 19 | export class ProjectOverviewComponent implements OnInit { 20 | public pous: string[] = []; 21 | public projectName = ''; 22 | 23 | constructor(private projectService: ProjectService, private importService: ImportService) { 24 | } 25 | 26 | // load the project name and the list of pous from the projectService 27 | ngOnInit(): void { 28 | this.pous = this.projectService.getPouName(); 29 | this.projectName = this.projectService.getProjectName(); 30 | } 31 | 32 | // load the list of pous from the projectService 33 | loadPous(): void { 34 | this.pous = this.projectService.getPouName(); 35 | } 36 | 37 | closePouModal(): void { 38 | document.getElementById('addPouModal').style.display = 'none'; 39 | } 40 | 41 | // create a new pou and add to the pou list 42 | addPou(data: any): void { 43 | this.projectService.addPou(data.name, data.type, data.lang); 44 | this.loadPous(); 45 | this.closePouModal(); 46 | } 47 | 48 | // remove the selected pou and reload the pou list 49 | deletePou(deleteItem: string): void { 50 | this.projectService.deletePou(deleteItem); 51 | this.loadPous(); 52 | } 53 | 54 | // export the project 55 | exportProject(): void { 56 | this.projectService.exportProject(); 57 | } 58 | 59 | // upload another file 60 | fileUpload(event: Event): void { 61 | this.importService.fileUpload(event); 62 | } 63 | 64 | openProject(): void { 65 | this.loadPous(); 66 | } 67 | 68 | } 69 | -------------------------------------------------------------------------------- /SOURCE/src/app/services/import.service.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @Filename : import.service.ts 3 | * 4 | * @Author : Leonie de Santis 5 | * 6 | * @Last_Modified : 13.05.2021 7 | * 8 | */ 9 | 10 | import { Injectable } from '@angular/core'; 11 | import {ProjectService} from './project.service'; 12 | import {Router} from '@angular/router'; 13 | 14 | @Injectable({ 15 | providedIn: 'root' 16 | }) 17 | 18 | export class ImportService { 19 | xmlFile: any; 20 | 21 | 22 | 23 | constructor(private projectService: ProjectService, private router: Router) { 24 | 25 | } 26 | 27 | // check if a file was uploaded and read the uploaded file as a string 28 | fileUpload(event: Event): void { 29 | // @ts-ignore 30 | if (event.target.files[0] !== null) { 31 | // @ts-ignore 32 | const file = event.target.files[0]; 33 | if (!file) { 34 | return; 35 | } 36 | const reader = new FileReader(); 37 | reader.onload = (evt) => { 38 | this.xmlFile = (evt as any).target.result; 39 | this.uploadProject(); 40 | }; 41 | reader.readAsText(file); 42 | } 43 | } 44 | 45 | // Tries to read the information of the xml-file and saves 46 | // the different parts (header, pous and instances) into the projectService. 47 | // If there is an invalid xml-file an error message is shown on the display 48 | uploadProject(): void { 49 | try { 50 | this.projectService.headerItems = []; 51 | this.projectService.pouItems = []; 52 | const parser = new DOMParser(); 53 | const dom = parser.parseFromString(this.xmlFile, 'application/xml'); 54 | const i = dom.documentElement.getElementsByTagName('pous')[0].childElementCount; 55 | for (let j = 0; j < i; j++){ 56 | this.projectService.pouItems.push(dom.documentElement.getElementsByTagName('pou')[j]); 57 | } 58 | this.projectService.headerItems.push(dom.documentElement.getElementsByTagName('fileHeader')[0]); 59 | this.projectService.headerItems.push(dom.documentElement.getElementsByTagName('contentHeader')[0]); 60 | this.projectService.instanceItems = dom.documentElement.getElementsByTagName('instances')[0]; 61 | } catch (e) { 62 | alert('The uploaded file is empty or corrupted! \n Please upload another file.'); 63 | if (this.router.url === '/') { 64 | this.router.navigateByUrl('home'); 65 | } 66 | else if (this.router.url === '/home') { 67 | this.router.navigateByUrl(''); 68 | } 69 | else if (this.router.url === '/projectOverview') { 70 | this.router.navigateByUrl('home'); 71 | } 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /SOURCE/src/app/models/fbdObjects/fbdLabel.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @Filename : fbdLabel.ts 3 | * 4 | * @Author : Leonie de Santis 5 | * 6 | * @Last_Modified : 13.05.2021 7 | * 8 | */ 9 | 10 | import { PLCNode } from '../PLCNode'; 11 | 12 | export class FbdLabel { 13 | public xml: any; 14 | public localId: string; 15 | public height = 20; 16 | public width = 20; 17 | public label = ''; 18 | public position: {x: number, y: number} = {x: 0, y: 0}; 19 | public node: PLCNode = {id: null, label: null, type: null, connectionPoints: null}; 20 | public edges: string[] = []; 21 | 22 | // check if imported xml ist empty, then create xml, otherwise reads relevant values of xml- file 23 | constructor(xmlLabel: any) { 24 | if (xmlLabel === '') { 25 | this.createNewLabel(); 26 | } 27 | else { 28 | this.xml = xmlLabel; 29 | this.localId = xmlLabel.getAttribute('localId'); 30 | if (xmlLabel.getAttribute('width') !== undefined) { 31 | this.width = xmlLabel.getAttribute('width'); 32 | } 33 | if (xmlLabel.getAttribute('height') !== undefined) { 34 | this.height = xmlLabel.getAttribute('height'); 35 | } 36 | if (xmlLabel.getAttribute('label') !== undefined) { 37 | this.label = xmlLabel.getAttribute('label'); 38 | } 39 | if (xmlLabel.getElementsByTagName('position')[0] !== undefined) { 40 | const position = xmlLabel.getElementsByTagName('position')[0]; 41 | this.position = { x: position.getAttribute('x'), y: position.getAttribute('y')}; 42 | } 43 | } 44 | 45 | // values that are relevant for illustration are written into nodes 46 | this.node.id = this.localId; 47 | this.node.label = this.label; 48 | this.node.type = 'label'; 49 | } 50 | 51 | // creates a default xml-file for the object 52 | createNewLabel(): void { 53 | const xmlString = ' '; 56 | const parser = new DOMParser(); 57 | this.xml = parser.parseFromString(xmlString, 'application/xml'); 58 | this.xml = this.xml.getElementsByTagName('label')[0]; 59 | } 60 | 61 | // updates attributes of position 62 | updatePosition(xPos: number, yPos: number): void { 63 | this.xml.getElementsByTagName('position')[0].setAttribute('x', xPos); 64 | this.xml.getElementsByTagName('position')[0].setAttribute('y', yPos); 65 | } 66 | 67 | // updates relevant attributes 68 | updateAttributes(localId: number, label: string): void{ 69 | this.xml.setAttribute('localId', localId); 70 | this.xml.setAttribute('label', label); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /SOURCE/src/assets/corruptedXML.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | InVar 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | InOutVar 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | OutVar 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /SOURCE/src/assets/plc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | InVar 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | InOutVar 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | OutVar 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /SOURCE/src/app/models/commonObjects/commonError.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @Filename : commonError.ts 3 | * 4 | * @Author : Franziska Kopp 5 | * 6 | * @Last_Modified : 19.05.2021 7 | * 8 | */ 9 | 10 | import {Node} from '@swimlane/ngx-graph'; 11 | import { PLCNode } from '../PLCNode'; 12 | 13 | export class CommonError{ 14 | public xml: any; 15 | public localId: string; 16 | public height = 20; 17 | public width = 20; 18 | public content = ''; 19 | public position: {x: 0, y: 0}; 20 | public node: PLCNode = {id: null, label: null, type: null, connectionPoints: null}; 21 | 22 | // check if imported xml ist empty, then create xml, otherwise reads relevant values of xml- file 23 | constructor(xmlCommonError: any) { 24 | if (xmlCommonError === '') { 25 | this.createXML(); 26 | } else { 27 | this.xml = xmlCommonError; 28 | if (xmlCommonError.getAttribute('localId') !== undefined) { 29 | this.localId = xmlCommonError.getAttribute('localId'); 30 | } 31 | if (xmlCommonError.getAttribute('height') !== undefined) { 32 | this.height = xmlCommonError.getAttribute('height'); 33 | } 34 | if (xmlCommonError.getAttribute('width') !== undefined) { 35 | this.width = xmlCommonError.getAttribute('width'); 36 | } 37 | if (xmlCommonError.getAttribute('content') !== undefined) { 38 | this.content = xmlCommonError.getElementsByTagName('content')[0].children[0].innerText; 39 | } 40 | if (xmlCommonError.getElementsByTagName('position') !== undefined) { 41 | const position = xmlCommonError.getElementsByTagName('position')[0]; 42 | this.position = {x: position.getAttribute('x'), y: position.getAttribute('y')}; 43 | } 44 | } 45 | // values that are relevant for illustration are written into nodes 46 | this.node.id = this.localId; 47 | this.node.type = 'default'; 48 | } 49 | // creates a default xml-file for the object 50 | createXML(): void{ 51 | const xmlString = '\n' + 52 | '\n' + 53 | ' \n' + 54 | '\n\n' + 55 | '\n' + 56 | ' \n'; 57 | const parser = new DOMParser(); 58 | this.xml = parser.parseFromString(xmlString, 'application/xml'); 59 | const temp = this.xml.getElementsByTagName('parsererror')[0]; 60 | temp.parentNode.removeChild(temp); 61 | this.xml = this.xml.getElementsByTagName('error')[0]; 62 | } 63 | // updates attributes of position 64 | updatePosition(xPos: number, yPos: number): void { 65 | this.xml.getElementsByTagName('position')[0].setAttribute('x', xPos); 66 | this.xml.getElementsByTagName('position')[0].setAttribute('y', yPos); 67 | } 68 | // updates relevant attributes 69 | updateAttributes(localId: number): void{ 70 | this.xml.setAttribute('localId', localId); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /SOURCE/src/app/variables-list/variables-list.component.html: -------------------------------------------------------------------------------- 1 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 27 | 32 | 44 | 51 | 56 | 61 | 70 | 75 | 76 | 77 | 78 |
Del.NameClassTypeIEC-AddressInitial valueOptionDocumentation
25 | 26 | 28 | 31 | 33 | 43 | 45 | 50 | 52 | 55 | 57 | 60 | 62 | 69 | 71 | 74 |
79 | 80 |
81 | -------------------------------------------------------------------------------- /SOURCE/src/app/models/commonObjects/commonComment.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @Filename : commonComment.ts 3 | * 4 | * @Author : Franziska Kopp 5 | * 6 | * @Last_Modified : 19.05.2021 7 | * 8 | */ 9 | 10 | import {Node} from '@swimlane/ngx-graph'; 11 | import { PLCNode } from '../PLCNode'; 12 | 13 | export class CommonComment{ 14 | public xml: any; 15 | public localId: string; 16 | public height = '20'; 17 | public width = '20'; 18 | public content = ''; 19 | public position: {x: number, y: number} = {x: 0, y: 0}; 20 | public node: PLCNode = {id: null, label: null, type: null, connectionPoints: null}; 21 | 22 | // check if imported xml ist empty, then create xml, otherwise reads relevant values of xml- file 23 | constructor(xmlCommonComment: any) { 24 | if (xmlCommonComment === ''){ 25 | this.createXML(); 26 | } else { 27 | this.xml = xmlCommonComment; 28 | if (xmlCommonComment.getAttribute('localId') !== undefined){ 29 | this.localId = xmlCommonComment.getAttribute('localId'); 30 | } 31 | if (xmlCommonComment.getAttribute('height') !== undefined){ 32 | this.height = xmlCommonComment.getAttribute('height'); 33 | } 34 | if (xmlCommonComment.getAttribute('width') !== undefined){ 35 | this.width = xmlCommonComment.getAttribute('width'); 36 | } 37 | if (xmlCommonComment.getAttribute('content') !== undefined){ 38 | this.content = xmlCommonComment.getElementsByTagName('content')[0].children[0].innerText; 39 | } 40 | if (xmlCommonComment.getElementsByTagName('position') !== undefined) { 41 | const position = xmlCommonComment.getElementsByTagName('position')[0]; 42 | this.position = {x: position.getAttribute('x'), y: position.getAttribute('y')}; 43 | } 44 | } 45 | // values that are relevant for illustration are written into nodes 46 | this.node.id = this.localId; 47 | this.node.label = this.content; 48 | this.node.type = 'var'; 49 | 50 | } 51 | 52 | // creates a default xml-file for the object 53 | createXML(): void{ 54 | const xmlString = '\n' + 55 | ' \n' + 56 | ' \n' + 57 | '\n\n' + 58 | '\n' + 59 | ' \n'; 60 | const parser = new DOMParser(); 61 | this.xml = parser.parseFromString(xmlString, 'application/xml'); 62 | const temp = this.xml.getElementsByTagName('parsererror')[0]; 63 | temp.parentNode.removeChild(temp); 64 | this.xml = this.xml.getElementsByTagName('comment')[0]; 65 | } 66 | // updates attributes of position 67 | updatePosition(xPos: number, yPos: number): void { 68 | this.xml.getElementsByTagName('position')[0].setAttribute('x', xPos); 69 | this.xml.getElementsByTagName('position')[0].setAttribute('y', yPos); 70 | } 71 | // updates relevant attributes 72 | updateAttributes(localId: number): void{ 73 | this.xml.setAttribute('localId', localId); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /SOURCE/src/polyfills.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * This file includes polyfills needed by Angular and is loaded before the app. 3 | * You can add your own extra polyfills to this file. 4 | * 5 | * This file is divided into 2 sections: 6 | * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers. 7 | * 2. Application imports. Files imported after ZoneJS that should be loaded before your main 8 | * file. 9 | * 10 | * The current setup is for so-called "evergreen" browsers; the last versions of browsers that 11 | * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera), 12 | * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile. 13 | * 14 | * Learn more in https://angular.io/guide/browser-support 15 | */ 16 | 17 | /*************************************************************************************************** 18 | * BROWSER POLYFILLS 19 | */ 20 | 21 | /** IE11 requires the following for NgClass support on SVG elements */ 22 | // import 'classlist.js'; // Run `npm install --save classlist.js`. 23 | 24 | /** 25 | * Web Animations `@angular/platform-browser/animations` 26 | * Only required if AnimationBuilder is used within the application and using IE/Edge or Safari. 27 | * Standard animation support in Angular DOES NOT require any polyfills (as of Angular 6.0). 28 | */ 29 | // import 'web-animations-js'; // Run `npm install --save web-animations-js`. 30 | 31 | /** 32 | * By default, zone.js will patch all possible macroTask and DomEvents 33 | * user can disable parts of macroTask/DomEvents patch by setting following flags 34 | * because those flags need to be set before `zone.js` being loaded, and webpack 35 | * will put import in the top of bundle, so user need to create a separate file 36 | * in this directory (for example: zone-flags.ts), and put the following flags 37 | * into that file, and then add the following code before importing zone.js. 38 | * import './zone-flags'; 39 | * 40 | * The flags allowed in zone-flags.ts are listed here. 41 | * 42 | * The following flags will work for all browsers. 43 | * 44 | * (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame 45 | * (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick 46 | * (window as any).__zone_symbol__UNPATCHED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames 47 | * 48 | * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js 49 | * with the following flag, it will bypass `zone.js` patch for IE/Edge 50 | * 51 | * (window as any).__Zone_enable_cross_context_check = true; 52 | * 53 | */ 54 | 55 | /*************************************************************************************************** 56 | * Zone JS is required by default for Angular itself. 57 | */ 58 | import 'zone.js/dist/zone'; // Included with Angular CLI. 59 | 60 | 61 | /*************************************************************************************************** 62 | * APPLICATION IMPORTS 63 | */ 64 | -------------------------------------------------------------------------------- /SOURCE/src/assets/plc_valid.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | InputVariable 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | Input/OutputVariable 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | OutputVariable 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | -------------------------------------------------------------------------------- /SOURCE/src/app/models/ldObjects/ldLeftPowerRail.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @Filename : ldLeftPowerRail.ts 3 | * 4 | * @Author : Leonie de Santis 5 | * 6 | * @Last_Modified : 13.05.2021 7 | * 8 | */ 9 | import { ConnectionPoint, PLCNode } from '../PLCNode'; 10 | 11 | 12 | export class LdLeftPowerRail { 13 | public xml: any; 14 | public localId: string; 15 | public height = 20; 16 | public width = 20; 17 | public position: { x: number, y: number } = {x: 0, y: 0}; 18 | public connectionPointOut: {x: number, y: number, refLocalID: string} = {x: 0, y: 0, refLocalID: null}; 19 | public node: PLCNode = {id: null, label: null, type: null, connectionPoints: null}; 20 | public edges: string[] = []; 21 | 22 | // check if imported xml ist empty, then create xml, otherwise reads relevant values of xml- file 23 | constructor(xmlLeftPowerRail: any) { 24 | if (xmlLeftPowerRail === '') { 25 | this.createNewLeftPowerRail(); 26 | } 27 | else { 28 | this.xml = xmlLeftPowerRail; 29 | this.localId = xmlLeftPowerRail.getAttribute('localId'); 30 | if (xmlLeftPowerRail.getAttribute('width') !== undefined) { 31 | this.width = xmlLeftPowerRail.getAttribute('width'); 32 | } 33 | if (xmlLeftPowerRail.getAttribute('height') !== undefined) { 34 | this.height = xmlLeftPowerRail.getAttribute('height'); 35 | } 36 | if (xmlLeftPowerRail.getElementsByTagName('position')[0] !== undefined) { 37 | const position = xmlLeftPowerRail.getElementsByTagName('position')[0]; 38 | this.position = { x: position.getAttribute('x'), y: position.getAttribute('y')}; 39 | } 40 | if ( xmlLeftPowerRail.getElementsByTagName('connectionPointOut')[0] !== undefined) { 41 | const connectionPointOut = xmlLeftPowerRail.getElementsByTagName('connectionPointOut')[0]; 42 | if (connectionPointOut.getElementsByTagName('relPosition') !== undefined) { 43 | const position = connectionPointOut.getElementsByTagName('relPosition')[0]; 44 | this.connectionPointOut.x = position.getAttribute('x'); 45 | this.connectionPointOut.y = position.getAttribute('y'); 46 | } 47 | if (connectionPointOut.getElementsByTagName('connection')[0] !== undefined) { 48 | const connection = connectionPointOut.getElementsByTagName('connection')[0]; 49 | this.connectionPointOut.refLocalID = connection.getAttribute('refLocalId'); 50 | } 51 | } 52 | } 53 | 54 | // values that are relevant for illustration are written into nodes 55 | this.node.id = this.localId; 56 | this.node.type = 'LPR'; 57 | const newConnectionPointOut: ConnectionPoint = { 58 | type: 'OUT', 59 | sourceId: this.localId, 60 | targetId: this.connectionPointOut.refLocalID, 61 | edgeId: null, 62 | }; 63 | this.node.connectionPoints.push(newConnectionPointOut); 64 | 65 | } 66 | 67 | // creates a default xml-file for the object 68 | createNewLeftPowerRail(): void { 69 | const xmlString = ' \n' + 70 | ' \n' + 71 | ' \n' + 72 | ' \n' + 73 | ' \n' + 74 | ' '; 75 | const parser = new DOMParser(); 76 | this.xml = parser.parseFromString(xmlString, 'application/xml'); 77 | this.xml = this.xml.getElementsByTagName('leftPowerRail')[0]; 78 | } 79 | 80 | // updates attributes of position 81 | updatePosition(xPos: number, yPos: number): void { 82 | this.xml.getElementsByTagName('position')[0].setAttribute('x', xPos); 83 | this.xml.getElementsByTagName('position')[0].setAttribute('y', yPos); 84 | } 85 | 86 | // updates relevant attributes 87 | updateAttributes(localId: number): void{ 88 | this.xml.setAttribute('localId', localId); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /SOURCE/src/app/models/fbdObjects/fbdReturn.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @Filename : fbdReturn.ts 3 | * 4 | * @Author : Leonie de Santis 5 | * 6 | * @Last_Modified : 13.05.2021 7 | * 8 | */ 9 | import { ConnectionPoint, PLCNode } from '../PLCNode'; 10 | 11 | 12 | export class FbdReturn { 13 | public xml: any; 14 | public localId: string; 15 | public height = 20; 16 | public width = 20; 17 | public position: {x: number, y: number}; 18 | public connectionPointIn: { x: number, y: number, refLocalID: string} = {x: 0, y: 0, refLocalID: null}; 19 | public node: PLCNode = {id: null, label: null, type: null, connectionPoints: null}; 20 | public edges: string[] = []; 21 | 22 | // check if imported xml ist empty, then create xml, otherwise reads relevant values of xml- file 23 | constructor(xmlReturn: any) { 24 | if (xmlReturn === '') { 25 | this.createNewReturn(); 26 | } 27 | else { 28 | this.xml = xmlReturn; 29 | this.localId = xmlReturn.getAttribute('localId'); 30 | if (xmlReturn.getAttribute('width') !== undefined) { 31 | this.width = xmlReturn.getAttribute('width'); 32 | } 33 | if (xmlReturn.getAttribute('height') !== undefined) { 34 | this.height = xmlReturn.getAttribute('height'); 35 | } 36 | if (xmlReturn.getElementsByTagName('position')[0] !== undefined) { 37 | const position = xmlReturn.getElementsByTagName('position')[0]; 38 | this.position = { x: position.getAttribute('x'), y: position.getAttribute('y')}; 39 | } 40 | if ( xmlReturn.getElementsByTagName('connectionPointIn')[0] !== undefined) { 41 | const connectionPointIn = xmlReturn.getElementsByTagName('connectionPointIn')[0]; 42 | if (connectionPointIn.getElementsByTagName('relPosition') !== undefined) { 43 | const position = connectionPointIn.getElementsByTagName('relPosition')[0]; 44 | this.connectionPointIn.x = position.getAttribute('x'); 45 | this.connectionPointIn.y = position.getAttribute('y'); 46 | } 47 | if (connectionPointIn.getElementsByTagName('connection')[0] !== undefined) { 48 | const connection = connectionPointIn.getElementsByTagName('connection')[0]; 49 | this.connectionPointIn.refLocalID = connection.getAttribute('refLocalId'); 50 | } 51 | } 52 | } 53 | 54 | // values that are relevant for illustration are written into nodes 55 | this.node.id = this.localId; 56 | this.node.type = 'return'; 57 | const newConnectionPointIn: ConnectionPoint = { 58 | type: 'IN', 59 | sourceId: this.connectionPointIn.refLocalID, 60 | targetId: this.localId, 61 | edgeId: null 62 | }; 63 | this.node.connectionPoints.push(newConnectionPointIn); 64 | } 65 | 66 | // creates a default xml-file for the object 67 | createNewReturn(): void { 68 | const xmlString = ' \n' + 69 | ' \n' + 70 | ' \n' + 71 | ' \n' + 72 | ' \n' + 73 | ' '; 74 | const parser = new DOMParser(); 75 | this.xml = parser.parseFromString(xmlString, 'application/xml'); 76 | this.xml = this.xml.getElementsByTagName('return')[0]; 77 | } 78 | 79 | // updates attributes of position 80 | updatePosition(xPos: number, yPos: number): void { 81 | this.xml.getElementsByTagName('position')[0].setAttribute('x', xPos); 82 | this.xml.getElementsByTagName('position')[0].setAttribute('y', yPos); 83 | } 84 | 85 | // updates relevant attributes 86 | updateAttributes(localId: number): void{ 87 | this.xml.setAttribute('localId', localId); 88 | } 89 | 90 | // updates refId of ConnectionPointIn 91 | change_refid(newRef): void { 92 | this.xml.getElementsByTagName('connectionPointIn')[0].getElementsByTagName('connection')[0].setAttribute('refLocalId', newRef); 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /SOURCE/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "tslint:recommended", 3 | "rulesDirectory": [ 4 | "codelyzer" 5 | ], 6 | "rules": { 7 | "align": { 8 | "options": [ 9 | "parameters", 10 | "statements" 11 | ] 12 | }, 13 | "array-type": false, 14 | "arrow-return-shorthand": true, 15 | "curly": true, 16 | "deprecation": { 17 | "severity": "warning" 18 | }, 19 | "eofline": true, 20 | "import-blacklist": [ 21 | true, 22 | "rxjs/Rx" 23 | ], 24 | "import-spacing": true, 25 | "indent": { 26 | "options": [ 27 | "spaces" 28 | ] 29 | }, 30 | "max-classes-per-file": false, 31 | "max-line-length": [ 32 | true, 33 | 140 34 | ], 35 | "member-ordering": [ 36 | true, 37 | { 38 | "order": [ 39 | "static-field", 40 | "instance-field", 41 | "static-method", 42 | "instance-method" 43 | ] 44 | } 45 | ], 46 | "no-console": [ 47 | true, 48 | "debug", 49 | "info", 50 | "time", 51 | "timeEnd", 52 | "trace" 53 | ], 54 | "no-empty": false, 55 | "no-inferrable-types": [ 56 | true, 57 | "ignore-params" 58 | ], 59 | "no-non-null-assertion": true, 60 | "no-redundant-jsdoc": true, 61 | "no-switch-case-fall-through": true, 62 | "no-var-requires": false, 63 | "object-literal-key-quotes": [ 64 | true, 65 | "as-needed" 66 | ], 67 | "quotemark": [ 68 | true, 69 | "single" 70 | ], 71 | "semicolon": { 72 | "options": [ 73 | "always" 74 | ] 75 | }, 76 | "space-before-function-paren": { 77 | "options": { 78 | "anonymous": "never", 79 | "asyncArrow": "always", 80 | "constructor": "never", 81 | "method": "never", 82 | "named": "never" 83 | } 84 | }, 85 | "typedef": [ 86 | true, 87 | "call-signature" 88 | ], 89 | "typedef-whitespace": { 90 | "options": [ 91 | { 92 | "call-signature": "nospace", 93 | "index-signature": "nospace", 94 | "parameter": "nospace", 95 | "property-declaration": "nospace", 96 | "variable-declaration": "nospace" 97 | }, 98 | { 99 | "call-signature": "onespace", 100 | "index-signature": "onespace", 101 | "parameter": "onespace", 102 | "property-declaration": "onespace", 103 | "variable-declaration": "onespace" 104 | } 105 | ] 106 | }, 107 | "variable-name": { 108 | "options": [ 109 | "ban-keywords", 110 | "check-format", 111 | "allow-pascal-case" 112 | ] 113 | }, 114 | "whitespace": { 115 | "options": [ 116 | "check-branch", 117 | "check-decl", 118 | "check-operator", 119 | "check-separator", 120 | "check-type", 121 | "check-typecast" 122 | ] 123 | }, 124 | "component-class-suffix": true, 125 | "contextual-lifecycle": true, 126 | "directive-class-suffix": true, 127 | "no-conflicting-lifecycle": true, 128 | "no-host-metadata-property": true, 129 | "no-input-rename": true, 130 | "no-inputs-metadata-property": true, 131 | "no-output-native": true, 132 | "no-output-on-prefix": true, 133 | "no-output-rename": true, 134 | "no-outputs-metadata-property": true, 135 | "template-banana-in-box": true, 136 | "template-no-negated-async": true, 137 | "use-lifecycle-interface": true, 138 | "use-pipe-transform-interface": true, 139 | "directive-selector": [ 140 | true, 141 | "attribute", 142 | "app", 143 | "camelCase" 144 | ], 145 | "component-selector": [ 146 | true, 147 | "element", 148 | "app", 149 | "kebab-case" 150 | ] 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /SOURCE/src/app/models/ldObjects/ldRightPowerRail.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @Filename : ldRightPowerRail.ts 3 | * 4 | * @Author : Leonie de Santis 5 | * 6 | * @Last_Modified : 13.05.2021 7 | * 8 | */ 9 | import { ConnectionPoint, PLCNode } from '../PLCNode'; 10 | 11 | 12 | export class LdRightPowerRail { 13 | public xml: any; 14 | public localId: string; 15 | public height = 20; 16 | public width = 20; 17 | public position: { x: number, y: number } = {x: 0, y: 0}; 18 | public connectionPointIn: { x: number, y: number, refLocalID: string} = {x: 0, y: 0, refLocalID: null}; 19 | public node: PLCNode = {id: null, label: null, type: null, connectionPoints: null}; 20 | public edges: string[] = []; 21 | 22 | // check if imported xml ist empty, then create xml, otherwise reads relevant values of xml- file 23 | constructor(xmlRightPowerRail: any) { 24 | if (xmlRightPowerRail === '') { 25 | this.createNewRightPowerRail(); 26 | } 27 | else { 28 | this.xml = xmlRightPowerRail; 29 | this.localId = xmlRightPowerRail.getAttribute('localId'); 30 | if (xmlRightPowerRail.getAttribute('width') !== undefined) { 31 | this.width = xmlRightPowerRail.getAttribute('width'); 32 | } 33 | if (xmlRightPowerRail.getAttribute('height') !== undefined) { 34 | this.height = xmlRightPowerRail.getAttribute('height'); 35 | } 36 | if (xmlRightPowerRail.getElementsByTagName('position')[0] !== undefined) { 37 | const position = xmlRightPowerRail.getElementsByTagName('position')[0]; 38 | this.position = { x: position.getAttribute('x'), y: position.getAttribute('y')}; 39 | } 40 | if ( xmlRightPowerRail.getElementsByTagName('connectionPointIn')[0] !== undefined) { 41 | const connectionPointIn = xmlRightPowerRail.getElementsByTagName('connectionPointIn')[0]; 42 | if (connectionPointIn.getElementsByTagName('relPosition')[0] !== undefined) { 43 | const position = connectionPointIn.getElementsByTagName('relPosition')[0]; 44 | this.connectionPointIn.x = position.getAttribute('x'); 45 | this.connectionPointIn.y = position.getAttribute('y'); 46 | } 47 | if (connectionPointIn.getElementsByTagName('connection')[0] !== undefined) { 48 | const connection = connectionPointIn.getElementsByTagName('connection')[0]; 49 | this.connectionPointIn.refLocalID = connection.getAttribute('refLocalId'); 50 | } 51 | } 52 | } 53 | 54 | // values that are relevant for illustration are written into nodes 55 | this.node.id = this.localId; 56 | this.node.type = 'RPR'; 57 | const newConnectionPointIn: ConnectionPoint = { 58 | type: 'IN', 59 | sourceId: this.connectionPointIn.refLocalID, 60 | targetId: this.localId, 61 | edgeId: null 62 | }; 63 | this.node.connectionPoints.push(newConnectionPointIn); 64 | } 65 | 66 | // creates a default xml-file for the object 67 | createNewRightPowerRail(): void { 68 | const xmlString = ' \n' + 69 | ' \n' + 70 | ' \n' + 71 | ' \n' + 72 | ' \n' + 73 | ' '; 74 | const parser = new DOMParser(); 75 | this.xml = parser.parseFromString(xmlString, 'application/xml'); 76 | this.xml = this.xml.getElementsByTagName('rightPowerRail')[0]; 77 | } 78 | 79 | // updates attributes of position 80 | updatePosition(xPos: number, yPos: number): void { 81 | this.xml.getElementsByTagName('position')[0].setAttribute('x', xPos); 82 | this.xml.getElementsByTagName('position')[0].setAttribute('y', yPos); 83 | } 84 | 85 | // updates relevant attributes 86 | updateAttributes(localId: number): void{ 87 | this.xml.setAttribute('localId', localId); 88 | } 89 | 90 | // updates refId of ConnectionPointIn 91 | change_refid(newRef): void { 92 | this.xml.getElementsByTagName('connectionPointIn')[0].getElementsByTagName('connection')[0].setAttribute('refLocalId', newRef); 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PLCOpen-Editor 2 |
3 |
4 | 5 |
6 |
7 |

Welcome to the repository of the Web PLCOpen-Editor!

8 | 9 | This is a student project developed at Baden-Wuerttemberg Cooperative State University (DHBW) Stuttgart and supervised by Markus Rentschler and Christian Holder. 10 |
11 | Our team members are:
12 | 1. Mouaz Tabboush 13 | 2. Leonie de Santis 14 | 3. Elian Yildirim 15 | 4. Franziska Kopp 16 |
17 | The goal of this project is to develop a web version of the [OpenPLC-Editor](https://www.openplcproject.com/plcopen-editor/) that supports the Sequential Function Chart (SFC) and Function Block Diagram (FBD) languages. 18 | It is supposed to be an easy tool to create PLC programs without needing to install the software. 19 | 20 | ## How to run 21 | **Installing NodeJs**
22 | In order to install Angular you need npm, which comes with NodeJs. 23 | visit this [link](https://nodejs.org/en/download/) to download and install NodeJs 24 | 25 | **Installing Angular**
26 | Now go to your terminal and install angular by entering the following: 27 | ```npm install -g @angular/cli``` 28 | 29 | **Installing dependencies**
30 | Navigate in the terminal into SOURCE in the terminal and enter 31 | ```npm i``` 32 | this should install all the packages needed for the project 33 | 34 | **Testing the project**
35 | If all the dependancies are installed just navigate to OpenPLC-UI in the terminal and enter 36 | ```ng serve``` 37 | This will lunch a development serve on ```localhost://4200``` open that on your browser and you should be good to go 38 | 39 | ## FAQ
40 | **What is PLC?**
41 | PLC stands for Programable logic controllers.
42 | A simple way to understand it would be to think of PLC as a small computer that performs a list of instructions.
43 | The list of instructions contains logic that helps determine which course of action to take on a given set of circumstances.
44 | PLC is used in alot of areas inside and outside the industrial work. Usually where automation of tasks is the goal.
45 | The PLC editor itself helps user create programs without the need to know the syntax of the used programming lanugage for the PLC. All what the user needs to do is to determine the logic for the controller.
46 | The OpenPLC [Reference page](https://www.openplcproject.com/plcopen-editor/) has a nice tutorial to follow along and learn about PLC
47 |
48 | **Why are we doing the project?**
49 | We want to develop a web version of the OpenPLC Editor to allow users to work on their programs remotely without requiring installation.
50 | You can find more information about the project by visiting the wiki and reading the [Customer Requirement Specification](https://github.com/elian15122000/TINF19C-PLCOpen-Editor/blob/master/PROJECT/CRS/TINF19C_CRS_Team_1_0v1.pdf), [System Requirements Specification](https://github.com/elian15122000/TINF19C-PLCOpen-Editor/wiki/1.-System-Requirements-Specification) and [System Architecture Specification](https://github.com/elian15122000/TINF19C-PLCOpen-Editor/wiki/2.-System-Architecture-Specification) documents.
51 | 52 | **What technologies are we using to make the application?**
53 | The Application runs is written in Angular 8 and runs solely as a front end application. There is no backedend envolved
54 |
55 |
56 | 57 | **Why are we using this template for the project?**
58 | Its required of us to use this template.
59 | However, you only need to navigate to OpenPLC-UI to see our source-code. Everything outside of the that folder is just for Project management purposes. 60 | The Template works as follows: 61 | * OpenPLC-UI: Has all the sourcefiles needed to run the project 62 | * Meeting Minutes: The folder where we save protocols of our meetings 63 | * Executables: Part of the template we have to use, but we don't have any executables so this folder is not necessary 64 | * Resources: The folder where we save all pictures we need to reference in the markdown documents 65 | * Project: The Folder where we keep all organisation related documents. 66 |
67 |
68 | -------------------------------------------------------------------------------- /SOURCE/src/app/models/sfcObjects/sfcJumpStep.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @Filename : sfcJumpStep.ts 3 | * 4 | * @Author : Franziska Kopp 5 | * 6 | * @Last_Modified : 19.05.2021 7 | * 8 | */ 9 | 10 | import { ConnectionPoint, PLCNode } from '../PLCNode'; 11 | 12 | 13 | export class SfcJumpStep { 14 | public xml: any; 15 | public localId: string; 16 | public height = 20; 17 | public width = 20; 18 | public targetName = ''; 19 | public connectionPointIn: {x: number, y: number, refLocalID: string, formalParameter: string} = {x: 0, y: 0, refLocalID: '', formalParameter: ''}; 20 | public position: {x: 0, y: 0}; 21 | public node: PLCNode = {id: null, label: null, type: null, connectionPoints: null}; 22 | 23 | // check if imported xml ist empty, then create xml, otherwise reads relevant values of xml- file 24 | constructor(xmlJumpStep: any) { 25 | if (xmlJumpStep === '') { 26 | this.createXML(); 27 | } else { 28 | this.xml = xmlJumpStep; 29 | if (xmlJumpStep.getAttribute('localId') !== undefined) { 30 | this.localId = xmlJumpStep.getAttribute('localId'); 31 | } 32 | if (xmlJumpStep.getAttribute('height') !== undefined) { 33 | this.height = xmlJumpStep.getAttribute('height'); 34 | } 35 | if (xmlJumpStep.getAttribute('width') !== undefined) { 36 | this.width = xmlJumpStep.getAttribute('width'); 37 | } 38 | if (xmlJumpStep.getAttribute('targetName') !== undefined) { 39 | this.targetName = xmlJumpStep.getAttribute('targetName'); 40 | } 41 | if ( xmlJumpStep.getElementsByTagName('connectionPointIn') !== undefined) { 42 | const connectionPointIn = xmlJumpStep.getElementsByTagName('connectionPointIn')[0]; 43 | if (connectionPointIn.getElementsByTagName('relPosition') !== undefined) { 44 | const position = connectionPointIn.getElementsByTagName('relPosition')[0]; 45 | this.connectionPointIn.x = position.getAttribute('x'); 46 | this.connectionPointIn.y = position.getAttribute('y'); 47 | } 48 | if (connectionPointIn.getElementsByTagName('connection')[0] !== undefined) { 49 | const connection = connectionPointIn.getElementsByTagName('connection')[0]; 50 | this.connectionPointIn.refLocalID = connection.getAttribute('refLocalId'); 51 | this.connectionPointIn.formalParameter = connection.getAttribute('formalParameter'); 52 | } 53 | } 54 | if (xmlJumpStep.getElementsByTagName('position') !== undefined) { 55 | const position = xmlJumpStep.getElementsByTagName('position')[0]; 56 | this.position = {x: position.getAttribute('x'), y: position.getAttribute('y')}; 57 | } 58 | } 59 | // values that are relevant for illustration are written into nodes 60 | this.node.id = this.localId; 61 | this.node.type = 'default'; 62 | const newConnectionPointIn: ConnectionPoint = { 63 | type: 'IN', 64 | sourceId: this.connectionPointIn.refLocalID, 65 | targetId: this.localId, 66 | edgeId: null 67 | }; 68 | this.node.connectionPoints.push(newConnectionPointIn); 69 | 70 | } 71 | // creates a default xml-file for the object 72 | createXML(): void{ 73 | const xmlString = '\n' + 74 | '\n' + 75 | ' \n' + 76 | ' \n' + 77 | '\n' + 78 | ' \n'; 79 | const parser = new DOMParser(); 80 | this.xml = parser.parseFromString(xmlString, 'application/xml').getElementsByTagName('jumpStep')[0]; 81 | } 82 | // updates attributes of position 83 | updatePosition(xPos: number, yPos: number): void { 84 | this.xml.getElementsByTagName('position')[0].setAttribute('x', xPos); 85 | this.xml.getElementsByTagName('position')[0].setAttribute('y', yPos); 86 | } 87 | // updates relevant attributes 88 | updateAttributes(localId: number, targetName: string): void{ 89 | this.xml.setAttribute('localId', localId); 90 | this.xml.setAttribute('targetName', targetName); 91 | } 92 | // updates refId of ConnectionPointIn 93 | change_refid(newRef): void { 94 | this.xml.getElementsByTagName('connectionPointIn')[0].getElementsByTagName('connection')[0].setAttribute('refLocalId', newRef); 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /SOURCE/src/app/models/commonObjects/commonContinuation.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @Filename : commonContinuation.ts 3 | * 4 | * @Author : Franziska Kopp 5 | * 6 | * @Last_Modified : 19.05.2021 7 | * 8 | */ 9 | 10 | import {Node} from '@swimlane/ngx-graph'; 11 | import { ConnectionPoint, PLCNode } from '../PLCNode'; 12 | 13 | export class CommonContinuation{ 14 | public xml: any; 15 | public localId: string; 16 | public height = 20; 17 | public width = 20; 18 | public connectionPointOut: {x: 0, y: 0, refLocalId: '', formalParameter: ''}; 19 | public position: {x: 0, y: 0}; 20 | public node: PLCNode = {id: null, label: null, type: null, connectionPoints: null}; 21 | 22 | // check if imported xml ist empty, then create xml, otherwise reads relevant values of xml- file 23 | constructor(xmlCommonContinuation: any) { 24 | if (xmlCommonContinuation === ''){ 25 | this.createXML(); 26 | } else { 27 | this.xml = xmlCommonContinuation; 28 | if (xmlCommonContinuation.getAttribute('localId') !== undefined){ 29 | this.localId = xmlCommonContinuation.getAttribute('localId'); 30 | } 31 | if (xmlCommonContinuation.getAttribute('height') !== undefined){ 32 | this.height = xmlCommonContinuation.getAttribute('height'); 33 | } 34 | if (xmlCommonContinuation.getAttribute('width') !== undefined){ 35 | this.width = xmlCommonContinuation.getAttribute('width'); 36 | } 37 | if (xmlCommonContinuation.getElementsByTagName('position') !== undefined) { 38 | const position = xmlCommonContinuation.getElementsByTagName('position')[0]; 39 | this.position = {x: position.getAttribute('x'), y: position.getAttribute('y')}; 40 | } 41 | if ( xmlCommonContinuation.getElementsByTagName('connectionPointOut') !== undefined) { 42 | const connectionPointOut = xmlCommonContinuation.getElementsByTagName('connectionPointOut')[0]; 43 | if (connectionPointOut.getElementsByTagName('relPosition') !== undefined) { 44 | const position = connectionPointOut.getElementsByTagName('relPosition')[0]; 45 | this.connectionPointOut.x = position.getAttribute('x'); 46 | this.connectionPointOut.y = position.getAttribute('y'); 47 | } 48 | if (connectionPointOut.getElementsByTagName('connection')[0] !== undefined) { 49 | const connection = connectionPointOut.getElementsByTagName('connection')[0]; 50 | this.connectionPointOut.refLocalId = connection.getAttribute('refLocalId'); 51 | this.connectionPointOut.formalParameter = connection.getAttribute('formalParameter'); 52 | } 53 | } 54 | } 55 | // values that are relevant for illustration are written into nodes 56 | this.node.id = this.localId; 57 | this.node.type = 'default'; 58 | const newConnectionPoint: ConnectionPoint = { 59 | type: "OUT", 60 | sourcePoint: "OUT", 61 | targetPoint: this.connectionPointOut.formalParameter, 62 | sourceId: this.localId, 63 | sourceName: this.node.label, 64 | targetId: this.connectionPointOut.refLocalId, 65 | edgeId: null, 66 | } 67 | this.node.connectionPoints.push(newConnectionPoint); 68 | } 69 | 70 | // creates a default xml-file for the object 71 | createXML(): void{ 72 | const xmlString = '\n' + 73 | '\n' + 74 | '\n' + 75 | '\n' + 76 | '\n' + 77 | ' \n'; 78 | const parser = new DOMParser(); 79 | this.xml = parser.parseFromString(xmlString, 'application/xml').getElementsByTagName('continuation')[0]; 80 | this.xml = this.xml.getElementsByTagName('continuation')[0]; 81 | } 82 | // updates attributes of position 83 | updatePosition(xPos: number, yPos: number): void { 84 | this.xml.getElementsByTagName('position')[0].setAttribute('x', xPos); 85 | this.xml.getElementsByTagName('position')[0].setAttribute('y', yPos); 86 | } 87 | // updates relevant attributes 88 | updateAttributes(localId: number, name: string): void{ 89 | this.xml.setAttribute('localId', localId); 90 | this.xml.setAttribute('name', name); 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /SOURCE/angular.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "./node_modules/@angular/cli/lib/config/schema.json", 3 | "cli": { 4 | "analytics": false 5 | }, 6 | "version": 1, 7 | "newProjectRoot": "projects", 8 | "projects": { 9 | "OpenPLC-Editor": { 10 | "projectType": "application", 11 | "schematics": {}, 12 | "root": "", 13 | "sourceRoot": "src", 14 | "prefix": "app", 15 | "architect": { 16 | "build": { 17 | "builder": "@angular-devkit/build-angular:browser", 18 | "options": { 19 | "outputPath": "dist/OpenPLC-Editor", 20 | "index": "src/index.html", 21 | "main": "src/main.ts", 22 | "polyfills": "src/polyfills.ts", 23 | "tsConfig": "tsconfig.app.json", 24 | "aot": true, 25 | "assets": [ 26 | "src/favicon.ico", 27 | "src/assets" 28 | ], 29 | "styles": [ 30 | "src/styles.css" 31 | ], 32 | "scripts": [] 33 | }, 34 | "configurations": { 35 | "production": { 36 | "fileReplacements": [ 37 | { 38 | "replace": "src/environments/environment.ts", 39 | "with": "src/environments/environment.prod.ts" 40 | } 41 | ], 42 | "optimization": true, 43 | "outputHashing": "all", 44 | "sourceMap": false, 45 | "namedChunks": false, 46 | "extractLicenses": true, 47 | "vendorChunk": false, 48 | "buildOptimizer": true, 49 | "budgets": [ 50 | { 51 | "type": "initial", 52 | "maximumWarning": "2mb", 53 | "maximumError": "5mb" 54 | }, 55 | { 56 | "type": "anyComponentStyle", 57 | "maximumWarning": "6kb", 58 | "maximumError": "10kb" 59 | } 60 | ] 61 | } 62 | } 63 | }, 64 | "serve": { 65 | "builder": "@angular-devkit/build-angular:dev-server", 66 | "options": { 67 | "browserTarget": "OpenPLC-Editor:build" 68 | }, 69 | "configurations": { 70 | "production": { 71 | "browserTarget": "OpenPLC-Editor:build:production" 72 | } 73 | } 74 | }, 75 | "extract-i18n": { 76 | "builder": "@angular-devkit/build-angular:extract-i18n", 77 | "options": { 78 | "browserTarget": "OpenPLC-Editor:build" 79 | } 80 | }, 81 | "test": { 82 | "builder": "@angular-devkit/build-angular:karma", 83 | "options": { 84 | "main": "src/test.ts", 85 | "polyfills": "src/polyfills.ts", 86 | "tsConfig": "tsconfig.spec.json", 87 | "karmaConfig": "karma.conf.js", 88 | "assets": [ 89 | "src/favicon.ico", 90 | "src/assets" 91 | ], 92 | "styles": [ 93 | "src/styles.css" 94 | ], 95 | "scripts": [] 96 | } 97 | }, 98 | "lint": { 99 | "builder": "@angular-devkit/build-angular:tslint", 100 | "options": { 101 | "tsConfig": [ 102 | "tsconfig.app.json", 103 | "tsconfig.spec.json", 104 | "e2e/tsconfig.json" 105 | ], 106 | "exclude": [ 107 | "**/node_modules/**" 108 | ] 109 | } 110 | }, 111 | "e2e": { 112 | "builder": "@angular-devkit/build-angular:protractor", 113 | "options": { 114 | "protractorConfig": "e2e/protractor.conf.js", 115 | "devServerTarget": "OpenPLC-Editor:serve" 116 | }, 117 | "configurations": { 118 | "production": { 119 | "devServerTarget": "OpenPLC-Editor:serve:production" 120 | } 121 | } 122 | } 123 | } 124 | } 125 | }, 126 | "defaultProject": "OpenPLC-Editor" 127 | } 128 | -------------------------------------------------------------------------------- /SOURCE/src/app/models/fbdObjects/fbdJump.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @Filename : fbdJump.ts 3 | * 4 | * @Author : Leonie de Santis 5 | * 6 | * @Last_Modified : 13.05.2021 7 | * 8 | */ 9 | import { PLCNode, ConnectionPoint } from '../PLCNode'; 10 | 11 | export class FbdJump { 12 | public xml: any; 13 | public localId: string; 14 | public height = 20; 15 | public width = 20; 16 | public label = ''; 17 | public position: {x: number, y: number} = {x: 0, y: 0}; 18 | public connectionPointIn: { x: number, y: number, refLocalID: string} = {x: 0, y: 0, refLocalID: null}; 19 | public node: PLCNode = {id: null, label: null, type: null, connectionPoints: null}; 20 | public edges: string[] = []; 21 | 22 | // check if imported xml ist empty, then create xml, otherwise reads relevant values of xml- file 23 | constructor(xmlJump: any) { 24 | if (xmlJump === '') { 25 | this.createNewJump(); 26 | } 27 | else { 28 | this.xml = xmlJump; 29 | this.localId = xmlJump.getAttribute('localId'); 30 | if (xmlJump.getAttribute('width') !== undefined) { 31 | this.width = xmlJump.getAttribute('width'); 32 | } 33 | if (xmlJump.getAttribute('height') !== undefined) { 34 | this.height = xmlJump.getAttribute('height'); 35 | } 36 | if (xmlJump.getAttribute('label') !== undefined) { 37 | this.label = xmlJump.getAttribute('label'); 38 | } 39 | if (xmlJump.getElementsByTagName('position')[0] !== undefined) { 40 | const position = xmlJump.getElementsByTagName('position')[0]; 41 | this.position = { x: position.getAttribute('x'), y: position.getAttribute('y')}; 42 | } 43 | if ( xmlJump.getElementsByTagName('connectionPointIn')[0] !== undefined) { 44 | const connectionPointIn = xmlJump.getElementsByTagName('connectionPointIn')[0]; 45 | if (connectionPointIn.getElementsByTagName('relPosition') !== undefined) { 46 | const position = connectionPointIn.getElementsByTagName('relPosition')[0]; 47 | this.connectionPointIn.x = position.getAttribute('x'); 48 | this.connectionPointIn.y = position.getAttribute('y'); 49 | } 50 | if (connectionPointIn.getElementsByTagName('connection')[0] !== undefined) { 51 | const connection = connectionPointIn.getElementsByTagName('connection')[0]; 52 | this.connectionPointIn.refLocalID = connection.getAttribute('refLocalId'); 53 | } 54 | } 55 | } 56 | 57 | // values that are relevant for illustration are written into nodes 58 | this.node.id = this.localId; 59 | this.node.label = this.label; 60 | this.node.type = 'jump'; 61 | const newConnectionPointIn: ConnectionPoint = { 62 | type: 'IN', 63 | sourceId: this.connectionPointIn.refLocalID, 64 | targetId: this.localId, 65 | edgeId: null 66 | }; 67 | this.node.connectionPoints.push(newConnectionPointIn); 68 | } 69 | 70 | // creates a default xml-file for the object 71 | createNewJump(): void { 72 | const xmlString = ' \n' + 73 | ' \n' + 74 | ' \n' + 75 | ' \n' + 76 | ' \n' + 77 | ' '; 78 | const parser = new DOMParser(); 79 | this.xml = parser.parseFromString(xmlString, 'application/xml'); 80 | this.xml = this.xml.getElementsByTagName('jump')[0]; 81 | } 82 | 83 | // updates attributes of position 84 | updatePosition(xPos: number, yPos: number): void { 85 | this.xml.getElementsByTagName('position')[0].setAttribute('x', xPos); 86 | this.xml.getElementsByTagName('position')[0].setAttribute('y', yPos); 87 | } 88 | 89 | // updates relevant attributes 90 | updateAttributes(localId: number, label: string): void{ 91 | this.xml.setAttribute('localId', localId); 92 | this.xml.setAttribute('label', label); 93 | } 94 | 95 | // updates refId of ConnectionPointIn 96 | change_refid(newRef): void { 97 | this.xml.getElementsByTagName('connectionPointIn')[0].getElementsByTagName('connection')[0].setAttribute('refLocalId', newRef); 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /SOURCE/src/app/models/commonObjects/commonConnector.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @Filename : commonConnector.ts 3 | * 4 | * @Author : Franziska Kopp 5 | * 6 | * @Last_Modified : 19.05.2021 7 | * 8 | */ 9 | 10 | import {Node} from '@swimlane/ngx-graph'; 11 | import { ConnectionPoint, PLCNode } from '../PLCNode'; 12 | 13 | 14 | export class CommonConnector{ 15 | public xml: any; 16 | public localId: string; 17 | public height = 20; 18 | public width = 20; 19 | public name = ''; 20 | public connectionPointIn: {x: 0, y: 0, refLocalId: '', formalParameter: ''}; 21 | public position: {x: 0, y: 0}; 22 | public node: PLCNode = {id: null, label: null, type: null, connectionPoints: null}; 23 | 24 | // check if imported xml ist empty, then create xml, otherwise reads relevant values of xml- file 25 | constructor(xmlCommonConnector: any) { 26 | if (xmlCommonConnector === ''){ 27 | this.createXML(); 28 | } else{ 29 | this.xml = xmlCommonConnector; 30 | if (xmlCommonConnector.getAttribute('localId') !== undefined){ 31 | this.localId = xmlCommonConnector.getAttribute('localId'); 32 | } 33 | if (xmlCommonConnector.getAttribute('height') !== undefined){ 34 | this.height = xmlCommonConnector.getAttribute('height'); 35 | } 36 | if (xmlCommonConnector.getAttribute('width') !== undefined){ 37 | this.width = xmlCommonConnector.getAttribute('width'); 38 | } 39 | if (xmlCommonConnector.getAttribute('name') !== undefined){ 40 | this.name = xmlCommonConnector.getAttribute('name'); 41 | } 42 | if (xmlCommonConnector.getElementsByTagName('position') !== undefined) { 43 | const position = xmlCommonConnector.getElementsByTagName('position')[0]; 44 | this.position = {x: position.getAttribute('x'), y: position.getAttribute('y')}; 45 | } 46 | if ( xmlCommonConnector.getElementsByTagName('connectionPointIn') !== undefined) { 47 | const connectionPointIn = xmlCommonConnector.getElementsByTagName('connectionPointIn')[0]; 48 | if (connectionPointIn.getElementsByTagName('relPosition') !== undefined) { 49 | const position = connectionPointIn.getElementsByTagName('relPosition')[0]; 50 | this.connectionPointIn.x = position.getAttribute('x'); 51 | this.connectionPointIn.y = position.getAttribute('y'); 52 | } 53 | if (connectionPointIn.getElementsByTagName('connection')[0] !== undefined) { 54 | const connection = connectionPointIn.getElementsByTagName('connection')[0]; 55 | this.connectionPointIn.refLocalId = connection.getAttribute('refLocalId'); 56 | this.connectionPointIn.formalParameter = connection.getAttribute('formalParameter'); 57 | } 58 | } 59 | } 60 | // values that are relevant for illustration are written into nodes 61 | this.node.id = this.localId; 62 | this.node.label = this.name; 63 | this.node.type = 'default'; 64 | const newConnectionPointIn: ConnectionPoint = { 65 | type: 'IN', 66 | sourceId: this.connectionPointIn.refLocalId, 67 | targetId: this.localId, 68 | edgeId: null 69 | }; 70 | this.node.connectionPoints.push(newConnectionPointIn); 71 | } 72 | // creates a default xml-file for the object 73 | createXML(): void{ 74 | const xmlString = '\n' + 75 | '\n' + 76 | ' \n' + 77 | ' \n' + 78 | '\n' + 79 | ' \n'; 80 | const parser = new DOMParser(); 81 | this.xml = parser.parseFromString(xmlString, 'application/xml').getElementsByTagName('connector')[0]; 82 | } 83 | 84 | // updates attributes of position 85 | updatePosition(xPos: number, yPos: number): void { 86 | this.xml.getElementsByTagName('position')[0].setAttribute('x', xPos); 87 | this.xml.getElementsByTagName('position')[0].setAttribute('y', yPos); 88 | } 89 | // updates relevant attributes 90 | updateAttributes(localId: number, name: string): void{ 91 | this.xml.setAttribute('localId', localId); 92 | this.xml.setAttribute('name', name); 93 | } 94 | // updates refId of ConnectionPointIn 95 | change_refid(newRef): void { 96 | this.xml.getElementsByTagName('connectionPointIn')[0].getElementsByTagName('connection')[0].setAttribute('refLocalId', newRef); 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /SOURCE/src/app/models/sfcObjects/sfcStep.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @Filename : sfcStep.ts 3 | * 4 | * @Author : Franziska Kopp 5 | * 6 | * @Last_Modified : 19.05.2021 7 | * 8 | */ 9 | 10 | export class SfcStep 11 | { 12 | public xml: any; 13 | public localId: number; 14 | public height = 20; 15 | public width = 20; 16 | public negated = false; 17 | public name = ''; 18 | public connectionPointIn: {x: 0, y: 0}; 19 | public connectionPointOut: {x: 0, y: 0}; 20 | public connectionPointOutAction: {x: 0, y: 0}; 21 | public position: {x: 0, y: 0}; 22 | public initialStep = false; 23 | 24 | // check if imported xml ist empty, then create xml, otherwise reads relevant values of xml- file 25 | constructor(xmlStep: any) { 26 | if (xmlStep === '') { 27 | this.createXML(); 28 | } else { 29 | this.xml = xmlStep; 30 | if (xmlStep.getAttribute('localId') !== undefined) { 31 | this.localId = xmlStep.getAttribute('localId'); 32 | } 33 | if (xmlStep.getAttribute('height') !== undefined) { 34 | this.height = xmlStep.getAttribute('height'); 35 | } 36 | if (xmlStep.getAttribute('width') !== undefined) { 37 | this.width = xmlStep.getAttribute('width'); 38 | } 39 | if (xmlStep.getAttribute('negated') !== true) { 40 | this.negated = xmlStep.getAttribute('negated'); 41 | } 42 | if (xmlStep.getAttribute('name') !== undefined) { 43 | this.name = xmlStep.getAttribute('name'); 44 | } 45 | if (xmlStep.getAttribute('initialStep') !== undefined) { 46 | this.initialStep = xmlStep.getAttribute('initialStep'); 47 | } 48 | if (xmlStep.getElementsByTagName('connectionPointIn') !== undefined) { 49 | for (const item of xmlStep.getElementsByTagName('connectionPointIn')) { 50 | const relPos = item.getElementsByTagName('relPosition'); 51 | const inX = relPos[0].getAttribute('x'); 52 | const inY = relPos[0].getAttribute('y'); 53 | this.connectionPointIn = {x: inX, y: inY}; 54 | } 55 | } 56 | if (xmlStep.getElementsByTagName('connectionPointOut') !== undefined) { 57 | for (const item of xmlStep.getElementsByTagName('connectionPointOut')) { 58 | const relPos = item.getElementsByTagName('relPosition'); 59 | const outX = relPos[0].getAttribute('x'); 60 | const outY = relPos[0].getAttribute('y'); 61 | this.connectionPointOut = {x: outX, y: outY}; 62 | } 63 | } 64 | if (xmlStep.getElementsByTagName('connectionPointOutAction') !== undefined) { 65 | for (const item of xmlStep.getElementsByTagName('connectionPointOutAction')) { 66 | const relPos = item.getElementsByTagName('relPosition'); 67 | const outX = relPos[0].getAttribute('x'); 68 | const outY = relPos[0].getAttribute('y'); 69 | this.connectionPointOutAction = {x: outX, y: outY}; 70 | } 71 | } 72 | if (xmlStep.getElementsByTagName('position') !== undefined) { 73 | const position = xmlStep.getElementsByTagName('position')[0]; 74 | this.position = {x: position.getAttribute('x'), y: position.getAttribute('y')}; 75 | } 76 | } 77 | } 78 | 79 | // creates a default xml-file for the object 80 | createXML(): void { 81 | const xmlString = '\n' + 82 | '\n' + 83 | ' \n' + 84 | ' \n' + 85 | '\n' + 86 | '\n' + 87 | '\n' + 88 | ' \n' + 89 | '\n' + 90 | '\n' + 91 | ' \n' + 92 | ' \n'; 93 | const parser = new DOMParser(); 94 | this.xml = parser.parseFromString(xmlString, 'application/xml').getElementsByTagName('step')[0]; 95 | } 96 | // updates attributes of position 97 | updatePosition(xPos: number, yPos: number): void { 98 | this.xml.getElementsByTagName('position')[0].setAttribute('x', xPos); 99 | this.xml.getElementsByTagName('position')[0].setAttribute('y', yPos); 100 | } 101 | // updates relevant attributes 102 | updateAttributes(localId: number, name: string, negated: string, initialStep: string): void{ 103 | this.xml.setAttribute('localId', localId); 104 | this.xml.setAttribute('name', name); 105 | this.xml.setAttribute('negated', negated); 106 | this.xml.setAttribute('initialStep', initialStep); 107 | } 108 | // updates refId of ConnectionPointIn 109 | change_refid(newRef): void { 110 | this.xml.getElementsByTagName('connectionPointIn')[0].getElementsByTagName('connection')[0].setAttribute('refLocalId', newRef); 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /SOURCE/src/app/variables-list/variables-list.component.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @Filename : variable-list.component.ts 3 | * 4 | * @Author : Leonie de Santis 5 | * 6 | * @Last_Modified : 13.05.2021 7 | * 8 | */ 9 | 10 | import { Component, OnInit } from '@angular/core'; 11 | import { Variable} from '../models/variable'; 12 | import {ActivatedRoute} from '@angular/router'; 13 | import {ProjectService} from '../services/project.service'; 14 | 15 | @Component({ 16 | selector: 'app-variables-list', 17 | templateUrl: './variables-list.component.html', 18 | styleUrls: ['./variables-list.component.css'] 19 | }) 20 | export class VariablesListComponent implements OnInit { 21 | public pouName: string; 22 | public variables: Variable[] = []; 23 | public types = ['BOOL', 'SINT', 'INT', 'DINT', 'LINT', 'USINT', 'UINT', 'UDINT', 'ULINT', 24 | 'REAL', 'LREAL', 'TIME', 'DATE', 'TOD', 'DT', 'STRING', 'BYTE', 'WORD', 'DWORD', 'LWORD']; 25 | public pou: any; 26 | 27 | constructor(private projectService: ProjectService, private route: ActivatedRoute) { 28 | } 29 | 30 | // find the current pou and read the values of the interface-Tag 31 | ngOnInit(): void { 32 | this.pouName = this.route.snapshot.params.pouName; 33 | this.pou = this.projectService.getPou(this.pouName); 34 | if (this.pou !== undefined) { 35 | if (this.pou.getElementsByTagName('interface')[0] !== undefined){ 36 | const list = this.pou.getElementsByTagName('interface')[0]; 37 | for (const localVars of list.getElementsByTagName('localVars')) { 38 | for ( const xmlVariable of localVars.getElementsByTagName('variable')) { 39 | const option = this.checkVariableOption(xmlVariable); 40 | this.variables.push(new Variable(xmlVariable, 'local', option)); 41 | } 42 | 43 | } 44 | for (const outputVars of list.getElementsByTagName('outputVars')) { 45 | for ( const xmlVariable of outputVars.getElementsByTagName('variable')) { 46 | const option = this.checkVariableOption(xmlVariable); 47 | this.variables.push(new Variable(xmlVariable, 'output', option)); 48 | } 49 | } 50 | for (const inputVars of list.getElementsByTagName('inputVars')) { 51 | for ( const xmlVariable of inputVars.getElementsByTagName('variable')) { 52 | const option = this.checkVariableOption(xmlVariable); 53 | this.variables.push(new Variable(xmlVariable, 'input', option)); 54 | } 55 | } 56 | for (const tempVars of list.getElementsByTagName('tempVars')) { 57 | for ( const xmlVariable of tempVars.getElementsByTagName('variable')) { 58 | const option = this.checkVariableOption(xmlVariable); 59 | this.variables.push(new Variable(xmlVariable, 'temp', option)); 60 | } 61 | } 62 | for (const externalVars of list.getElementsByTagName('externalVars')) { 63 | for ( const xmlVariable of externalVars.getElementsByTagName('variable')) { 64 | const option = this.checkVariableOption(xmlVariable); 65 | this.variables.push(new Variable(xmlVariable, 'external', option)); 66 | } 67 | } 68 | for (const inOutVars of list.getElementsByTagName('inOutVars')) { 69 | for ( const xmlVariable of inOutVars.getElementsByTagName('variable')) { 70 | const option = this.checkVariableOption(xmlVariable); 71 | this.variables.push(new Variable(xmlVariable, 'inOut', option)); 72 | } 73 | } 74 | } 75 | } 76 | } 77 | 78 | // check the option of the variable 79 | checkVariableOption(xmlVariable: any): string{ 80 | let option = ''; 81 | if (xmlVariable.getAttribute('constant') === 'true') { 82 | option = 'constant'; 83 | } else if (xmlVariable.getAttribute('retain') === 'true') { 84 | option = 'retain'; 85 | } else if (xmlVariable.getAttribute('nonretain') === 'true') { 86 | option = 'non-retain'; 87 | } 88 | return option; 89 | } 90 | 91 | // Create a new variable and add to the list of Variables 92 | public newVariable(): void { 93 | const newVariable = new Variable('', '', ''); 94 | this.variables.push(newVariable); 95 | this.onchange(); 96 | } 97 | 98 | // delete variable -> remove item of the variable list 99 | deleteVariable(item: Variable): void { 100 | this.variables = this.variables.filter(obj => obj !== item); 101 | } 102 | 103 | // updates the xml of the variable and push into xml of the pou 104 | onchange(): void{ 105 | this.pou.getElementsByTagName('interface')[0].innerHTML = ''; 106 | for (const variable of this.variables) { 107 | if(variable.name === ""){ 108 | alert("Variable name can't be empty"); 109 | variable.name = "default_name"; 110 | } 111 | variable.createXML(); 112 | this.pou.getElementsByTagName('interface')[0].appendChild(variable.xml); 113 | } 114 | } 115 | 116 | } 117 | 118 | -------------------------------------------------------------------------------- /SOURCE/src/app/models/fbdObjects/fbdOutVariable.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @Filename : fbdOutVariable.ts 3 | * 4 | * @Author : Leonie de Santis 5 | * 6 | * @Last_Modified : 13.05.2021 7 | * 8 | */ 9 | import { ConnectionPoint, PLCNode } from '../PLCNode'; 10 | 11 | export class FbdOutVariable { 12 | public xml: any; 13 | public name = ''; 14 | public localId: string; 15 | public height = 20; 16 | public width = 20; 17 | public negated = false; 18 | public position: { x: 0, y: 0 } = { x: 0, y: 0 }; 19 | public connectionPointIn: { x: number, y: number, refLocalID: string } = { x: 0, y: 0, refLocalID: null }; 20 | public node: PLCNode = { id: null, label: null, type: null, connectionPoints: [] }; 21 | 22 | 23 | // check if imported xml ist empty, then create xml, otherwise reads relevant values of xml- file 24 | constructor(xmlOutVariable: any) { 25 | if (xmlOutVariable === '') { 26 | this.createNewOutVariable(); 27 | } 28 | else { 29 | this.xml = xmlOutVariable; 30 | if (xmlOutVariable.getElementsByTagName('expression')[0] !== undefined) { 31 | this.name = xmlOutVariable.getElementsByTagName('expression')[0].innerHTML; 32 | } 33 | this.localId = xmlOutVariable.getAttribute('localId'); 34 | if (xmlOutVariable.getAttribute('width') !== undefined) { 35 | this.width = xmlOutVariable.getAttribute('width'); 36 | } 37 | if (xmlOutVariable.getAttribute('height') !== undefined) { 38 | this.height = xmlOutVariable.getAttribute('height'); 39 | } 40 | if (xmlOutVariable.getAttribute('negated') === true) { 41 | this.negated = xmlOutVariable.getAttribute('negated'); 42 | } 43 | if (xmlOutVariable.getElementsByTagName('position')[0] !== undefined) { 44 | const posistion = xmlOutVariable.getElementsByTagName('position')[0]; 45 | this.position = { x: posistion.getAttribute('x'), y: posistion.getAttribute('y') }; 46 | } 47 | if (xmlOutVariable.getElementsByTagName('connectionPointIn')[0] !== undefined) { 48 | const connectionPointIn = xmlOutVariable.getElementsByTagName('connectionPointIn')[0]; 49 | if (connectionPointIn.getElementsByTagName('relPosition')[0] !== undefined) { 50 | const position = connectionPointIn.getElementsByTagName('relPosition')[0]; 51 | this.connectionPointIn.x = position.getAttribute('x'); 52 | this.connectionPointIn.y = position.getAttribute('y'); 53 | } 54 | if (connectionPointIn.getElementsByTagName('connection')[0] !== undefined) { 55 | const connection = connectionPointIn.getElementsByTagName('connection')[0]; 56 | this.connectionPointIn.refLocalID = connection.getAttribute('refLocalId'); 57 | } 58 | } 59 | 60 | // values that are relevant for illustration are written into nodes 61 | this.node.id = this.localId; 62 | this.node.label = this.name; 63 | this.node.type = 'var'; 64 | 65 | const newConnectionPoint: ConnectionPoint = { 66 | type: 'IN', 67 | targetPoint: 'IN', 68 | sourceId: this.connectionPointIn.refLocalID, 69 | targetId: this.localId, 70 | targetName: this.node.label, 71 | edgeId: null 72 | }; 73 | this.node.connectionPoints.push(newConnectionPoint); 74 | } 75 | } 76 | 77 | // creates a default xml-file for the object 78 | createNewOutVariable(): void { 79 | const xmlString = ' \n' + 80 | ' \n' + 81 | ' \n' + 82 | ' \n' + 83 | ' \n' + 84 | ' LocalVar0' + 85 | ' '; 86 | const parser = new DOMParser(); 87 | this.xml = parser.parseFromString(xmlString, 'application/xml'); 88 | this.xml = this.xml.getElementsByTagName('outVariable')[0]; 89 | } 90 | 91 | // updates attributes of position 92 | updatePosition(xPos: number, yPos: number): void { 93 | this.xml.getElementsByTagName('position')[0].setAttribute('x', xPos); 94 | this.xml.getElementsByTagName('position')[0].setAttribute('y', yPos); 95 | } 96 | 97 | // updates relevant attributes 98 | updateAttributes(localId: string, name: string,negated: string): void{ 99 | this.xml.setAttribute('localId', localId); 100 | this.xml.setAttribute('negated', negated); 101 | this.xml.getElementsByTagName('expression')[0].innerHTML = name; 102 | this.node.id = localId; 103 | this.node.label = name; 104 | this.node.type = 'var'; 105 | const newConnectionPoint: ConnectionPoint = { 106 | type: 'IN', 107 | targetPoint: 'IN', 108 | targetId: this.localId, 109 | targetName: this.node.label, 110 | edgeId: null 111 | }; 112 | this.node.connectionPoints.push(newConnectionPoint); 113 | 114 | 115 | } 116 | 117 | // updates refId of ConnectionPointIn 118 | change_refid(newRef, formalParameter): void { 119 | this.xml.getElementsByTagName('connectionPointIn')[0].getElementsByTagName('connection')[0].setAttribute('refLocalId', newRef); 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /SOURCE/src/app/models/variable.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @Filename : variable.ts 3 | * 4 | * @Author : Leonie de Santis 5 | * 6 | * @Last_Modified : 13.05.2021 7 | * 8 | */ 9 | export class Variable { 10 | public xml: any; 11 | public class = 'local'; 12 | public option = ''; 13 | public name = 'variable'; 14 | public type = 'BOOL'; 15 | public iec = ''; 16 | public init = ''; 17 | public documentation = ''; 18 | 19 | // check if imported xml ist empty, then create xml, otherwise reads relevant values of xml- file 20 | constructor(xmlVariable: any, varClass: string, varOption: string) { 21 | if (xmlVariable !== '') { 22 | this.class = varClass; 23 | this.option = varOption; 24 | 25 | if (xmlVariable.getAttribute('address') !== undefined){ 26 | this.iec = xmlVariable.getAttribute('address'); 27 | } 28 | if (xmlVariable.getElementsByTagName('documentation') !== undefined) { 29 | if (xmlVariable.getElementsByTagName('documentation')[0] !== undefined) { 30 | if (xmlVariable.getElementsByTagName('documentation')[0].children[0] !== undefined) { 31 | this.documentation = xmlVariable.getElementsByTagName('documentation')[0].children[0].innerText; 32 | } 33 | } 34 | } 35 | if (xmlVariable.getElementsByTagName('simpleValue') !== undefined){ 36 | const init = xmlVariable.getElementsByTagName('simpleValue')[0]; 37 | if (init !== undefined){ 38 | this.init = init.getAttribute('value'); 39 | } 40 | } 41 | if (xmlVariable.getAttribute('name') !== undefined){ 42 | this.name = xmlVariable.getAttribute('name'); 43 | } 44 | if (xmlVariable.getElementsByTagName('type') !== undefined) { 45 | if (xmlVariable.getElementsByTagName('derived').length > 0){ 46 | const derived = xmlVariable.getElementsByTagName('derived')[0]; 47 | this.type = derived.getAttribute('name'); 48 | } 49 | else { 50 | const types = ['BOOL', 'SINT', 'INT', 'DINT', 'LINT', 'USINT', 'UINT', 'UDINT', 'ULINT', 51 | 'REAL', 'LREAL', 'TIME', 'DATE', 'TOD', 'DT', 'STRING', 'BYTE', 'WORD', 'DWORD', 'LWORD']; 52 | for (const type of types) { 53 | if (xmlVariable.getElementsByTagName(type).length > 0) { 54 | this.type = type; 55 | } 56 | } 57 | } 58 | } 59 | } 60 | this.createXML(); 61 | } 62 | 63 | // creates a default xml-file for the object 64 | createXML(): void { 65 | let variableString = '\n' + 66 | ' \n' + 67 | ' <' + this.type + '/>\n' + 68 | ' \n' + 69 | ' \n' + 70 | ' \n' + 71 | ' \n' + 72 | ' \n' + 73 | ' \n' + 74 | ' \n'; 75 | 76 | const parser = new DOMParser(); 77 | 78 | switch (this.class) { 79 | case 'local': variableString = '\n\n' + variableString + '\n\n'; 80 | const variableXML1 = parser.parseFromString(variableString, 'application/xml'); 81 | this.xml = variableXML1.getElementsByTagName('localVars')[0]; 82 | break; 83 | case 'input': variableString = '\n\n' + variableString + '\n'; 84 | const variableXML2 = parser.parseFromString(variableString, 'application/xml'); 85 | this.xml = variableXML2.getElementsByTagName('inputVars')[0]; 86 | break; 87 | case 'output': variableString = '\n' + variableString + '\n'; 88 | const variableXML3 = parser.parseFromString(variableString, 'application/xml'); 89 | this.xml = variableXML3.getElementsByTagName('outputVars')[0]; 90 | break; 91 | case 'external': variableString = '\n' + variableString + '\n'; 92 | const variableXML4 = parser.parseFromString(variableString, 'application/xml'); 93 | this.xml = variableXML4.getElementsByTagName('externalVars')[0]; 94 | break; 95 | case 'temp': variableString = '\n' + variableString + '\n'; 96 | const variableXML5 = parser.parseFromString(variableString, 'application/xml'); 97 | this.xml = variableXML5.getElementsByTagName('tempVars')[0]; 98 | break; 99 | case 'inOut': variableString = '\n' + variableString + '\n'; 100 | const variableXML6 = parser.parseFromString(variableString, 'application/xml'); 101 | this.xml = variableXML6.getElementsByTagName('inOutVars')[0]; 102 | break; 103 | default: variableString = '\n' + variableString + '\n'; 104 | const variableXML7 = parser.parseFromString(variableString, 'application/xml'); 105 | this.xml = variableXML7.getElementsByTagName('localVars')[0]; 106 | } 107 | 108 | } 109 | } 110 | 111 | -------------------------------------------------------------------------------- /SOURCE/src/app/models/fbdObjects/fbdInVariable.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @Filename : fbdInVariable.ts 3 | * 4 | * @Author : Leonie de Santis 5 | * 6 | * @Last_Modified : 13.05.2021 7 | * 8 | */ 9 | import { PLCNode, ConnectionPoint } from '../PLCNode'; 10 | 11 | export class FbdInVariable { 12 | public xml: any; 13 | public name = ''; 14 | public localId: string; 15 | public height = 20; 16 | public width = 20; 17 | public negated = false; 18 | public position: { x: number, y: number } = { x: 0, y: 0 }; 19 | public connectionPointOut: { x: number, y: number, refLocalID: string, formalParameter: string } = 20 | { x: 0, y: 0, refLocalID: null, formalParameter: null }; 21 | public node: PLCNode = { id: null, label: null, type: null, connectionPoints: [] }; 22 | 23 | // check if imported xml ist empty, then create xml, otherwise reads relevant values of xml- file 24 | constructor(xmlInVariable: any) { 25 | if (xmlInVariable === '') { 26 | this.createNewInVariable(); 27 | } 28 | else { 29 | this.xml = xmlInVariable; 30 | if (xmlInVariable.getElementsByTagName('expression') !== undefined) { 31 | this.name = xmlInVariable.getElementsByTagName('expression')[0].innerHTML; 32 | } 33 | this.localId = xmlInVariable.getAttribute('localId'); 34 | if (xmlInVariable.getAttribute('width') !== undefined) { 35 | this.width = xmlInVariable.getAttribute('width'); 36 | } 37 | if (xmlInVariable.getAttribute('height') !== undefined) { 38 | this.height = xmlInVariable.getAttribute('height'); 39 | } 40 | if (xmlInVariable.getAttribute('negated') === true) { 41 | this.negated = xmlInVariable.getAttribute('negated'); 42 | } 43 | if (xmlInVariable.getElementsByTagName('position')[0] !== undefined) { 44 | const position = xmlInVariable.getElementsByTagName('position')[0]; 45 | this.position = { x: position.getAttribute('x'), y: position.getAttribute('y') }; 46 | } 47 | if (xmlInVariable.getElementsByTagName('connectionPointOut')[0] !== undefined) { 48 | const connectionPointOut = xmlInVariable.getElementsByTagName('connectionPointOut')[0]; 49 | if (connectionPointOut.getElementsByTagName('relPosition') !== undefined) { 50 | const position = connectionPointOut.getElementsByTagName('relPosition')[0]; 51 | this.connectionPointOut.x = position.getAttribute('x'); 52 | this.connectionPointOut.y = position.getAttribute('y'); 53 | } 54 | if (connectionPointOut.getElementsByTagName('connection')[0] !== undefined) { 55 | const connection = connectionPointOut.getElementsByTagName('connection')[0]; 56 | this.connectionPointOut.refLocalID = connection.getAttribute('refLocalId'); 57 | // IMPORTANT: read the formal parameter 58 | this.connectionPointOut.formalParameter = connection.getAttribute('formalParameter'); 59 | console.log(connection); 60 | } 61 | } 62 | 63 | // values that are relevant for illustration are written into nodes 64 | this.node.id = this.localId; 65 | this.node.label = this.name; 66 | this.node.type = 'var'; 67 | const newConnectionPoint: ConnectionPoint = { 68 | type: 'OUT', 69 | sourcePoint: 'OUT', 70 | targetPoint: this.connectionPointOut.formalParameter, 71 | sourceId: this.localId, 72 | sourceName: this.node.label, 73 | targetId: this.connectionPointOut.refLocalID, 74 | edgeId: null, 75 | }; 76 | this.node.connectionPoints.push(newConnectionPoint); 77 | 78 | } 79 | } 80 | 81 | // creates a default xml-file for the object 82 | createNewInVariable(): void { 83 | const xmlString = ' \n' + 84 | ' \n' + 85 | ' \n' + 86 | ' \n' + 87 | ' \n' + 88 | ' LocalVar0' + // added expression 89 | ' '; 90 | const parser = new DOMParser(); 91 | this.xml = parser.parseFromString(xmlString, 'application/xml'); 92 | this.xml = this.xml.getElementsByTagName('inVariable')[0]; 93 | 94 | } 95 | 96 | // updates attributes of position 97 | updatePosition(xPos: number, yPos: number): void { 98 | this.xml.getElementsByTagName('position')[0].setAttribute('x', xPos); 99 | this.xml.getElementsByTagName('position')[0].setAttribute('y', yPos); 100 | } 101 | 102 | // updates relevant attribute 103 | updateAttributes(localId, name, negated): void{ 104 | this.xml.setAttribute('localId', localId); 105 | this.xml.setAttribute('negated', negated); 106 | this.xml.getElementsByTagName('expression')[0].innerHTML = name; 107 | this.node.id = localId; 108 | this.node.label = name; 109 | this.node.type = 'var'; 110 | const newConnectionPoint: ConnectionPoint = { 111 | type: 'OUT', 112 | sourcePoint: 'OUT', 113 | targetPoint: null, 114 | targetName: null, 115 | sourceId: localId, 116 | sourceName: name, 117 | targetId: null, 118 | edgeId: null, 119 | }; 120 | this.node.connectionPoints.push(newConnectionPoint); 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /SOURCE/src/app/services/project.service.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @Filename : project.service.ts 3 | * 4 | * @Author : Leonie de Santis 5 | * 6 | * @Last_Modified : 13.05.2021 7 | * 8 | */ 9 | 10 | import { Injectable } from '@angular/core'; 11 | import {DatePipe} from '@angular/common'; 12 | import { saveAs } from 'file-saver'; 13 | 14 | @Injectable({ 15 | providedIn: 'root' 16 | }) 17 | 18 | // The ProjectService contains all relevant information 19 | export class ProjectService { 20 | public pouItems: any[] = []; 21 | public headerItems: any[] = []; 22 | public instanceItems: any; 23 | 24 | constructor() { } 25 | 26 | // This function reads the names of the pous and is used for the project overview 27 | getPouName(): string[] { 28 | const pouNames = []; 29 | this.pouItems.forEach((item) => { 30 | pouNames.push(item.getAttribute('name')); 31 | }); 32 | return pouNames; 33 | } 34 | 35 | // This function reads the project name 36 | getProjectName(): string { 37 | if ( this.headerItems[1].getAttribute('name') !== undefined) { 38 | return this.headerItems[1].getAttribute('name'); 39 | } 40 | return ''; 41 | } 42 | 43 | // the function searches for the according pou 44 | getPou(pouName: string): any { 45 | let pou; 46 | for (const item of this.pouItems) { 47 | const check = (item.getAttribute('name') === pouName); 48 | if (check) { 49 | pou = item; 50 | return pou; 51 | } 52 | } 53 | return undefined; 54 | } 55 | 56 | // Delete a pou -> remove pou from pouItems 57 | deletePou(pouName: string): void { 58 | this.pouItems = this.pouItems.filter( (pou) => 59 | pou.getAttribute('name') !== pouName); 60 | } 61 | 62 | // Add Pou -> create a default-xml of a pou and add it to the pouItems 63 | addPou(pouName: string, pouType: string, lang: string): void { 64 | const pouStr = '\n' + 65 | ' ' + 66 | ' \n' + 67 | ' <' + lang + '/>\n' + 68 | ' \n' + 69 | ' '; 70 | 71 | const parser = new DOMParser(); 72 | this.pouItems.push(parser.parseFromString(pouStr, 'application/xml').getElementsByTagName('pou')[0]); 73 | } 74 | 75 | // Create a new Project 76 | // clear the existing lists 77 | // create the default xml strings of the headers and instances 78 | createNewProject(projectName: string): void { 79 | this.headerItems = []; 80 | this.pouItems = []; 81 | const datePipe = new DatePipe('en-US'); 82 | const date = datePipe.transform(new Date(), 'yyyy-MM-ddTHH:mm:ss'); 83 | if (projectName === '') { 84 | projectName = 'Unbenannt'; 85 | } 86 | 87 | // xml string of the fileHeader-Tag 88 | const fileHeaderStr = ''; 90 | 91 | // xml string of the conentHeader-Tag 92 | const contentHeaderStr = '\n' + 93 | ' \n' + 94 | ' \n' + 95 | ' \n' + 96 | ' \n' + 97 | ' \n' + 98 | ' \n' + 99 | ' \n' + 100 | ' \n' + 101 | ' \n' + 102 | ' \n' + 103 | ' \n' + 104 | ' '; 105 | 106 | // xml string of the default instances-Tag 107 | const instanceStr = '\n' + 108 | ' \n' + 109 | ' \n' + 110 | ' \n' + 111 | ' \n' + 112 | ' \n' + 113 | ' '; 114 | 115 | const parser = new DOMParser(); 116 | this.headerItems.push(parser.parseFromString(fileHeaderStr, 'application/xml').getElementsByTagName('fileHeader')[0]); 117 | this.headerItems.push(parser.parseFromString(contentHeaderStr, 'application/xml').getElementsByTagName('contentHeader')[0]); 118 | this.instanceItems = parser.parseFromString(instanceStr, 'application/xml').getElementsByTagName('instances')[0]; 119 | } 120 | 121 | // export Project -> merge the xml of the headers, pous and instances and save as plc.xml 122 | exportProject(): void { 123 | let pouString = ''; 124 | this.pouItems.forEach((item) => { 125 | pouString = pouString + item.outerHTML + '\n'; 126 | while (pouString.includes('xmlns:xhtml="http://www.w3.org/1999/xhtml"')) { 127 | pouString = pouString.replace(' xmlns:xhtml="http://www.w3.org/1999/xhtml"', ''); 128 | } 129 | while (pouString.includes('xmlns=""')) { 130 | pouString = pouString.replace(' xmlns=""', ''); 131 | } 132 | }); 133 | const xmlString2 = 134 | '\n' + 135 | ' \n' + 136 | ' \n' + pouString + 137 | ' \n' + 138 | '\n'; 139 | const fullStr = 140 | '\n' + 141 | '\n' + 142 | this.headerItems[0].outerHTML + '\n' + this.headerItems[1].outerHTML + '\n' + xmlString2 + this.instanceItems.outerHTML + '\n'; 143 | const blob = new Blob([fullStr], {type: 'text/xml;charset=utf-8'}); 144 | saveAs(blob, 'plc.xml'); 145 | } 146 | 147 | } 148 | 149 | 150 | -------------------------------------------------------------------------------- /SOURCE/src/app/project-overview/project-overview.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 |
5 |
6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 |
14 | 15 |

Project Overview

16 | 17 |

Project: {{projectName}}

18 | 19 | 20 | 21 |
22 | {{pou}} 23 | 24 |
25 | 26 | 27 | 28 | 46 |
47 | 48 | 49 |
50 | 51 | 52 | 53 | 54 | 55 | 56 |

58 |

59 | 60 | 61 | 62 | 63 | 64 |
65 | 66 | 67 | 68 |
69 | 70 | 71 |
72 | 73 | 74 | 75 | 76 | 77 | 78 |
79 | 80 | 81 | 82 | 83 | 84 | 85 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 157 | 158 | 159 | -------------------------------------------------------------------------------- /SOURCE/src/app/models/sfcObjects/sfcMacroStep.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @Filename : sfcMacroStep.ts 3 | * 4 | * @Author : Franziska Kopp 5 | * 6 | * @Last_Modified : 19.05.2021 7 | * 8 | */ 9 | 10 | import {ConnectionPoint, PLCNode} from '../PLCNode'; 11 | 12 | export class SfcMacroStep { 13 | public xml: any; 14 | public localId: string; 15 | public height = 20; 16 | public width = 20; 17 | public name = ''; 18 | public connectionPointIn: {x: 0, y: 0, refLocalId: '', formalParameter: string}; 19 | public connectionPointOut: {x: 0, y: 0, refLocalId: '', formalParameter: string}; 20 | public position: {x: 0, y: 0}; 21 | public node: PLCNode = {id: null, label: null, type: null, connectionPoints: null}; 22 | 23 | // check if imported xml ist empty, then create xml, otherwise reads relevant values of xml- file 24 | constructor(xmlMacroStep: any) { 25 | if (xmlMacroStep === '') { 26 | this.createXML(); 27 | } else { 28 | this.xml = xmlMacroStep; 29 | if (xmlMacroStep.getAttribute('localId') !== undefined) { 30 | this.localId = xmlMacroStep.getAttribute('localId'); 31 | } 32 | if (xmlMacroStep.getAttribute('height') !== undefined) { 33 | this.height = xmlMacroStep.getAttribute('height'); 34 | } 35 | if (xmlMacroStep.getAttribute('width') !== undefined) { 36 | this.width = xmlMacroStep.getAttribute('width'); 37 | } 38 | if (xmlMacroStep.getAttribute('name') !== undefined) { 39 | this.name = xmlMacroStep.getAttribute('name'); 40 | } 41 | if (xmlMacroStep.getElementsByTagName('position') !== undefined) { 42 | const position = xmlMacroStep.getElementsByTagName('position')[0]; 43 | this.position = {x: position.getAttribute('x'), y: position.getAttribute('y')}; 44 | } 45 | if (xmlMacroStep.getElementsByTagName('connectionPointIn') !== undefined) { 46 | for (const item of xmlMacroStep.getElementsByTagName('connectionPointIn')) { 47 | const relPos = item.getElementsByTagName('relPosition'); 48 | if (relPos[0] !== undefined) { 49 | this.connectionPointIn.x = relPos[0].getAttribute('x'); 50 | this.connectionPointIn.y = relPos[0].getAttribute('y'); 51 | } 52 | if (item.getElementsByTagName('connection')[0] !== undefined) { 53 | const connection = item.getElementsByTagName('connection')[0]; 54 | this.connectionPointIn.refLocalId = connection.getAttribute('refLocalId'); 55 | this.connectionPointIn.formalParameter = connection.getAttribute('formalParameter'); 56 | } 57 | 58 | } 59 | } 60 | if (xmlMacroStep.getElementsByTagName('connectionPointOut') !== undefined) { 61 | for (const item of xmlMacroStep.getElementsByTagName('connectionPointOut')) { 62 | const relPos = item.getElementsByTagName('relPosition'); 63 | if (relPos[0] !== undefined) { 64 | const outX = relPos[0].getAttribute('x'); 65 | const outY = relPos[0].getAttribute('y'); 66 | this.connectionPointOut.x = outX; 67 | this.connectionPointOut.y = outY; 68 | } 69 | if (item.getElementsByTagName('connection')[0] !== undefined) { 70 | const connection = item.getElementsByTagName('connection')[0]; 71 | this.connectionPointOut.refLocalId = connection.getAttribute('refLocalId'); 72 | this.connectionPointOut.formalParameter = connection.getAttribute('formalParameter'); 73 | } 74 | } 75 | } 76 | } 77 | // values that are relevant for illustration are written into nodes 78 | this.node.id = this.localId; 79 | this.node.label = ''; 80 | this.node.type = 'default'; 81 | const newConnectionPointIn: ConnectionPoint = { 82 | type: 'IN', 83 | sourcePoint: 'IN', 84 | sourceId: this.connectionPointIn.refLocalId, 85 | targetId: this.localId, 86 | edgeId: null 87 | }; 88 | const newConnectionPointOut: ConnectionPoint = { 89 | type: 'OUT', 90 | sourcePoint: 'OUT', 91 | sourceId: this.connectionPointOut.refLocalId, 92 | targetId: this.localId, 93 | edgeId: null 94 | }; 95 | this.node.connectionPoints.push(newConnectionPointIn); 96 | this.node.connectionPoints.push(newConnectionPointOut); 97 | } 98 | // creates a default xml-file for the object 99 | createXML(): void { 100 | const xmlString = '\n' + 101 | '\n' + 102 | ' \n' + 103 | ' \n' + 104 | '\n' + 105 | '\n' + 106 | '\n' + 107 | ' \n' + 108 | ' \n'; 109 | const parser = new DOMParser(); 110 | this.xml = parser.parseFromString(xmlString, 'application/xml').getElementsByTagName('macroStep')[0]; 111 | } 112 | // updates attributes of position 113 | updatePosition(xPos: number, yPos: number): void { 114 | this.xml.getElementsByTagName('position')[0].setAttribute('x', xPos); 115 | this.xml.getElementsByTagName('position')[0].setAttribute('y', yPos); 116 | } 117 | // updates relevant attributes 118 | updateAttributes(localId: number, name: string): void{ 119 | this.xml.setAttribute('localId', localId); 120 | this.xml.setAttribute('name', name); 121 | } 122 | // updates refId of ConnectionPointIn 123 | change_refid(newRef): void { 124 | this.xml.getElementsByTagName('connectionPointIn')[0].getElementsByTagName('connection')[0].setAttribute('refLocalId', newRef); 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /SOURCE/src/app/models/ldObjects/ldCoil.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @Filename : ldCoil.ts 3 | * 4 | * @Author : Leonie de Santis 5 | * 6 | * @Last_Modified : 13.05.2021 7 | * 8 | */ 9 | 10 | import { ConnectionPoint, PLCNode } from '../PLCNode'; 11 | 12 | 13 | export class LdCoil { 14 | public xml: any; 15 | public localId: string; 16 | public height = 20; 17 | public width = 20; 18 | public negated = false; 19 | public variable = ''; 20 | public position: { x: number, y: number } = {x: 0, y: 0}; 21 | public connectionPointIn: { x: number, y: number, refLocalID: string} = {x: 0, y: 0, refLocalID: null}; 22 | public connectionPointOut: {x: number, y: number, refLocalID: string} = {x: 0, y: 0, refLocalID: null}; 23 | public node: PLCNode = {id: null, label: null, type: null, connectionPoints: null}; 24 | public edges: string[] = []; 25 | 26 | // check if imported xml ist empty, then create xml, otherwise reads relevant values of xml- file 27 | constructor(xmlCoil: any) { 28 | if (xmlCoil === '') { 29 | this.createNewCoil(); 30 | } 31 | else { 32 | this.xml = xmlCoil; 33 | this.localId = xmlCoil.getAttribute('localId'); 34 | if (xmlCoil.getAttribute('width') !== undefined) { 35 | this.width = xmlCoil.getAttribute('width'); 36 | } 37 | if (xmlCoil.getAttribute('height') !== undefined) { 38 | this.height = xmlCoil.getAttribute('height'); 39 | } 40 | if (xmlCoil.getAttribute('negated') === true) { 41 | this.negated = true; 42 | } 43 | if (xmlCoil.getElementsByTagName('variable') !== undefined) { 44 | this.variable = xmlCoil.getElementsByTagName('variable')[0].innerHTML; 45 | } 46 | if (xmlCoil.getElementsByTagName('position')[0] !== undefined) { 47 | const position = xmlCoil.getElementsByTagName('position')[0]; 48 | this.position = { x: position.getAttribute('x'), y: position.getAttribute('y')}; 49 | } 50 | if ( xmlCoil.getElementsByTagName('connectionPointIn')[0] !== undefined) { 51 | const connectionPointIn = xmlCoil.getElementsByTagName('connectionPointIn')[0]; 52 | if (connectionPointIn.getElementsByTagName('relPosition') !== undefined) { 53 | const position = connectionPointIn.getElementsByTagName('relPosition')[0]; 54 | this.connectionPointIn.x = position.getAttribute('x'); 55 | this.connectionPointIn.y = position.getAttribute('y'); 56 | } 57 | if (connectionPointIn.getElementsByTagName('connection')[0] !== undefined) { 58 | const connection = connectionPointIn.getElementsByTagName('connection')[0]; 59 | this.connectionPointIn.refLocalID = connection.getAttribute('refLocalId'); 60 | } 61 | } 62 | if ( xmlCoil.getElementsByTagName('connectionPointOut')[0] !== undefined) { 63 | const connectionPointOut = xmlCoil.getElementsByTagName('connectionPointOut')[0]; 64 | if (connectionPointOut.getElementsByTagName('relPosition') !== undefined) { 65 | const position = connectionPointOut.getElementsByTagName('relPosition')[0]; 66 | this.connectionPointOut.x = position.getAttribute('x'); 67 | this.connectionPointOut.y = position.getAttribute('y'); 68 | } 69 | if (connectionPointOut.getElementsByTagName('connection')[0] !== undefined) { 70 | const connection = connectionPointOut.getElementsByTagName('connection')[0]; 71 | this.connectionPointOut.refLocalID = connection.getAttribute('refLocalId'); 72 | } 73 | } 74 | } 75 | 76 | // values that are relevant for illustration are written into nodes 77 | this.node.id = this.localId; 78 | this.node.label = this.variable; 79 | this.node.type = 'coil'; 80 | 81 | const newConnectionPointOut: ConnectionPoint = { 82 | type: 'OUT', 83 | sourceId: this.localId, 84 | targetId: this.connectionPointOut.refLocalID, 85 | edgeId: null, 86 | }; 87 | this.node.connectionPoints.push(newConnectionPointOut); 88 | 89 | const newConnectionPointIn: ConnectionPoint = { 90 | type: 'IN', 91 | sourceId: this.connectionPointIn.refLocalID, 92 | targetId: this.localId, 93 | edgeId: null 94 | }; 95 | this.node.connectionPoints.push(newConnectionPointIn); 96 | } 97 | 98 | // creates a default xml-file for the object 99 | createNewCoil(): void { 100 | const xmlString = ' \n' + 101 | ' \n' + 102 | ' \n' + 103 | ' \n' + 104 | ' \n' + 105 | ' \n' + 106 | ' \n' + 107 | ' \n' + 108 | ' \n' + 109 | ' '; 110 | const parser = new DOMParser(); 111 | this.xml = parser.parseFromString(xmlString, 'application/xml'); 112 | this.xml = this.xml.getElementsByTagName('coil')[0]; 113 | } 114 | 115 | // updates attributes of position 116 | updatePosition(xPos: number, yPos: number): void { 117 | this.xml.getElementsByTagName('position')[0].setAttribute('x', xPos); 118 | this.xml.getElementsByTagName('position')[0].setAttribute('y', yPos); 119 | } 120 | 121 | // updates relevant attributes 122 | updateAttributes(localId: number, negated: string): void{ 123 | this.xml.setAttribute('localId', localId); 124 | this.xml.setAttribute('negated', negated); 125 | } 126 | 127 | // updates refId of ConnectionPointIn 128 | change_refid(newRef): void { 129 | this.xml.getElementsByTagName('connectionPointIn')[0].getElementsByTagName('connection')[0].setAttribute('refLocalId', newRef); 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /SOURCE/src/app/homepage/homepage.component.html: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | 9 | 10 | 11 |
12 |

Welcome to PLCopen-Editor

13 |

PLCopen is an organization in the field of industrial control technology.
This can be developed 14 | using our web editor with the help of building blocks and both imported and exported in XML format.

15 |
16 | 17 | 18 |
19 |
20 |
21 |
22 |

New Project

23 |
24 |
25 |

Get started with a new project.
It's completely free!


26 | 28 |
29 |
30 |
31 |
32 |

Open Project

33 |
34 |
35 |

Open your existing XML project and continue working
It's completely free!

36 | 38 | 39 |
40 |
41 |
42 |
43 |

User Manual

44 |
45 |
46 |

Read our user manual if you don't know further.
We'll help you!

47 | 49 |
50 |
51 |
52 | 53 | 54 |
55 | 56 | 57 | 59 | 60 | 62 | 63 | 64 | 65 | 66 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 110 | 111 | 112 | 113 | 114 | 141 | 142 | -------------------------------------------------------------------------------- /SOURCE/src/app/models/commonObjects/commonActionBlock.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @Filename : commonActionBlock.ts 3 | * 4 | * @Author : Franziska Kopp 5 | * 6 | * @Last_Modified : 19.05.2021 7 | * 8 | */ 9 | 10 | // import {PLCNode} from '@swimlane/ngx-graph'; 11 | import { ConnectionPoint, PLCNode } from '../PLCNode'; 12 | 13 | export class CommonActionBlock{ 14 | public xml: any; 15 | public localId: string; 16 | public height = 20; 17 | public width = 20; 18 | public negated = false; 19 | public position: {x: 0, y: 0}; 20 | public actionRelPosition: {x: 0, y: 0}; 21 | public actionReferenceName = ''; 22 | public actionLocalId: number; 23 | public actionQualifier: string; 24 | public actionHeight = 20; 25 | public actionWidth = 20; 26 | public actionDuration = ''; 27 | public actionIndicator = ''; 28 | public connectionPointIn: {x: number, y: number, refLocalId: string, formalParameter: string}; 29 | public PLCNode: PLCNode = {id: null, label: null, type: null, connectionPoints: null}; 30 | 31 | // check if imported xml ist empty, then create xml, otherwise reads relevant values of xml- file 32 | constructor(xmlCommonActionBlock: any) { 33 | if (xmlCommonActionBlock === ''){ 34 | this.createXML(); 35 | } else { 36 | this.xml = xmlCommonActionBlock; 37 | if (xmlCommonActionBlock.getAttribute('localId') !== undefined) { 38 | this.localId = xmlCommonActionBlock.getAttribute('localId'); 39 | } 40 | if (xmlCommonActionBlock.getAttribute('height') !== undefined) { 41 | this.height = xmlCommonActionBlock.getAttribute('height'); 42 | } 43 | if (xmlCommonActionBlock.getAttribute('width') !== undefined) { 44 | this.width = xmlCommonActionBlock.getAttribute('width'); 45 | } 46 | if (xmlCommonActionBlock.getAttribute('negated') !== undefined) { 47 | this.negated = xmlCommonActionBlock.getAttribute('negated'); 48 | } 49 | if (xmlCommonActionBlock.getElementsByTagName('position') !== undefined) { 50 | const position = xmlCommonActionBlock.getElementsByTagName('position')[0]; 51 | this.position = {x: position.getAttribute('x'), y: position.getAttribute('y')}; 52 | } 53 | if (xmlCommonActionBlock.getElementsByTagName('connectionPointIn') !== undefined) { 54 | for (const item of xmlCommonActionBlock.getElementsByTagName('connectionPointIn')) { 55 | const relPos = item.getElementsByTagName('relPosition'); 56 | let inX: number; 57 | let inY: number; 58 | let refId = ''; 59 | let formalParam = ''; 60 | 61 | if (relPos[0] !== undefined) { 62 | inX = relPos[0].getAttribute('x'); 63 | inY = relPos[0].getAttribute('y'); 64 | } 65 | if (item.getElementsByTagName('connection')[0] !== undefined) { 66 | const connection = item.getElementsByTagName('connection')[0]; 67 | refId = connection.getAttribute('refLocalId'); 68 | formalParam = connection.getAttribute('formalParameter'); 69 | } 70 | this.connectionPointIn = {x: inX, y: inY, refLocalId: refId, formalParameter: formalParam}; 71 | } 72 | } 73 | if (xmlCommonActionBlock.getElementsByTagName('action') !== undefined) { 74 | for (const item of xmlCommonActionBlock.getElementsByTagName('action')) { 75 | let i = 0; 76 | const relPos = item.getElementsByTagName('relPosition'); 77 | if (relPos[0] !== undefined) { 78 | const inX = relPos[0].getAttribute('x'); 79 | const inY = relPos[0].getAttribute('y'); 80 | this.actionRelPosition = {x: inX, y: inY}; 81 | } 82 | const ref = item.getElementsByTagName('reference'); 83 | if (ref[0] !== undefined) { 84 | this.actionReferenceName = ref[0].getAttribute('name'); 85 | } 86 | const action = xmlCommonActionBlock.getElementsByTagName('action'); 87 | this.actionQualifier = action[i].getAttribute('qualifier'); 88 | this.actionHeight = action[i].getAttribute('height'); 89 | this.actionWidth = action[i].getAttribute('width'); 90 | this.actionLocalId = action[i].getAttribute('localId'); 91 | this.actionDuration = action[i].getAttribute('duration'); 92 | this.actionIndicator = action[i].getAttribute('indicator'); 93 | i += 1; 94 | } 95 | 96 | } 97 | } 98 | // values that are relevant for illustration are written into nodes 99 | this.PLCNode.id = this.localId; 100 | this.PLCNode.label = ''; 101 | this.PLCNode.type = 'default'; 102 | const newConnectionPointIn: ConnectionPoint = { 103 | type: 'IN', 104 | sourcePoint: 'IN', 105 | sourceId: this.connectionPointIn.refLocalId, 106 | targetId: this.localId, 107 | edgeId: null 108 | }; 109 | this.PLCNode.connectionPoints.push(newConnectionPointIn); 110 | } 111 | 112 | // creates a default xml-file for the object 113 | createXML(): void{ 114 | const xmlString = '\n' + 115 | ' \n' + 116 | ' \n' + 117 | '' + 118 | ' \n' + 119 | '' + 120 | '' + 121 | '\n' + 122 | '\n' + 123 | '\n' + 124 | '' + 125 | ' \n' + 126 | '\n' + 127 | ' \n'; 128 | const parser = new DOMParser(); 129 | this.xml = parser.parseFromString(xmlString, 'application/xml').getElementsByTagName('actionBlock')[0]; 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /SOURCE/src/app/models/ldObjects/ldContact.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @Filename : ldContact.ts 3 | * 4 | * @Author : Leonie de Santis 5 | * 6 | * @Last_Modified : 13.05.2021 7 | * 8 | */ 9 | import { ConnectionPoint, PLCNode } from '../PLCNode'; 10 | 11 | 12 | export class LdContact { 13 | public xml: any; 14 | public localId: string; 15 | public height = 20; 16 | public width = 20; 17 | public negated = false; 18 | public variable = ''; 19 | public position: { x: number, y: number } = {x: 0, y: 0}; 20 | public connectionPointIn: { x: number, y: number, refLocalID: string} = {x: 0, y: 0, refLocalID: null}; 21 | public connectionPointOut: {x: number, y: number, refLocalID: string} = {x: 0, y: 0, refLocalID: null}; 22 | public node: PLCNode = {id: null, label: null, type: null, connectionPoints: null}; 23 | public edges: string[] = []; 24 | 25 | // check if imported xml ist empty, then create xml, otherwise reads relevant values of xml- file 26 | constructor(xmlContact: any) { 27 | if (xmlContact === '') { 28 | this.createNewContact(); 29 | } 30 | else { 31 | this.xml = xmlContact; 32 | this.localId = xmlContact.getAttribute('localId'); 33 | if (xmlContact.getAttribute('width') !== undefined) { 34 | this.width = xmlContact.getAttribute('width'); 35 | } 36 | if (xmlContact.getAttribute('height') !== undefined) { 37 | this.height = xmlContact.getAttribute('height'); 38 | } 39 | if (xmlContact.getAttribute('negated') === true) { 40 | this.negated = true; 41 | } 42 | if (xmlContact.getElementsByTagName('variable')[0] !== undefined) { 43 | this.variable = xmlContact.getElementsByTagName('variable')[0].innerHTML; 44 | } 45 | if (xmlContact.getElementsByTagName('position')[0] !== undefined) { 46 | const position = xmlContact.getElementsByTagName('position')[0]; 47 | this.position = { x: position.getAttribute('x'), y: position.getAttribute('y')}; 48 | } 49 | if ( xmlContact.getElementsByTagName('connectionPointIn')[0] !== undefined) { 50 | const connectionPointIn = xmlContact.getElementsByTagName('connectionPointIn')[0]; 51 | if (connectionPointIn.getElementsByTagName('relPosition') !== undefined) { 52 | const position = connectionPointIn.getElementsByTagName('relPosition')[0]; 53 | this.connectionPointIn.x = position.getAttribute('x'); 54 | this.connectionPointIn.y = position.getAttribute('y'); 55 | } 56 | if (connectionPointIn.getElementsByTagName('connection')[0] !== undefined) { 57 | const connection = connectionPointIn.getElementsByTagName('connection')[0]; 58 | this.connectionPointIn.refLocalID = connection.getAttribute('refLocalID'); 59 | } 60 | } 61 | if ( xmlContact.getElementsByTagName('connectionPointOut')[0] !== undefined) { 62 | const connectionPointOut = xmlContact.getElementsByTagName('connectionPointOut')[0]; 63 | if (connectionPointOut.getElementsByTagName('relPosition') !== undefined) { 64 | const position = connectionPointOut.getElementsByTagName('relPosition')[0]; 65 | this.connectionPointOut.x = position.getAttribute('x'); 66 | this.connectionPointOut.y = position.getAttribute('y'); 67 | } 68 | if (connectionPointOut.getElementsByTagName('connection')[0] !== undefined) { 69 | const connection = connectionPointOut.getElementsByTagName('connection')[0]; 70 | this.connectionPointOut.refLocalID = connection.getAttribute('refLocalId'); 71 | } 72 | } 73 | } 74 | 75 | // values that are relevant for illustration are written into nodes 76 | this.node.id = this.localId; 77 | this.node.label = this.variable; 78 | this.node.type = 'contact'; 79 | const newConnectionPointOut: ConnectionPoint = { 80 | type: 'OUT', 81 | sourceId: this.localId, 82 | targetId: this.connectionPointOut.refLocalID, 83 | edgeId: null, 84 | }; 85 | this.node.connectionPoints.push(newConnectionPointOut); 86 | 87 | const newConnectionPointIn: ConnectionPoint = { 88 | type: 'IN', 89 | sourceId: this.connectionPointIn.refLocalID, 90 | targetId: this.localId, 91 | edgeId: null 92 | }; 93 | this.node.connectionPoints.push(newConnectionPointIn); 94 | } 95 | 96 | // creates a default xml-file for the object 97 | createNewContact(): void { 98 | const xmlString = ' \n' + 99 | ' \n' + 100 | ' \n' + 101 | ' \n' + 102 | ' \n' + 103 | ' \n' + 104 | ' \n' + 105 | ' \n' + 106 | ' \n' + 107 | ' '; 108 | const parser = new DOMParser(); 109 | this.xml = parser.parseFromString(xmlString, 'application/xml'); 110 | this.xml = this.xml.getElementsByTagName('contact')[0]; 111 | } 112 | 113 | // updates attributes of position 114 | updatePosition(xPos: number, yPos: number): void { 115 | this.xml.getElementsByTagName('position')[0].setAttribute('x', xPos); 116 | this.xml.getElementsByTagName('position')[0].setAttribute('y', yPos); 117 | } 118 | 119 | // updates relevant attributes 120 | updateAttributes(localId: number, negated: string): void{ 121 | this.xml.setAttribute('localId', localId); 122 | this.xml.setAttribute('negated', negated); 123 | } 124 | 125 | // updates refId of ConnectionPointIn 126 | change_refid(newRef): void { 127 | this.xml.getElementsByTagName('connectionPointIn')[0].getElementsByTagName('connection')[0].setAttribute('refLocalId', newRef); 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /SOURCE/src/app/models/sfcObjects/sfcSelectionDivergence.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @Filename : sfcSelectionDivergence.ts 3 | * 4 | * @Author : Franziska Kopp 5 | * 6 | * @Last_Modified : 19.05.2021 7 | * 8 | */ 9 | 10 | import {ConnectionPoint, PLCNode} from '../PLCNode'; 11 | 12 | export class SfcSelectionDivergence{ 13 | public xml: any; 14 | public localId: string; 15 | public globalId: number; 16 | public height = 20; 17 | public width = 20; 18 | public connectionPointIn: {x: number, y: number, refLocalId: string, formalParameter: string} [] = []; 19 | public connectionPointOut: {x: number, y: number, refLocalId: string, formalParameter: string} [] = []; 20 | public position: {x: 0, y: 0}; 21 | public node: PLCNode = {id: null, label: null, type: null, connectionPoints: null}; 22 | 23 | // check if imported xml ist empty, then create xml, otherwise reads relevant values of xml- file 24 | constructor(xmlSelDivergence: any) { 25 | if (xmlSelDivergence === '') { 26 | this.createXML(); 27 | } else { 28 | this.xml = xmlSelDivergence; 29 | if (xmlSelDivergence.getAttribute('localId') !== undefined) { 30 | this.localId = xmlSelDivergence.getAttribute('localId'); 31 | } 32 | if (xmlSelDivergence.getAttribute('globalId') !== undefined) { 33 | this.globalId = xmlSelDivergence.getAttribute('globalId'); 34 | } 35 | if (xmlSelDivergence.getAttribute('height') !== undefined) { 36 | this.height = xmlSelDivergence.getAttribute('height'); 37 | } 38 | if (xmlSelDivergence.getAttribute('width') !== undefined) { 39 | this.width = xmlSelDivergence.getAttribute('width'); 40 | } 41 | for (const connectionPointIn of xmlSelDivergence.getElementsByTagName('connectionPointIn')){ 42 | const newConnectionPoint = {x: 0, y: 0, refLocalId: '', formalParameter: ''}; 43 | if (connectionPointIn.getElementsByTagName('relPosition') !== undefined) { 44 | const position = connectionPointIn.getElementsByTagName('relPosition')[0]; 45 | newConnectionPoint.x = position.getAttribute('x'); 46 | newConnectionPoint.y = position.getAttribute('y'); 47 | } 48 | if (connectionPointIn.getElementsByTagName('connection')[0] !== undefined) { 49 | const connection = connectionPointIn.getElementsByTagName('connection')[0]; 50 | newConnectionPoint.refLocalId = connection.getAttribute('refLocalId'); 51 | newConnectionPoint.formalParameter = connection.getAttribute('formalParameter'); 52 | } 53 | this.connectionPointIn.push(newConnectionPoint); 54 | } 55 | for (const connectionPointOut of xmlSelDivergence.getElementsByTagName('connectionPointOut')) 56 | { 57 | const newConnectionPoint = {x: 0, y: 0, refLocalId: '', formalParameter: ''}; 58 | if (connectionPointOut.getElementsByTagName('relPosition') !== undefined) { 59 | const position = connectionPointOut.getElementsByTagName('relPosition')[0]; 60 | newConnectionPoint.x = position.getAttribute('x'); 61 | newConnectionPoint.y = position.getAttribute('y'); 62 | } 63 | if (connectionPointOut.getElementsByTagName('connection')[0] !== undefined) { 64 | const connection = connectionPointOut.getElementsByTagName('connection')[0]; 65 | newConnectionPoint.refLocalId = connection.getAttribute('refLocalId'); 66 | newConnectionPoint.formalParameter = connection.getAttribute('formalParameter'); 67 | } 68 | this.connectionPointIn.push(newConnectionPoint); 69 | } 70 | if (xmlSelDivergence.getElementsByTagName('position') !== undefined) { 71 | const position = xmlSelDivergence.getElementsByTagName('position')[0]; 72 | this.position = {x: position.getAttribute('x'), y: position.getAttribute('y')}; 73 | } 74 | } 75 | // values that are relevant for illustration are written into nodes 76 | this.node.id = this.localId; 77 | this.node.type = 'default'; 78 | for (let i = 0; i < this.connectionPointIn.length; i++){ 79 | const newConnectionPointIn: ConnectionPoint = { 80 | type: 'IN', 81 | sourceId: this.connectionPointIn[i].refLocalId, 82 | targetId: this.localId, 83 | edgeId: null 84 | }; 85 | this.node.connectionPoints.push(newConnectionPointIn); 86 | } 87 | for (let i = 0; i < this.connectionPointOut.length; i++){ 88 | const newConnectionPointOut: ConnectionPoint = { 89 | type: 'OUT', 90 | sourceId: this.connectionPointOut[i].refLocalId, 91 | targetId: this.localId, 92 | edgeId: null 93 | }; 94 | this.node.connectionPoints.push(newConnectionPointOut); 95 | } 96 | } 97 | // creates a default xml-file for the object 98 | createXML(): void { 99 | const xmlString = '\n' + 100 | '\n' + 101 | ' \n' + 102 | ' \n' + 103 | '\n' + 104 | '\n' + 105 | '\n' + 106 | ' \n' + 107 | ' \n'; 108 | const parser = new DOMParser(); 109 | this.xml = parser.parseFromString(xmlString, 'application/xml').getElementsByTagName('selectionDivergence')[0]; 110 | } 111 | // updates attributes of position 112 | updatePosition(xPos: number, yPos: number): void { 113 | this.xml.getElementsByTagName('position')[0].setAttribute('x', xPos); 114 | this.xml.getElementsByTagName('position')[0].setAttribute('y', yPos); 115 | } 116 | // updates relevant attributes 117 | updateAttributes(localId: number): void{ 118 | this.xml.setAttribute('localId', localId); 119 | } 120 | // updates refId of ConnectionPointIn 121 | change_refid(newRef): void { 122 | this.xml.getElementsByTagName('connectionPointIn')[0].getElementsByTagName('connection')[0].setAttribute('refLocalId', newRef); 123 | } 124 | } 125 | 126 | 127 | -------------------------------------------------------------------------------- /SOURCE/src/app/models/sfcObjects/sfcSelectionConvergence.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @Filename : sfcSelectionConvergence.ts 3 | * 4 | * @Author : Franziska Kopp 5 | * 6 | * @Last_Modified : 19.05.2021 7 | * 8 | */ 9 | 10 | import {ConnectionPoint, PLCNode} from '../PLCNode'; 11 | 12 | export class SfcSelectionConvergence { 13 | public xml: any; 14 | public localId: string; 15 | public globalId: number; 16 | public height = 20; 17 | public width = 20; 18 | public connectionPointIn: { x: number, y: number, refLocalId: string, formalParameter: string }[] = []; 19 | public connectionPointOut: { x: number, y: number, refLocalId: string, formalParameter: string }[] = []; 20 | public position: { x: 0, y: 0 }; 21 | public node: PLCNode = {id: null, label: null, type: null, connectionPoints: null}; 22 | 23 | // check if imported xml ist empty, then create xml, otherwise reads relevant values of xml- file 24 | constructor(xmlSelConvergence: any) { 25 | if (xmlSelConvergence === '') { 26 | this.createXML(); 27 | } else { 28 | this.xml = xmlSelConvergence; 29 | if (xmlSelConvergence.getAttribute('localId') !== undefined) { 30 | this.localId = xmlSelConvergence.getAttribute('localId'); 31 | } 32 | if (xmlSelConvergence.getAttribute('globalId') !== undefined) { 33 | this.globalId = xmlSelConvergence.getAttribute('globalId'); 34 | } 35 | if (xmlSelConvergence.getAttribute('height') !== undefined) { 36 | this.height = xmlSelConvergence.getAttribute('height'); 37 | } 38 | if (xmlSelConvergence.getAttribute('width') !== undefined) { 39 | this.width = xmlSelConvergence.getAttribute('width'); 40 | } 41 | for (const connectionPointIn of xmlSelConvergence.getElementsByTagName('connectionPointIn')) { 42 | const newConnectionPoint = {x: 0, y: 0, refLocalId: '', formalParameter: ''}; 43 | if (connectionPointIn.getElementsByTagName('relPosition') !== undefined) { 44 | const position = connectionPointIn.getElementsByTagName('relPosition')[0]; 45 | newConnectionPoint.x = position.getAttribute('x'); 46 | newConnectionPoint.y = position.getAttribute('y'); 47 | } 48 | if (connectionPointIn.getElementsByTagName('connection')[0] !== undefined) { 49 | const connection = connectionPointIn.getElementsByTagName('connection')[0]; 50 | newConnectionPoint.refLocalId = connection.getAttribute('refLocalId'); 51 | newConnectionPoint.formalParameter = connection.getAttribute('formalParameter'); 52 | } 53 | this.connectionPointIn.push(newConnectionPoint); 54 | } 55 | for (const connectionPointOut of xmlSelConvergence.getElementsByTagName('connectionPointOut')) 56 | { 57 | const newConnectionPoint = {x: 0, y: 0, refLocalId: '', formalParameter: ''}; 58 | if (connectionPointOut.getElementsByTagName('relPosition') !== undefined) { 59 | const position = connectionPointOut.getElementsByTagName('relPosition')[0]; 60 | newConnectionPoint.x = position.getAttribute('x'); 61 | newConnectionPoint.y = position.getAttribute('y'); 62 | } 63 | if (connectionPointOut.getElementsByTagName('connection')[0] !== undefined) { 64 | const connection = connectionPointOut.getElementsByTagName('connection')[0]; 65 | newConnectionPoint.refLocalId = connection.getAttribute('refLocalId'); 66 | newConnectionPoint.formalParameter = connection.getAttribute('formalParameter'); 67 | } 68 | this.connectionPointIn.push(newConnectionPoint); 69 | } 70 | if (xmlSelConvergence.getElementsByTagName('position') !== undefined) { 71 | const position = xmlSelConvergence.getElementsByTagName('position')[0]; 72 | this.position = {x: position.getAttribute('x'), y: position.getAttribute('y')}; 73 | } 74 | } 75 | // values that are relevant for illustration are written into nodes 76 | this.node.id = this.localId; 77 | this.node.type = 'default'; 78 | for (let i = 0; i < this.connectionPointIn.length; i++){ 79 | const newConnectionPointIn: ConnectionPoint = { 80 | type: 'IN', 81 | sourceId: this.connectionPointIn[i].refLocalId, 82 | targetId: this.localId, 83 | edgeId: null 84 | }; 85 | this.node.connectionPoints.push(newConnectionPointIn); 86 | } 87 | for (let i = 0; i < this.connectionPointOut.length; i++){ 88 | const newConnectionPointOut: ConnectionPoint = { 89 | type: 'OUT', 90 | sourceId: this.connectionPointOut[i].refLocalId, 91 | targetId: this.localId, 92 | edgeId: null 93 | }; 94 | this.node.connectionPoints.push(newConnectionPointOut); 95 | } 96 | } 97 | // creates a default xml-file for the object 98 | createXML(): void { 99 | const xmlString = '\n' + 100 | '\n' + 101 | ' \n' + 102 | ' \n' + 103 | '\n' + 104 | '\n' + 105 | '\n' + 106 | ' \n' + 107 | ' \n'; 108 | const parser = new DOMParser(); 109 | this.xml = parser.parseFromString(xmlString, 'application/xml').getElementsByTagName('selectionConvergence')[0]; 110 | } 111 | // updates attributes of position 112 | updatePosition(xPos: number, yPos: number): void { 113 | this.xml.getElementsByTagName('position')[0].setAttribute('x', xPos); 114 | this.xml.getElementsByTagName('position')[0].setAttribute('y', yPos); 115 | } 116 | // updates relevant attributes 117 | updateAttributes(localId: number): void{ 118 | this.xml.setAttribute('localId', localId); 119 | } 120 | // updates refId of ConnectionPointIn 121 | change_refid(newRef): void { 122 | this.xml.getElementsByTagName('connectionPointIn')[0].getElementsByTagName('connection')[0].setAttribute('refLocalId', newRef); 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /SOURCE/src/app/models/sfcObjects/sfcSimultaneousConvergence.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @Filename : sfcSimultaneousConvergence.ts 3 | * 4 | * @Author : Franziska Kopp 5 | * 6 | * @Last_Modified : 19.05.2021 7 | * 8 | */ 9 | 10 | import {ConnectionPoint, PLCNode} from '../PLCNode'; 11 | 12 | export class SfcSimultaneousConvergence{ 13 | public xml: any; 14 | public localId: string; 15 | public globalId: number; 16 | public height = 20; 17 | public width = 20; 18 | public connectionPointIn: { x: number, y: number, refLocalId: string, formalParameter: string }[] = []; 19 | public connectionPointOut: { x: number, y: number, refLocalId: string, formalParameter: string }[] = []; 20 | public position: {x: 0, y: 0}; 21 | public node: PLCNode = {id: null, label: null, type: null, connectionPoints: null}; 22 | 23 | // check if imported xml ist empty, then create xml, otherwise reads relevant values of xml- file 24 | constructor(xmlSimConvergence: any) { 25 | if (xmlSimConvergence === '') { 26 | this.createXML(); 27 | } else { 28 | this.xml = xmlSimConvergence; 29 | if (xmlSimConvergence.getAttribute('localId') !== undefined) { 30 | this.localId = xmlSimConvergence.getAttribute('localId'); 31 | } 32 | if (xmlSimConvergence.getAttribute('globalId') !== undefined) { 33 | this.globalId = xmlSimConvergence.getAttribute('globalId'); 34 | } 35 | if (xmlSimConvergence.getAttribute('height') !== undefined) { 36 | this.height = xmlSimConvergence.getAttribute('height'); 37 | } 38 | if (xmlSimConvergence.getAttribute('width') !== undefined) { 39 | this.width = xmlSimConvergence.getAttribute('width'); 40 | } 41 | for (const connectionPointIn of xmlSimConvergence.getElementsByTagName('connectionPointIn')) { 42 | const newConnectionPoint = {x: 0, y: 0, refLocalId: '', formalParameter: ''}; 43 | if (connectionPointIn.getElementsByTagName('relPosition') !== undefined) { 44 | const position = connectionPointIn.getElementsByTagName('relPosition')[0]; 45 | newConnectionPoint.x = position.getAttribute('x'); 46 | newConnectionPoint.y = position.getAttribute('y'); 47 | } 48 | if (connectionPointIn.getElementsByTagName('connection')[0] !== undefined) { 49 | const connection = connectionPointIn.getElementsByTagName('connection')[0]; 50 | newConnectionPoint.refLocalId = connection.getAttribute('refLocalId'); 51 | newConnectionPoint.formalParameter = connection.getAttribute('formalParameter'); 52 | } 53 | this.connectionPointIn.push(newConnectionPoint); 54 | } 55 | for (const connectionPointOut of xmlSimConvergence.getElementsByTagName('connectionPointOut')) 56 | { 57 | const newConnectionPoint = {x: 0, y: 0, refLocalId: '', formalParameter: ''}; 58 | if (connectionPointOut.getElementsByTagName('relPosition') !== undefined) { 59 | const position = connectionPointOut.getElementsByTagName('relPosition')[0]; 60 | newConnectionPoint.x = position.getAttribute('x'); 61 | newConnectionPoint.y = position.getAttribute('y'); 62 | } 63 | if (connectionPointOut.getElementsByTagName('connection')[0] !== undefined) { 64 | const connection = connectionPointOut.getElementsByTagName('connection')[0]; 65 | newConnectionPoint.refLocalId = connection.getAttribute('refLocalId'); 66 | newConnectionPoint.formalParameter = connection.getAttribute('formalParameter'); 67 | } 68 | this.connectionPointIn.push(newConnectionPoint); 69 | } 70 | if (xmlSimConvergence.getElementsByTagName('position') !== undefined) { 71 | const position = xmlSimConvergence.getElementsByTagName('position')[0]; 72 | this.position = {x: position.getAttribute('x'), y: position.getAttribute('y')}; 73 | } 74 | } 75 | // values that are relevant for illustration are written into nodes 76 | this.node.id = this.localId; 77 | this.node.type = 'default'; 78 | for (let i = 0; i < this.connectionPointIn.length; i++){ 79 | const newConnectionPointIn: ConnectionPoint = { 80 | type: 'IN', 81 | sourceId: this.connectionPointIn[i].refLocalId, 82 | targetId: this.localId, 83 | edgeId: null 84 | }; 85 | this.node.connectionPoints.push(newConnectionPointIn); 86 | } 87 | for (let i = 0; i < this.connectionPointOut.length; i++){ 88 | const newConnectionPointOut: ConnectionPoint = { 89 | type: 'OUT', 90 | sourceId: this.connectionPointOut[i].refLocalId, 91 | targetId: this.localId, 92 | edgeId: null 93 | }; 94 | this.node.connectionPoints.push(newConnectionPointOut); 95 | } 96 | } 97 | // creates a default xml-file for the object 98 | createXML(): void { 99 | const xmlString = '\n' + 100 | '\n' + 101 | ' \n' + 102 | ' \n' + 103 | '\n' + 104 | '\n' + 105 | '\n' + 106 | ' \n' + 107 | ' \n'; 108 | const parser = new DOMParser(); 109 | this.xml = parser.parseFromString(xmlString, 'application/xml').getElementsByTagName('simultaneousConvergence')[0]; 110 | } 111 | // updates attributes of position 112 | updatePosition(xPos: number, yPos: number): void { 113 | this.xml.getElementsByTagName('position')[0].setAttribute('x', xPos); 114 | this.xml.getElementsByTagName('position')[0].setAttribute('y', yPos); 115 | } 116 | // updates relevant attributes 117 | updateAttributes(localId: number): void{ 118 | this.xml.setAttribute('localId', localId); 119 | } 120 | // updates refId of ConnectionPointIn 121 | change_refid(newRef): void { 122 | this.xml.getElementsByTagName('connectionPointIn')[0].getElementsByTagName('connection')[0].setAttribute('refLocalId', newRef); 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /SOURCE/src/app/models/sfcObjects/sfcSimultaneousDivergence.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @Filename : sfcSimultaneousDivergence.ts 3 | * 4 | * @Author : Franziska Kopp 5 | * 6 | * @Last_Modified : 19.05.2021 7 | * 8 | */ 9 | 10 | import {ConnectionPoint, PLCNode} from '../PLCNode'; 11 | 12 | export class SfcSimultaneousDivergence{ 13 | public xml: any; 14 | public localId: string; 15 | public globalId: number; 16 | public height = 20; 17 | public width = 20; 18 | public name = ''; 19 | public connectionPointIn: { x: number, y: number, refLocalId: string, formalParameter: string }[] = []; 20 | public connectionPointOut: { x: number, y: number, refLocalId: string, formalParameter: string }[] = []; 21 | public position: {x: 0, y: 0}; 22 | public node: PLCNode = {id: null, label: null, type: null, connectionPoints: null}; 23 | 24 | // check if imported xml ist empty, then create xml, otherwise reads relevant values of xml- file 25 | constructor(xmlSimDivergence: any) { 26 | if (xmlSimDivergence === '') { 27 | this.createXML(); 28 | } else { 29 | this.xml = xmlSimDivergence; 30 | if (xmlSimDivergence.getAttribute('localId') !== undefined) { 31 | this.localId = xmlSimDivergence.getAttribute('localId'); 32 | } 33 | if (xmlSimDivergence.getAttribute('globalId') !== undefined) { 34 | this.globalId = xmlSimDivergence.getAttribute('globalId'); 35 | } 36 | if (xmlSimDivergence.getAttribute('height') !== undefined) { 37 | this.height = xmlSimDivergence.getAttribute('height'); 38 | } 39 | if (xmlSimDivergence.getAttribute('width') !== undefined) { 40 | this.width = xmlSimDivergence.getAttribute('width'); 41 | } 42 | if (xmlSimDivergence.getAttribute('name') !== undefined) { 43 | this.name = xmlSimDivergence.getAttribute('name'); 44 | } 45 | 46 | for (const connectionPointIn of xmlSimDivergence.getElementsByTagName('connectionPointIn')) { 47 | const newConnectionPoint = {x: 0, y: 0, refLocalId: '', formalParameter: ''}; 48 | if (connectionPointIn.getElementsByTagName('relPosition') !== undefined) { 49 | const position = connectionPointIn.getElementsByTagName('relPosition')[0]; 50 | newConnectionPoint.x = position.getAttribute('x'); 51 | newConnectionPoint.y = position.getAttribute('y'); 52 | } 53 | if (connectionPointIn.getElementsByTagName('connection')[0] !== undefined) { 54 | const connection = connectionPointIn.getElementsByTagName('connection')[0]; 55 | newConnectionPoint.refLocalId = connection.getAttribute('refLocalId'); 56 | newConnectionPoint.formalParameter = connection.getAttribute('formalParameter'); 57 | } 58 | this.connectionPointIn.push(newConnectionPoint); 59 | } 60 | for (const connectionPointOut of xmlSimDivergence.getElementsByTagName('connectionPointOut')) 61 | { 62 | const newConnectionPoint = {x: 0, y: 0, refLocalId: '', formalParameter: ''}; 63 | if (connectionPointOut.getElementsByTagName('relPosition') !== undefined) { 64 | const position = connectionPointOut.getElementsByTagName('relPosition')[0]; 65 | newConnectionPoint.x = position.getAttribute('x'); 66 | newConnectionPoint.y = position.getAttribute('y'); 67 | } 68 | if (connectionPointOut.getElementsByTagName('connection')[0] !== undefined) { 69 | const connection = connectionPointOut.getElementsByTagName('connection')[0]; 70 | newConnectionPoint.refLocalId = connection.getAttribute('refLocalId'); 71 | newConnectionPoint.formalParameter = connection.getAttribute('formalParameter'); 72 | } 73 | this.connectionPointIn.push(newConnectionPoint); 74 | } 75 | if (xmlSimDivergence.getElementsByTagName('position') !== undefined) { 76 | const position = xmlSimDivergence.getElementsByTagName('position')[0]; 77 | this.position = {x: position.getAttribute('x'), y: position.getAttribute('y')}; 78 | } 79 | } 80 | // values that are relevant for illustration are written into nodes 81 | this.node.id = this.localId; 82 | this.node.type = 'default'; 83 | this.node.label = this.name; 84 | for (let i = 0; i < this.connectionPointIn.length; i++){ 85 | const newConnectionPointIn: ConnectionPoint = { 86 | type: 'IN', 87 | sourceId: this.connectionPointIn[i].refLocalId, 88 | targetId: this.localId, 89 | edgeId: null 90 | }; 91 | this.node.connectionPoints.push(newConnectionPointIn); 92 | } 93 | for (let i = 0; i < this.connectionPointOut.length; i++){ 94 | const newConnectionPointOut: ConnectionPoint = { 95 | type: 'OUT', 96 | sourceId: this.connectionPointOut[i].refLocalId, 97 | targetId: this.localId, 98 | edgeId: null 99 | }; 100 | this.node.connectionPoints.push(newConnectionPointOut); 101 | } 102 | } 103 | // creates a default xml-file for the object 104 | createXML(): void { 105 | const xmlString = '\n' + 106 | '\n' + 107 | ' \n' + 108 | ' \n' + 109 | '\n' + 110 | '\n' + 111 | '\n' + 112 | ' \n' + 113 | ' \n'; 114 | const parser = new DOMParser(); 115 | this.xml = parser.parseFromString(xmlString, 'application/xml').getElementsByTagName('simultaneousDivergence')[0]; 116 | } 117 | // updates attributes of position 118 | updatePosition(xPos: number, yPos: number): void { 119 | this.xml.getElementsByTagName('position')[0].setAttribute('x', xPos); 120 | this.xml.getElementsByTagName('position')[0].setAttribute('y', yPos); 121 | } 122 | // updates relevant attributes 123 | updateAttributes(localId: number, name: string): void{ 124 | this.xml.setAttribute('localId', localId); 125 | this.xml.setAttribute('name', name); 126 | } 127 | // updates refId of ConnectionPointIn 128 | change_refid(newRef): void { 129 | this.xml.getElementsByTagName('connectionPointIn')[0].getElementsByTagName('connection')[0].setAttribute('refLocalId', newRef); 130 | } 131 | } 132 | --------------------------------------------------------------------------------