123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227 |
- <!-- eslint-disable no-fallthrough -->
- <script setup lang="ts">
- import { AddressSpace } from '@/ua/AddressSpace';
- import { UANodeSet } from '@/ua/UANodeSet';
- import { useStore } from '@/store'
- import { ref } from 'vue';
- import VDialog from './VDialog.vue'
- import JSZip from "jszip";
- import YAML from 'yaml'
- import { assert } from '@/util/assert';
- import { storeToRefs } from 'pinia';
- import type { IMappingValue } from '@/ua/IAddressSpace';
- import { ServerConfig } from '@/ServerConfig';
- const store = useStore()
- const newProjectOpen = ref(false);
- const newProjectDisabled = ref(false); //Disables buttons while loading project
- const serverConfigOpen = ref(false);
- const projectType = ref("ua");
- const { serverConfig } = storeToRefs(store)
- async function exportProject() {
- const zip = new JSZip();
- const fileNames:string[]=[];
- const as=store.addressSpace;
- for(const ns of as.nodesets) {
- fileNames.push(ns.fileName);
- zip.file(ns.fileName, ns.toXML(ns.nameSpaceTable, as.nst).toString());
- }
- store.serverConfig.configData.nodesets = fileNames;
- zip.file("project.json", JSON.stringify(store.serverConfig.configData));
- const mapString=YAML.stringify(as.mapping.values());
- zip.file("mapping.yaml", mapString)
- const blob =await zip.generateAsync({type:'blob'});
- downloadBlob(blob, "project.zip");
- }
- function downloadBlob(blob: Blob, filename: string) {
- let link = document.createElement('a')
- link.href = window.URL.createObjectURL(blob)
- link.download = filename
- document.body.appendChild(link);
- link.click()
- document.body.removeChild(link);
- }
- async function newProject() {
- newProjectDisabled.value = true;
- store.addressSpace = new AddressSpace([] as UANodeSet[])
- let nodesets: string[] = [];
- switch (projectType.value) {
- case "emco":
- nodesets.push("nodesets/emco_machine_tools.xml");
- case "machinetool":
- nodesets.push("nodesets/Opc.Ua.MachineTool.Nodeset2.xml");
- nodesets.push("nodesets/Opc.Ua.Machinery.NodeSet2.xml");
- nodesets.push("nodesets/Opc.Ua.Ia.NodeSet2.xml");
- nodesets.push("nodesets/Opc.Ua.Di.NodeSet2.xml");
- case "ua":
- nodesets.push("nodesets/Opc.Ua.NodeSet2.xml");
- default:
- break;
- }
- let as = await AddressSpace.load(nodesets.reverse());
- store.setAddressSpace(as);
- newProjectOpen.value = false;
- newProjectDisabled.value = false;
- }
- async function handleDrop(e: DragEvent) {
- if (!e.dataTransfer)
- return;
- if(e.dataTransfer.files.length==1 && e.dataTransfer.files[0].name.endsWith(".zip")) {
- loadZip(e.dataTransfer.files[0]);
- }
- else {
- for (const file of e.dataTransfer.files) {
- const xmlString = await file.text();
- assert(store.addressSpace!=null);
- store.addNodeset(await UANodeSet.parse(xmlString, file.name, store.addressSpace));
- }
- }
- }
- async function loadZip(file: File){
- const zip= await JSZip.loadAsync(file.arrayBuffer())
- const config=ServerConfig.load(JSON.parse(await zip.files["project.json"].async("string")));
- const as=new AddressSpace([]);
- for(const fileName of config.configData.nodesets||[]) {
- as.addNodeset(await UANodeSet.parse(await zip.files[fileName].async("string"), fileName, as));
- }
- const mlist=YAML.parse(await zip.files['mapping.yaml'].async("string")) as IMappingValue[];
- const mapping=new Map<string, IMappingValue>();
- for(const entry of mlist) {
- mapping.set(entry.path, entry);
- }
- as.mapping=mapping;
- store.serverConfig=config;
- store.setAddressSpace(as);
- }
- </script>
- <template>
- <div v-if="store.addressSpace">
- <div class="card">
- <div class="card-body" v-if="store.addressSpace">
- <div class="d-flex justify-content-between align-items-center mb-3">
- <h5 class="card-title d-inline">Models</h5>
- <div class="btn-group">
- <button class="btn btn-light" @click="serverConfigOpen = true">Settings</button>
- <button class="btn btn-light" @click.prevent="exportProject()">Export</button>
- <button class="btn btn-light" @click="newProjectOpen = true">New</button>
- </div>
- </div>
- <p class="card-text">
- <ul class="list-group list-group-flush" v-for="nodeset in store.addressSpace.nodesets"
- v-bind:key="nodeset.models[0].modelUri">
- <div v-for="model in nodeset.models" v-bind:key="model.modelUri">
- <li class="list-group-item">{{ model.modelUri }}</li>
- </div>
- </ul>
- <ul class="list-group list-group-flush" @drop.prevent="handleDrop($event)" dropzone="copy"
- @dragover="$event.preventDefault();">
- <div>
- <li class="list-group-item">(drop .xml nodeset or .zip project file here)</li>
- </div>
- </ul>
- </p>
- </div>
- </div>
- </div>
- <v-dialog :open="newProjectOpen" @cancel="newProjectOpen = false">
- <h5>New project</h5>
- <p></p>
- <div class="form-check">
- <input class="form-check-input" type="radio" name="projectType" id="empty" value="empty" v-model="projectType">
- <label class="form-check-label" for="empty">Empty Namespace</label>
- </div>
- <div class="form-check">
- <input class="form-check-input" type="radio" name="projectType" id="ua" value="ua" v-model="projectType" checked>
- <label class="form-check-label" for="ua">http://opcfoundation.org/UA/ Namespace</label>
- </div>
- <div class="form-check">
- <input class="form-check-input" type="radio" name="projectType" id="machinetool" value="machinetool"
- v-model="projectType">
- <label class="form-check-label" for="machinetool">Machinetool Namespace +dependencies</label>
- </div>
- <div class="form-check">
- <input class="form-check-input" type="radio" name="projectType" id="emco" value="emco" v-model="projectType">
- <label class="form-check-label" for="emco">Full EMCO Project</label>
- </div>
- <div class="btn-group d-flex justify-content-end">
- <button class="btn btn-light" @click="newProjectOpen = false" :disabled="newProjectDisabled">Cancel</button>
- <button class="btn btn-light" @click="newProject()" :disabled="newProjectDisabled">OK</button>
- </div>
- </v-dialog>
- <v-dialog :open="serverConfigOpen" @cancel="serverConfigOpen = false">
- <h5>Server Configuration</h5>
- <p></p>
- <div v-if="serverConfig">
- <div class="input-group mb-3">
- <div class="input-group-prepend">
- <span class="input-group-text" id="inputGroup-sizing-default">Manufacturer Name</span>
- </div>
- <input type="text" class="form-control" aria-label="Default" aria-describedby="inputGroup-sizing-default" v-model="serverConfig.configData.manufacturerName">
- </div>
- <div class="input-group mb-3">
- <div class="input-group-prepend">
- <span class="input-group-text" id="inputGroup-sizing-default">Product Name</span>
- </div>
- <input type="text" class="form-control" aria-label="Default" aria-describedby="inputGroup-sizing-default" v-model="serverConfig.configData.productName">
- </div>
- <div class="input-group mb-3">
- <div class="input-group-prepend">
- <span class="input-group-text" id="inputGroup-sizing-default">Software Version</span>
- </div>
- <input type="text" class="form-control" aria-label="Default" aria-describedby="inputGroup-sizing-default" v-model="serverConfig.configData.softwareVersion">
- </div>
- <div class="input-group mb-3">
- <div class="input-group-prepend">
- <span class="input-group-text" id="inputGroup-sizing-default">Application Name</span>
- </div>
- <input type="text" class="form-control" aria-label="Default" aria-describedby="inputGroup-sizing-default" v-model="serverConfig.configData.applicationName">
- </div>
- <div class="input-group mb-3">
- <div class="input-group-prepend">
- <span class="input-group-text" id="inputGroup-sizing-default">Application Uri</span>
- </div>
- <input type="text" class="form-control" aria-label="Default" aria-describedby="inputGroup-sizing-default" v-model="serverConfig.configData.applicationUri">
- </div>
- <div class="input-group mb-3">
- <div class="input-group-prepend">
- <span class="input-group-text" id="inputGroup-sizing-default">Product Uri</span>
- </div>
- <input type="text" class="form-control" aria-label="Default" aria-describedby="inputGroup-sizing-default" v-model="serverConfig.configData.productUri">
- </div>
- <div class="input-group mb-3">
- <div class="input-group-prepend">
- <span class="input-group-text" id="inputGroup-sizing-default">Port</span>
- </div>
- <input type="text" class="form-control" aria-label="Default" aria-describedby="inputGroup-sizing-default" v-model="serverConfig.configData.port">
- </div>
- <div class="input-group mb-3">
- <div class="input-group-prepend">
- <span class="input-group-text" id="inputGroup-sizing-default">Max Connections Per Endpoint</span>
- </div>
- <input type="text" class="form-control" aria-label="Default" aria-describedby="inputGroup-sizing-default" v-model="serverConfig.configData.maxConnections">
- </div>
- <div class="input-group mb-3">
- <div class="input-group-prepend form-check">
- <input class="form-check-input" type="checkbox" id="checkAllowAnon" v-model="serverConfig.configData.allowAnonymous" />
- <label class="form-check-label" for="checkAllowAnon">Allow Anonymous</label>
- </div>
- </div>
- </div>
- <div class="btn-group d-flex justify-content-end">
- <button class="btn btn-light" @click="serverConfigOpen = false">OK</button>
- </div>
- </v-dialog>
- </template>
- <style></style>
|