Skip to content

Commit

Permalink
fix: reading PxC API
Browse files Browse the repository at this point in the history
  • Loading branch information
ralfaron committed Oct 26, 2024
1 parent 6a5ccd5 commit cafb6e5
Show file tree
Hide file tree
Showing 29 changed files with 370 additions and 188 deletions.
2 changes: 1 addition & 1 deletion .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@
"name": "Test aas-portal",
"type": "chrome",
"request": "launch",
"preLaunchTask": "test",
"preLaunchTask": "test-aas-portal",
"url": "http://localhost:9876/debug.html",
},
{
Expand Down
2 changes: 1 addition & 1 deletion .vscode/tasks.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
},
{
"type": "shell",
"label": "test",
"label": "test-aas-portal",
"command": "npm",
"args": ["run", "test", "-w", "aas-portal"],
"isBackground": true,
Expand Down
2 changes: 1 addition & 1 deletion projects/aas-core/src/lib/aas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ export type ModelType =
| 'Blob'
| 'Capability'
| 'ConceptDescription'
| 'DataSpecificationIec61360'
| 'DataSpecificationIEC61360'
| 'Entity'
| 'File'
| 'MultiLanguageProperty'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,39 +42,45 @@ <h4 class="modal-title">
(input)="inputValue()">
</div>
@if (selectedItem().type === 'AAS_API') {
<div class="col-12 mt-2" style="max-height: 200px; overflow-y: auto;">
<table class="table table-sm table-striped table-borderless">
<thead>
<tr>
<th scope="col" class="text-center" [style.width]="'32px'">#</th>
<th scope="col" class="text-center">Header</th>
<th scope="col" class="text-center">Value</th>
</tr>
</thead>
<tbody>
@for (header of headers(); track header.id) {
<tr>
<th scope="row" (click)="selectHeader(header)">{{header.id}}</th>
@if (header === selectedHeader()) {
<td>
<input type="text" class="form-control form-control-sm" [(ngModel)]="header.name"
placeholder="header name" name="headerName" />
</td>
<td>
<input type="text" class="form-control form-control-sm" [(ngModel)]="header.value"
placeholder="value" name="headerValue" />
</td>
} @else {
<td (click)="selectHeader(header)">
<div class="small">{{header.name}}</div>
</td>
<td (click)="selectHeader(header)">
<div class="small">{{header.value}}</div>
</td>
}
}
</tbody>
</table>
<button type="button" class="btn btn-primary my-2" (click)="collapse.toggle()"
[attr.aria-expanded]="isCollapsed() === false" aria-controls="collapseExample" translate>
AddEndpointForm.ADVANCED_SETTINGS
</button>
<div #collapse="ngbCollapse" [(ngbCollapse)]="isCollapsed">
<div class="col-12 mt-2" style="max-height: 200px; overflow-y: auto;">
<table class="table table-sm table-striped table-borderless">
<thead>
<tr>
<th scope="col" class="text-center" [style.width]="'32px'">#</th>
<th scope="col" class="text-center">Header</th>
<th scope="col" class="text-center">Value</th>
</tr>
</thead>
<tbody>
@for (header of headers(); track header.id) {
<tr>
<th scope="row" (click)="selectHeader(header)">{{header.id}}</th>
@if (header === selectedHeader()) {
<td>
<input type="text" class="form-control form-control-sm" [(ngModel)]="header.name"
placeholder="header name" name="headerName" />
</td>
<td>
<input type="text" class="form-control form-control-sm" [(ngModel)]="header.value"
placeholder="value" name="headerValue" />
</td>
} @else {
<td (click)="selectHeader(header)">
<div class="small">{{header.name}}</div>
</td>
<td (click)="selectHeader(header)">
<div class="small">{{header.value}}</div>
</td>
}
}
</tbody>
</table>
</div>
</div>
}
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

import { ChangeDetectionStrategy, Component, computed, signal } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { NgbActiveModal, NgbDropdownModule, NgbToast } from '@ng-bootstrap/ng-bootstrap';
import { NgbActiveModal, NgbCollapse, NgbDropdownModule, NgbToast } from '@ng-bootstrap/ng-bootstrap';
import { TranslateModule, TranslateService } from '@ngx-translate/core';
import { AASEndpoint, AASEndpointType, stringFormat } from 'aas-core';

Expand All @@ -29,7 +29,7 @@ export interface EndpointItem {
templateUrl: './add-endpoint-form.component.html',
styleUrls: ['./add-endpoint-form.component.scss'],
standalone: true,
imports: [NgbToast, NgbDropdownModule, TranslateModule, FormsModule],
imports: [NgbToast, NgbDropdownModule, NgbCollapse, TranslateModule, FormsModule],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class AddEndpointFormComponent {
Expand Down Expand Up @@ -60,11 +60,7 @@ export class AddEndpointFormComponent {
]);

private readonly _selectedHeaderIndex = signal(-1);
private readonly _headers = signal<HeaderItem[]>([
{ id: '1', name: '', value: '' },
{ id: '2', name: '', value: '' },
{ id: '3', name: '', value: '' },
]);
private readonly _headers = signal<HeaderItem[]>([{ id: '1', name: '', value: '' }]);

public constructor(
private modal: NgbActiveModal,
Expand All @@ -81,6 +77,8 @@ export class AddEndpointFormComponent {

public readonly headers = this._headers.asReadonly();

public readonly isCollapsed = signal(true);

public readonly selectedHeader = computed(() =>
this._selectedHeaderIndex() < 0 ? undefined : this._headers()[this._selectedHeaderIndex()],
);
Expand All @@ -98,6 +96,7 @@ export class AddEndpointFormComponent {
this._selectedHeaderIndex.update(state => {
const index = this._headers().indexOf(value);
if (state >= 0 && index === state) {
this.updateHeaders();
return -1;
}

Expand Down Expand Up @@ -192,7 +191,6 @@ export class AddEndpointFormComponent {
return url;
} catch (error) {
this._messages.update(messages => [...messages, this.createMessage('ERROR_INVALID_URL', value)]);

return undefined;
}
}
Expand All @@ -205,6 +203,10 @@ export class AddEndpointFormComponent {
if (url.protocol !== 'http:' && url.protocol !== 'https:') {
throw new Error('Protocol "http:" or "https:" expected.');
}

if (!url.hostname) {
throw new Error('Empty host name.');
}
}

private validateFileSystemEndpoint(url: URL): void {
Expand All @@ -226,8 +228,8 @@ export class AddEndpointFormComponent {
throw new Error('Protocol "opc.tcp:" expected.');
}

if (url.pathname === '//' || url.pathname === '/') {
throw new Error('Empty pathname.');
if (!url.hostname) {
throw new Error('Empty host name.');
}
}

Expand All @@ -236,8 +238,23 @@ export class AddEndpointFormComponent {
throw new Error('Protocol "http:" or "https:" expected.');
}

if (url.pathname === '/') {
throw new Error('Empty pathname.');
if (!url.hostname) {
throw new Error('Empty host name.');
}
}

private updateHeaders(): void {
this._headers.update(state => {
let index = 1;
const items: HeaderItem[] = [];
for (const item of state) {
if (item.name || item.value) {
items.push({ ...item, id: String(index++) });
}
}

items.push({ id: String(index++), name: '', value: '' });
return items;
});
}
}
3 changes: 2 additions & 1 deletion projects/aas-portal/src/assets/i18n/de-de.json
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,8 @@
"PLACEHOLDER_URL_FILE": "file:///",
"PLACEHOLDER_URL_HTTP": "http(s)://",
"PLACEHOLDER_URL_OPCUA": "opc.tcp://",
"PLACEHOLDER_URL_WEBDAV": "http(s)://"
"PLACEHOLDER_URL_WEBDAV": "http(s)://",
"ADVANCED_SETTINGS": "Erweiterte Einstellungen"
},
"CustomerFeedback": {
"OverallRating": "Gesamtbewertung",
Expand Down
3 changes: 2 additions & 1 deletion projects/aas-portal/src/assets/i18n/en-us.json
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,8 @@
"PLACEHOLDER_URL_FILE": "file:///",
"PLACEHOLDER_URL_HTTP": "http(s)://",
"PLACEHOLDER_URL_OPCUA": "opc.tcp://",
"PLACEHOLDER_URL_WEBDAV": "http(s)://"
"PLACEHOLDER_URL_WEBDAV": "http(s)://",
"ADVANCED_SETTINGS": "Advanced settings"
},
"CustomerFeedback": {
"OverallRating": "Overall rating",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,6 @@ import { AddEndpointFormComponent } from '../../app/start/add-endpoint-form/add-
describe('AddEndpointFormComponent', () => {
let component: AddEndpointFormComponent;
let fixture: ComponentFixture<AddEndpointFormComponent>;
let inputNameElement: HTMLInputElement;
let inputUrlElement: HTMLInputElement;
let modal: NgbActiveModal;
let form: HTMLFormElement;

Expand All @@ -39,8 +37,6 @@ describe('AddEndpointFormComponent', () => {
fixture.detectChanges();

form = fixture.debugElement.nativeElement.querySelector('form');
inputNameElement = fixture.debugElement.nativeElement.querySelector('#input-endpoint-name');
inputUrlElement = fixture.debugElement.nativeElement.querySelector('#input-endpoint-url');
});

it('should create', () => {
Expand All @@ -53,6 +49,7 @@ describe('AddEndpointFormComponent', () => {

component.selectItem(component.items()[3]);
component.name.set('My endpoint');
component.selectedItem().value = 'file:///my-endpoint';

form.dispatchEvent(new Event('submit'));
expect(modal.close).toHaveBeenCalled();
Expand All @@ -67,6 +64,7 @@ describe('AddEndpointFormComponent', () => {

component.selectItem(component.items()[3]);
component.name.set('My endpoint');
component.selectedItem().value = 'file:///a\\b\\my-endpoint';

form.dispatchEvent(new Event('submit'));
expect(modal.close).toHaveBeenCalled();
Expand All @@ -80,6 +78,7 @@ describe('AddEndpointFormComponent', () => {

component.selectItem(component.items()[3]);
component.name.set('');
component.selectedItem().value = 'file:///my-endpoint';

form.dispatchEvent(new Event('submit'));
expect(modal.close).toHaveBeenCalledTimes(0);
Expand All @@ -91,6 +90,7 @@ describe('AddEndpointFormComponent', () => {

component.selectItem(component.items()[3]);
component.name.set('My endpoint');
component.selectedItem().value = 'file:///';

form.dispatchEvent(new Event('submit'));
expect(modal.close).toHaveBeenCalledTimes(0);
Expand All @@ -103,6 +103,7 @@ describe('AddEndpointFormComponent', () => {

component.selectItem(component.items()[1]);
component.name.set('I4AAS Server');
component.selectedItem().value = 'opc.tcp://localhost:30001/I4AASServer';

form.dispatchEvent(new Event('submit'));
expect(modal.close).toHaveBeenCalled();
Expand All @@ -115,12 +116,8 @@ describe('AddEndpointFormComponent', () => {
spyOn(modal, 'close');

component.selectItem(component.items()[1]);
fixture.detectChanges();

inputNameElement.value = 'I4AAS Server';
inputNameElement.dispatchEvent(new Event('input'));
inputUrlElement.value = 'opc.tcp://';
inputUrlElement.dispatchEvent(new Event('input'));
component.name.set('I4AAS Server');
component.selectedItem().value = 'opc.tcp://';

form.dispatchEvent(new Event('submit'));
expect(modal.close).toHaveBeenCalledTimes(0);
Expand All @@ -133,6 +130,7 @@ describe('AddEndpointFormComponent', () => {

component.selectItem(component.items()[0]);
component.name.set('AASX Server');
component.selectedItem().value = 'http://localhost:50001/';

form.dispatchEvent(new Event('submit'));
expect(modal.close).toHaveBeenCalled();
Expand All @@ -147,6 +145,7 @@ describe('AddEndpointFormComponent', () => {

component.selectItem(component.items()[2]);
component.name.set('WebDAV Server');
component.selectedItem().value = 'http://localhost:8080/root/folder';

form.dispatchEvent(new Event('submit'));
expect(modal.close).toHaveBeenCalled();
Expand Down
6 changes: 4 additions & 2 deletions projects/aas-server/src/app/aas-index/aas-index-factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,18 +17,20 @@ import { LowDbData } from './lowdb/lowdb-types.js';
import { MySqlIndex } from './mysql/mysql-index.js';
import { Logger } from '../logging/logger.js';
import { urlToString } from '../convert.js';
import { KeywordDirectory } from './keyword-directory.js';

export class AASIndexFactory {
public constructor(private readonly container: DependencyContainer) {}

public create(): AASIndex {
const variable = this.container.resolve(Variable);
const logger = this.container.resolve<Logger>('Logger');
const keywordDirectory = this.container.resolve(KeywordDirectory);
if (variable.AAS_INDEX) {
try {
const url = new URL(variable.AAS_INDEX);
if (url.protocol === 'mysql:') {
const index = new MySqlIndex(variable);
const index = new MySqlIndex(keywordDirectory, variable);
logger.info(`AAS index connected to ${urlToString(url)}.`);
return index;
}
Expand All @@ -41,7 +43,7 @@ export class AASIndexFactory {

const dbFile = path.join(variable.CONTENT_ROOT, 'db.json');
const db = new Low<LowDbData>(new JSONFile(dbFile), { documents: [], endpoints: [], elements: [] });
const index = new LowDbIndex(db, variable);
const index = new LowDbIndex(db, keywordDirectory, variable);
logger.info('Using internal AAS index.');
return index;
}
Expand Down
Loading

0 comments on commit cafb6e5

Please sign in to comment.