Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# External Control URCap

This URCap allows you to send script code to the robot, and is primarily meant for connecting the robot to the Universal ROS/ROS2 driver, Isaac Sim driver, and the Universal Robots Client Library.
This URCap allows you to inject script code to the robot, and is primarily meant for connecting the robot to the Universal ROS/ROS 2 driver and the Universal Robots Client Library.


## Build and Deploy Sample
Expand Down
1 change: 1 addition & 0 deletions external-control-frontend/.eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
],
"rules": {
"@typescript-eslint/no-unused-vars": "off",
"@typescript-eslint/no-explicit-any": "off",
"@angular-eslint/component-selector": [
"error",
{
Expand Down
5,159 changes: 1,550 additions & 3,609 deletions external-control-frontend/package-lock.json

Large diffs are not rendered by default.

11 changes: 6 additions & 5 deletions external-control-frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
"prebuild": "npm run generatepath",
"build": "ng build --configuration=production",
"lint": "ng lint",
"test": "ng test",
"generatepath": "generate-manifest-constants ../manifest.yaml"
},
"private": true,
Expand Down Expand Up @@ -40,11 +41,11 @@
"@types/jasmine": "~5.1.0",
"@typescript-eslint/eslint-plugin": "6.19.0",
"@typescript-eslint/parser": "6.19.0",
"@universal-robots/contribution-api": "32.1.7",
"@universal-robots/urcap-utils": "1.0.5",
"@universal-robots/utilities-units": "5.6.1",
"@universal-robots/designtokens": "0.10.0",
"@universal-robots/ui-models": "0.18.4",
"@universal-robots/contribution-api": "38.1.2",
"@universal-robots/urcap-utils": "1.1.0",
"@universal-robots/utilities-units": "5.7.1",
"@universal-robots/designtokens": "0.11.0",
"@universal-robots/ui-models": "0.23.0",
"eslint": "8.48.0",
"jasmine-core": "~5.1.0",
"karma": "~6.4.0",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@ import {
import { ExternalControlApplicationNode } from './external-control-application.node';

// factory is required
const createApplicationNode = (): OptionalPromise<ExternalControlApplicationNode> => ({
export const createApplicationNode = (): OptionalPromise<ExternalControlApplicationNode> => ({
type: 'universal-robots-external-control-external-control-application', // type is required
version: '1.0.0', // version is required
port: 50002,
robotIP: 'servicegateway'
robotIP: '192.168.56.1' //the hostname of the internal daemon docker is "servicegateway"
});

// generatePreamble is optional
Expand All @@ -23,13 +23,13 @@ const generatePreambleScriptCode = (node: ExternalControlApplicationNode): Optio

// upgradeNode is optional
const upgradeApplicationNode
= (loadedNode: ApplicationNode, defaultNode: ExternalControlApplicationNode): ExternalControlApplicationNode =>
defaultNode;
= (loadedNode: ApplicationNode, defaultNode: ExternalControlApplicationNode): ExternalControlApplicationNode =>
defaultNode;

// downgradeNode is optional
const downgradeApplicationNode
= (loadedNode: ApplicationNode, defaultNode: ExternalControlApplicationNode): ExternalControlApplicationNode =>
defaultNode;
= (loadedNode: ApplicationNode, defaultNode: ExternalControlApplicationNode): ExternalControlApplicationNode =>
defaultNode;

const behaviors: ApplicationBehaviors = {
factory: createApplicationNode,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,27 +1,33 @@
<div *ngIf="applicationNode" class="main-section">
<h1 class="header" [textContent]="baseTranslationKey + '.title' | translate"></h1>
<p class="info-text" [textContent]="baseTranslationKey + '.description.line1' | translate"></p>
<p class="info-text" [textContent]="baseTranslationKey + '.description.line2' | translate"></p>

<h2 class="sub-header" [textContent]="baseTranslationKey + '.settings.header' | translate"></h2>
<p class="info-text" [textContent]="baseTranslationKey + '.settings.description' | translate"></p>
<div class="flex-container">
<div>
<div class="input-ip">
<ur-dialog-input [type]="'text'" [label]="'presenter.robotIP-input-description' | translate"
[value]="applicationNode.robotIP" (valueChanged)="saveNode()" #robotIP></ur-dialog-input>
[value]="applicationNode.robotIP" (valueChanged)="writeParam(port.value, robotIP.value)"
#robotIP></ur-dialog-input>
</div>
<div>
<ur-label [label]="'&nbsp;'" [required]="false" [size]="'large'"></ur-label>
</div>
<div>
<ur-label [label]="'&nbsp;'" [required]="false" [size]="'large'"></ur-label>
</div>
<div>
<div class="input-port">
<ur-dialog-input [type]="'number'" [label]="'presenter.port-input-description' | translate"
[value]="applicationNode.port" (valueChanged)="saveNode()" #port></ur-dialog-input>
</div>
<div>
<ur-label [label]="'&nbsp;'" [required]="false" [size]="'large'"></ur-label>
</div>
<div>
<ur-label [label]="'&nbsp;&nbsp;'" [required]="false" [size]="'large'"></ur-label>
[value]="applicationNode.port" (valueChanged)="writeParam(port.value, robotIP.value)"
#port></ur-dialog-input>
</div>
<ur-button (handleClick)="writeParam(port.value, robotIP.value)" [type]="'cta'" class="button">
{{'presenter.confirm-button' | translate}}
</ur-button>
</div>

<h2 class="sub-header" [textContent]="baseTranslationKey + '.important.header' | translate"></h2>
<p class="info-text pre-line" [textContent]="baseTranslationKey + '.important.line1' | translate"></p>
<p class="info-text pre-line" [textContent]="baseTranslationKey + '.important.line2' | translate"></p>
<div class="vertical-spacer"></div>
<p class="info-text" [textContent]="baseTranslationKey + '.serviceHowToEnable.text' | translate"></p>
<span class="highlight">
{{ baseTranslationKey + '.serviceHowToEnable.settings' | translate }}
<span class="arrow">→</span>
{{ baseTranslationKey + '.serviceHowToEnable.security' | translate }}
<span class="arrow">→</span>
{{ baseTranslationKey + '.serviceHowToEnable.services' | translate }}
</span>
</div>
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,51 @@
}
}

.main-section {
margin-left: 50px;
}

.flex-container {
display: flex;
align-content: flex-start;
padding: 0.5rem 0.5rem 1rem;
flex-wrap: wrap;
gap: 10px;
}

.input-ip,
.input-port {
min-width: 200px;
}

.header {
font-size: 24px;
margin-bottom: 20px;
}

.sub-header {
font-size: 18px;
margin-top: 40px;
margin-bottom: 10px;
}

.pre-line {
white-space: pre-line;
}

.vertical-spacer {
height: 1px;
}

.arrow {
font-size: 20px;
vertical-align: bottom;
margin: 0 5px;
}

.highlight {
background-color: #ffffff;
padding: 5px;
border-radius: 5px;
font-weight: bold;
color: #333;
margin-left: 30px;
}
Original file line number Diff line number Diff line change
@@ -1,31 +1,89 @@
/**
* Contains unit tests for the ExternalControlApplicationComponent.
* It ensures that the component is created correctly and its functionalities
* work as expected.
*/
import {provideHttpClient, withInterceptorsFromDi} from '@angular/common/http';
import {provideHttpClientTesting} from '@angular/common/http/testing';
import {ComponentFixture, TestBed} from '@angular/core/testing';
import {ExternalControlApplicationComponent} from "./external-control-application.component";
import {TranslateLoader, TranslateModule} from "@ngx-translate/core";
import {Observable, of} from "rxjs";
import {TranslateLoader, TranslateModule, TranslateService} from '@ngx-translate/core';
import {Observable, of} from 'rxjs';
import {ExternalControlApplicationComponent} from './external-control-application.component';
import { RobotSettings } from '@universal-robots/contribution-api';

describe('ExternalControlApplicationComponent', () => {
let fixture: ComponentFixture<ExternalControlApplicationComponent>;
let component: ExternalControlApplicationComponent;
let translateService: TranslateService;

beforeEach(() => {
TestBed.configureTestingModule({
declarations: [ExternalControlApplicationComponent],
imports: [TranslateModule.forRoot({
loader: {
provide: TranslateLoader, useValue: {
getTranslation(): Observable<Record<string, string>> {
return of({});
TestBed
.configureTestingModule({
declarations: [ExternalControlApplicationComponent],
imports: [TranslateModule.forRoot({
loader: {
provide: TranslateLoader,
useValue: {
getTranslation(): Observable<Record<string, string>> {
return of({});
}
}
}
}
}
})],
}).compileComponents();
})],
providers: [
provideHttpClient(withInterceptorsFromDi()),
provideHttpClientTesting()
],
})
.compileComponents();

fixture = TestBed.createComponent(ExternalControlApplicationComponent);
component = fixture.componentInstance;
translateService = TestBed.inject(TranslateService);
});

/**
* Test to ensure the component is created successfully.
*/
it('should create the component', () => {
expect(component).toBeTruthy();
});

/**
* Test to ensure the component initializes with default values.
*/
it('should initialize with default values', () => {
expect(component.baseTranslationKey)
.toBe(
'application.nodes.universal-robots-external-control-external-control-application');
expect(component.messageEmitter$.value).toBe('');
});

/**
* Test to ensure the ngOnChanges method updates the translation language EN.
*/
it('should update translation language on robotSettings change', () => {
component.robotSettings = { language: 'en', units: null } as RobotSettings;
component.ngOnChanges({ robotSettings: { currentValue: component.robotSettings } } as any);
expect(component.currentLanguage).toBe('en');
});

/**
* Test to ensure the ngOnChanges method updates the translation language DA.
*/
it('should update translation language on robotSettings change', () => {
component.robotSettings = { language: 'da', units: null } as RobotSettings;
component.ngOnChanges({ robotSettings: { currentValue: component.robotSettings } } as any);
expect(component.currentLanguage).toBe('da');
});

/**
* Test to ensure the ngOnChanges method sets the default language if the
* language is not supported.
*/
it('should set default language to "en" if the language is not supported', () => {
component.robotSettings = { language: 'qq', units: null } as RobotSettings;
component.ngOnChanges({ robotSettings: { currentValue: component.robotSettings } } as any);
expect(component.currentLanguage).toBe('en');
});
});
Loading