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

Work on AASList / AASListDetails / AASTreeview / SubmodelList (#135) #5

Merged
merged 1 commit into from
Dec 17, 2024
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
8 changes: 8 additions & 0 deletions aas-web-ui/entrypoint.sh
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,14 @@ if [ -n "$ENDPOINT_CONFIG_AVAILABLE" ]; then
jq '.endpointConfigAvailable = env.ENDPOINT_CONFIG_AVAILABLE' /usr/src/app/dist/config.json > /tmp/config.json && mv /tmp/config.json /usr/src/app/dist/config.json
fi

if [ -n "$SINGLE_AAS" ]; then
jq '.singleAas = env.SINGLE_AAS' /usr/src/app/dist/config.json > /tmp/config.json && mv /tmp/config.json /usr/src/app/dist/config.json
fi

if [ -n "$SINGLE_AAS_REDIRECT" ]; then
jq '.singleAasRedirect = env.SINGLE_AAS_REDIRECT' /usr/src/app/dist/config.json > /tmp/config.json && mv /tmp/config.json /usr/src/app/dist/config.json
fi

if [ -n "$BASE_PATH" ]; then
echo "====================="
echo "BASE_PATH: $BASE_PATH"
Expand Down
4 changes: 3 additions & 1 deletion aas-web-ui/public/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,7 @@
"keycloakUrl": "",
"keycloakRealm": "",
"keycloakClientId": "",
"endpointConfigAvailable": true
"endpointConfigAvailable": true,
"singleAas": false,
"singleAasRedirect": ""
}
37 changes: 20 additions & 17 deletions aas-web-ui/src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -20,17 +20,9 @@
import RequestHandling from '@/mixins/RequestHandling';
import SubmodelElementHandling from '@/mixins/SubmodelElementHandling';
import { useAASStore } from '@/store/AASDataStore';
import { useEnvStore } from '@/store/EnvironmentStore';
import { useNavigationStore } from '@/store/NavigationStore';

interface AASType {
endpoints: Array<{
protocolInformation: {
href: string;
};
interface: string;
}>;
}

export default defineComponent({
name: 'App',
components: {
Expand All @@ -46,16 +38,18 @@
const aasStore = useAASStore();
const route = useRoute();
const router = useRouter();
const envStore = useEnvStore();

return {
navigationStore, // NavigationStore Object
aasStore, // AASStore Object
route, // Route Object
router, // Router Object
envStore, // EnvironmentStore Object
};
},

mounted() {
async mounted() {
let mobile = this.$vuetify.display.mobile;
// include IPad as mobile device
if (this.$vuetify.display.platform.mac && this.$vuetify.display.platform.touch) {
Expand All @@ -76,6 +70,20 @@
const aasEndpoint = searchParams.get('aas');
const submodelElementPath = searchParams.get('path');

// Ensure available aasEndpoint query parameter
if (
this.envStore.singleAas &&
(aasEndpoint === null || aasEndpoint === undefined || aasEndpoint.trim() === '')
) {
if (this.envStore.getSingleAasRedirect) {
window.location.replace(this.envStore.getSingleAasRedirect);
return;
} else if (this.route.name !== '404NotFound404') {
this.router.push({ name: 'NotFound404' });
return;
}
}

// check which platform is used and change the fitting view
if (mobile) {
if (this.route.name === 'MainWindow') {
Expand Down Expand Up @@ -117,12 +125,7 @@

if (aasEndpoint) {
// console.log('AAS Query is set: ', aasEndpoint);
let aas = {} as AASType;
let endpoints = [];
endpoints.push({ protocolInformation: { href: aasEndpoint }, interface: 'AAS-3.0' });
aas.endpoints = endpoints;
// dispatch the AAS set by the URL to the store
this.aasStore.dispatchSelectedAAS(aas);
await this.fetchAndDispatchAas(aasEndpoint);
}

if (aasEndpoint && submodelElementPath) {
Expand All @@ -142,7 +145,7 @@
this.aasStore.dispatchNode(response.data); // set the updatedNode in the AASStore
} else {
// execute if the Request failed
if (Object.keys(response.data).length == 0) {
if (response?.data && Object.keys(response?.data).length === 0) {
// don't copy the static SubmodelElement Data if no Node is selected or Node is invalid
this.navigationStore.dispatchSnackbar({
status: true,
Expand Down
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,7 +65,7 @@
methods: {
// Function to initialize the HelloWorld-Plugin
initializePlugin() {
if (Object.keys(this.submodelElementData).length == 0) {
if (Object.keys(this.submodelElementData).length === 0) {
this.pluginData = {}; // Reset the Plugin Data when no Node is selected
return;
}
Expand Down
127 changes: 43 additions & 84 deletions aas-web-ui/src/components/AASTreeview.vue
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@
<v-col cols="auto">
<span>AAS Treeview</span>
</v-col>
<v-col v-if="nameToDisplay(SelectedAAS)" cols="auto" class="pl-1 pt-2">
<v-col v-if="nameToDisplay(selectedAAS)" cols="auto" class="pl-1 pt-2">
<v-chip size="x-small" color="primary" label border>{{
'AAS: ' + nameToDisplay(SelectedAAS)
'AAS: ' + nameToDisplay(selectedAAS)
}}</v-chip>
</v-col>
</v-row>
Expand All @@ -29,14 +29,20 @@
</template>
</v-list-item>
</div>
<!-- TODO: Replace with Vuetify Treeview Component when it get's released in Q1 2023 -->
<VTreeview
v-for="item in submodelData"
v-else
:key="item.id"
class="root"
:item="item"
:depth="0"></VTreeview>
<template v-else>
<v-empty-state
v-if="selectedAAS && Object.keys(selectedAAS).length > 0 && submodelData.length === 0"
title="No existing Submodels"
text="The selected AAS does not contain any Submodels"
class="text-divider"></v-empty-state>
<!-- TODO: Replace with Vuetify Treeview Component when it get's released in Q1 2023 -->
<VTreeview
v-for="item in submodelData"
:key="item.id"
class="root"
:item="item"
:depth="0"></VTreeview>
</template>
</v-card-text>
</v-card>
</v-container>
Expand Down Expand Up @@ -81,15 +87,10 @@

computed: {
// get selected AAS from Store
SelectedAAS() {
selectedAAS() {
return this.aasStore.getSelectedAAS;
},

// get the trigger for AAS selection from Store
triggerAAS() {
return this.navigationStore.getTriggerAASSelected;
},

// gets loading State from Store
loading() {
return this.aasStore.getLoadingState;
Expand All @@ -116,8 +117,7 @@
},

watch: {
// initialize Treeview when AAS gets selected
triggerAAS() {
selectedAAS() {
this.initializeTree();
},

Expand Down Expand Up @@ -154,74 +154,32 @@
},

methods: {
// Function to get the Submodels from the selected AAS (retrieved from the AAS with the provided endpoint)
initializeTree() {
// console.log('Initialize Treeview', this.initialUpdate, this.initialNode);
async initializeTree() {
// console.log('Initialize Treeview', this.SelectedAAS, this.initialUpdate, this.initialNode);
// return if no endpoints are available
if (!this.SelectedAAS || !this.SelectedAAS.endpoints || this.SelectedAAS.endpoints.length === 0) {
// TODO: this seems to get executed on reload with a selected AAS
if (!this.selectedAAS || !this.selectedAAS.endpoints || this.selectedAAS.endpoints.length === 0) {
// this.navigationStore.dispatchSnackbar({ status: true, timeout: 4000, color: 'error', btnColor: 'buttonText', text: 'AAS with no (valid) Endpoint selected!' });
this.submodelData = [];
return;
}
if (this.loading && !this.initialUpdate) return; // return if loading state is true -> prevents multiple requests
this.aasStore.dispatchLoadingState(true); // set loading state to true
this.submodelData = []; // reset Treeview Data
// retrieve AAS from endpoint
const shellHref = this.extractEndpointHref(this.selectedAAS, 'AAS-3.0');
let path = shellHref;
let context = 'retrieving AAS Data';
let disableMessage = false;
this.getRequest(path, context, disableMessage)
.then(async (response: any) => {
if (response.success) {
// execute if the Request was successful
try {
let AAS = response.data;
AAS.endpoints = this.SelectedAAS.endpoints;
this.aasStore.dispatchSelectedAAS(AAS); // dispatch the selected AAS to the Store
if (!AAS.submodels) {
throw new Error('No Submodels found in AAS!');
}
// request submodels from the retrieved AAS (top layer of the Treeview)
let submodelData = await this.requestSubmodels(AAS.submodels);
// set the isActive prop of the initialNode if it exists and the initialUpdate flag is set
if (this.initialUpdate && this.initialNode) {
let expandedSubmodelData = this.expandTree(submodelData, this.initialNode); // Update the Treeview to expand until the initially set node is reached
// this.updateNode(this.initialNode); // set the isActive prop of the initialNode to true
this.initialUpdate = false;
this.initialNode = {};
// console.log('Expanded Treeview Data: ', expandedSubmodelData)
this.submodelData = expandedSubmodelData; // set the Treeview Data
} else {
this.submodelData = submodelData; // set the Treeview Data
// console.log('Treeview Data: ', this.submodelData)
}
} catch (error: any) {
// console.error('Error while parsing the Submodel References: ', error);
const errorMessage = error.message;
const errorStack = error.stack;
const errorLocation = errorStack ? errorStack.split('\n')[1] : '';
this.navigationStore.dispatchSnackbar({
status: true,
timeout: 60000,
color: 'error',
btnColor: 'buttonText',
baseError: 'Error while parsing the Submodel References!',
extendedError: `Error: ${errorMessage}\nLocation: ${errorLocation.trim()}`,
});
} finally {
this.aasStore.dispatchLoadingState(false);
}
} else {
// execute if the Request failed
this.submodelData = [];
this.aasStore.dispatchLoadingState(false);
}
})
.catch(() => {
this.aasStore.dispatchLoadingState(false);
});
if (this.selectedAAS.submodels) {
let submodelData = await this.requestSubmodels(this.selectedAAS.submodels);
// set the isActive prop of the initialNode if it exists and the initialUpdate flag is set
if (this.initialUpdate && this.initialNode) {
let expandedSubmodelData = this.expandTree(submodelData, this.initialNode); // Update the Treeview to expand until the initially set node is reached
// this.updateNode(this.initialNode); // set the isActive prop of the initialNode to true
this.initialUpdate = false;
this.initialNode = {};
this.submodelData = expandedSubmodelData;
} else {
this.submodelData = submodelData;
}
} else {
this.submodelData = [];
}
this.aasStore.dispatchLoadingState(false);
},

// Function to request all Submodels for the selected AAS
Expand All @@ -231,8 +189,9 @@
// retrieve endpoint for submodel from submodel registry
// console.log('SubmodelRef: ', submodelRef, ' Submodel Registry: ', this.submodelRegistryServerURL);
// check if submodelRegistryURL includes "/submodel-descriptors" and add id if not (backward compatibility)
if (!this.submodelRegistryURL.includes('/submodel-descriptors')) {
this.submodelRegistryURL += '/submodel-descriptors';
let submodelRegistryURL = this.submodelRegistryURL;
if (!submodelRegistryURL.includes('/submodel-descriptors')) {
submodelRegistryURL += '/submodel-descriptors';
}
const submodelId = submodelRef.keys[0].value;
let path = this.submodelRegistryURL + '/' + this.URLEncode(submodelId);
Expand Down Expand Up @@ -439,9 +398,9 @@

// Function to initialize the treeview with route params
initTreeWithRouteParams() {
// check if the SelectedAAS is already set in the Store and initialize the Treeview if so
if (this.SelectedAAS && this.SelectedAAS.endpoints && this.SelectedAAS.endpoints.length > 0) {
// console.log('init Tree from Route Params: ', this.SelectedAAS);
// check if the selectedAAS is already set in the Store and initialize the Treeview if so
if (this.selectedAAS && this.selectedAAS.endpoints && this.selectedAAS.endpoints.length > 0) {
// console.log('init Tree from Route Params: ', this.selectedAAS);
this.initializeTree();
}

Expand Down
Loading
Loading