Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Visualization component loading #106

Merged
merged 11 commits into from
Dec 7, 2024
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,9 @@ See examples in the [basyx-java-server-sdk](https://github.com/eclipse-basyx/bas

The BaSyx-UI includes a Feature to develop your own Plugins. They can be used to display and interact with a Submodel (and/or SubmodelElements).

Plugins will be displayed in the `Visualization`-Part of the UI. In order for Plugins to be shown, a Submodel(Element) has to have a SemanticID which matches with the configured SemanticID of the desired Plugin.
Plugins will be displayed in the `Visualization`-Part of the UI. In order for Plugins to be loaded, a Submodel(Element) has to have a SemanticID which matches with the configured `semanticId` of the desired Plugin. The configuration of a Plugin `semanticId`can be done via a string (e.g. `'http://hello.world.de/plugin_submodel'`) or via an array for multiple SemanticIds (e.g. `['http://hello.world.de/plugin_submodel', 'http://hello.world.de/plugin_property']`)

To include your own Plugin, you have to create a Vue.js Component and add it to the `UserPlugins`-Folder in the `aas-web-ui/src`-Directory. The Plugin will then be automatically loaded and displayed in the UI.
To include your own Plugins, you have to create a Vue.js Component and add it to the `UserPlugins`-Folder in the `aas-web-ui/src`-Directory. The Plugin will then be automatically loaded and displayed in the UI.

> If you plan on including your own plugins, keep in mind that you have to build the Docker Image yourself!

Expand Down
3 changes: 1 addition & 2 deletions aas-web-ui/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
node_modules
/dist


# local env files
.env.local
.env.*.local
Expand All @@ -20,4 +19,4 @@ pnpm-debug.log*
*.ntvs*
*.njsproj
*.sln
*.sw?
*.sw?
2 changes: 1 addition & 1 deletion aas-web-ui/src/UserPlugins/HelloWorldPlugin.vue
Original file line number Diff line number Diff line change
Expand Up @@ -65,11 +65,11 @@
methods: {
// Function to initialize the HelloWorld-Plugin
initializePlugin() {
// Check if a Node is selected
if (Object.keys(this.submodelElementData).length == 0) {
this.pluginData = {}; // Reset the Plugin Data when no Node is selected
return;
}

let pluginData = { ...this.submodelElementData }; // Get the SubmodelElement from the AAS
let pluginSubmodelElements = pluginData.submodelElements;
// add pathes and id's to the SubmodelElements
Expand Down
80 changes: 73 additions & 7 deletions aas-web-ui/src/components/ComponentVisualization.vue
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,45 @@
</v-card-title>
<v-divider></v-divider>
<v-card-text
v-if="submodelElementData && Object.keys(submodelElementData).length > 0"
v-if="
SelectedNode &&
Object.keys(SelectedNode).length > 0 &&
Object.keys(submodelElementData).length > 0 &&
submodelElementData.semanticId &&
submodelElementData.semanticId.keys &&
submodelElementData.semanticId.keys.length > 0
"
style="overflow-y: auto; height: calc(100svh - 170px)">
<!-- Add Plugins matched on SemanticId's inside the SubmodelEntrypoint -->
<SubmodelEntrypoint
:submodel-element-data="submodelElementData"
:selected-node="SelectedNodeToTransfer"></SubmodelEntrypoint>
<template v-if="submodelElementData.modelType == 'File' || submodelElementData.modelType == 'Blob'">
<ImagePreview
v-if="submodelElementData.contentType && submodelElementData.contentType.includes('image')"
:submodel-element-data="submodelElementData"></ImagePreview>
<PDFPreview
v-if="submodelElementData.contentType && submodelElementData.contentType.includes('pdf')"
:submodel-element-data="submodelElementData"></PDFPreview>
<CADPreview
v-if="
submodelElementData.contentType &&
(submodelElementData.contentType.includes('sla') ||
submodelElementData.contentType.includes('stl') ||
submodelElementData.contentType.includes('model') ||
submodelElementData.contentType.includes('obj') ||
submodelElementData.contentType.includes('gltf'))
"
:submodel-element-data="submodelElementData"></CADPreview>
</template>
<template v-else>
<component
:is="plugin.name"
v-for="(plugin, index) in filteredPlugins"
:key="index"
:submodel-element-data="submodelElementData"
>{{ plugin.name }}</component
>
<GenericDataVisu
v-if="viewerMode && filteredPlugins.length === 0"
:submodel-element-data="submodelElementData.submodelElements"></GenericDataVisu>
</template>
</v-card-text>
</v-card>
</v-container>
Expand All @@ -37,7 +70,10 @@
<script lang="ts">
import { defineComponent } from 'vue';
import { useRoute, useRouter } from 'vue-router';
import SubmodelEntrypoint from '@/components/SubmodelPlugins/_SubmodelEntrypoint.vue';
import CADPreview from '@/components/Plugins/CADPreview.vue';
import ImagePreview from '@/components/Plugins/ImagePreview.vue';
import PDFPreview from '@/components/Plugins/PDFPreview.vue';
import GenericDataVisu from '@/components/UIComponents/GenericDataVisu.vue';
import RequestHandling from '@/mixins/RequestHandling';
import SubmodelElementHandling from '@/mixins/SubmodelElementHandling';
import { useAASStore } from '@/store/AASDataStore';
Expand All @@ -46,7 +82,10 @@
export default defineComponent({
name: 'ComponentVisualization',
components: {
SubmodelEntrypoint, // Submodel Plugin Entrypoint Component
GenericDataVisu,
ImagePreview,
PDFPreview,
CADPreview,
},
mixins: [RequestHandling, SubmodelElementHandling],

Expand Down Expand Up @@ -113,6 +152,33 @@
isMobile() {
return this.navigationStore.getIsMobile;
},

importedPlugins() {
return this.navigationStore.getPlugins;
},

// Filtered Plugins
filteredPlugins() {
return this.importedPlugins.filter((plugin: any) => {
if (!plugin.semanticId) return false;

if (typeof plugin.semanticId === 'string') {
return this.checkSemanticId(this.submodelElementData, plugin.semanticId);
} else if (plugin.semanticId.constructor === Array) {
for (const pluginSemanticId of plugin.semanticId) {
if (this.checkSemanticId(this.submodelElementData, pluginSemanticId)) return true;
}
return false;
}
return false;
});
},

// return if in viewer mode
viewerMode() {
// check if the route name is aasviewer
return this.route.name === 'AASViewer' || this.route.name === 'ComponentVisualization';
},
},

watch: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import TimeSeriesData from '@/components/SubmodelPlugins/TimeSeriesData.vue';
import TimeSeriesData from '@/components/Plugins/Submodels/TimeSeriesData.vue';
import DashboardHandling from '@/mixins/DashboardHandling';
import SubmodelElementHandling from '@/mixins/SubmodelElementHandling';
import { useEnvStore } from '@/store/EnvironmentStore';
Expand Down
2 changes: 1 addition & 1 deletion aas-web-ui/src/components/Dashboard/DashboardElement.vue
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@

<script lang="ts">
import { defineComponent } from 'vue';
import TimeSeriesData from '@/components/SubmodelPlugins/TimeSeriesData.vue';
import TimeSeriesData from '@/components/Plugins/Submodels/TimeSeriesData.vue';
import DashboardHandling from '@/mixins/DashboardHandling';
import SubmodelElementHandling from '@/mixins/SubmodelElementHandling';
import { useEnvStore } from '@/store/EnvironmentStore';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1118,6 +1118,7 @@

export default defineComponent({
name: 'HTWFuehrungskomponente',
semanticId: 'http://htw-berlin.de/smc_statemachine',
mixins: [RequestHandling, SubmodelElementHandling],
props: ['submodelElementData', 'selectedNode'],

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

export default defineComponent({
name: 'JSONArrayProperty',
semanticId: 'http://iese.fraunhofer.de/prop_jsonarray',
props: ['submodelElementData'],

setup() {
Expand Down Expand Up @@ -112,10 +113,10 @@

methods: {
initChart() {
// Check if a Node is selected
if (Object.keys(this.submodelElementData).length == 0) {
return;
}

let chartData = JSON.parse(this.submodelElementData.value); // parse the value of the SubmodelElement
let seriesName = this.submodelElementData.idShort; // get the idShort of the SubmodelElement
// check if the value is an array or an object
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,10 @@

export default defineComponent({
name: 'BillsOfMaterial',
semanticId: [
'https://admin-shell.io/idta/HierarchicalStructures/1/0/Submodel',
'https://admin-shell.io/idta/HierarchicalStructures/1/1/Submodel',
],
mixins: [RequestHandling, SubmodelElementHandling],
props: ['submodelElementData'],

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@

export default defineComponent({
name: 'ContactInformation',
semanticId: 'https://admin-shell.io/zvei/nameplate/1/0/ContactInformations',
mixins: [RequestHandling, SubmodelElementHandling],
props: ['submodelElementData'],

Expand Down Expand Up @@ -126,13 +127,19 @@
methods: {
async initContactInformation() {
this.loading = true;
// console.log('Initialize Contact Information Plugin: ', this.submodelElementData);

if (Object.keys(this.submodelElementData).length == 0) {
this.contactInformationData = {}; // Reset the DigitalNameplate Data when no Node is selected
this.loading = false;
return;
}

let submodelElementData = { ...this.submodelElementData };
submodelElementData = await this.calculateSubmodelElementPathes(
this.contactInformationData = await this.calculateSubmodelElementPathes(
submodelElementData,
this.SelectedNode.path
);
this.contactInformationData = submodelElementData;

// create array of contacts
let contacts = this.contactInformationData.submodelElements.filter((element: any) => {
return (
Expand Down
Loading