TheModels.vue 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227
  1. <!-- eslint-disable no-fallthrough -->
  2. <script setup lang="ts">
  3. import { AddressSpace } from '@/ua/AddressSpace';
  4. import { UANodeSet } from '@/ua/UANodeSet';
  5. import { useStore } from '@/store'
  6. import { ref } from 'vue';
  7. import VDialog from './VDialog.vue'
  8. import JSZip from "jszip";
  9. import YAML from 'yaml'
  10. import { assert } from '@/util/assert';
  11. import { storeToRefs } from 'pinia';
  12. import type { IMappingValue } from '@/ua/IAddressSpace';
  13. import { ServerConfig } from '@/ServerConfig';
  14. const store = useStore()
  15. const newProjectOpen = ref(false);
  16. const newProjectDisabled = ref(false); //Disables buttons while loading project
  17. const serverConfigOpen = ref(false);
  18. const projectType = ref("ua");
  19. const { serverConfig } = storeToRefs(store)
  20. async function exportProject() {
  21. const zip = new JSZip();
  22. const fileNames:string[]=[];
  23. const as=store.addressSpace;
  24. for(const ns of as.nodesets) {
  25. fileNames.push(ns.fileName);
  26. zip.file(ns.fileName, ns.toXML(ns.nameSpaceTable, as.nst).toString());
  27. }
  28. store.serverConfig.configData.nodesets = fileNames;
  29. zip.file("project.json", JSON.stringify(store.serverConfig.configData));
  30. const mapString=YAML.stringify(as.mapping.values());
  31. zip.file("mapping.yaml", mapString)
  32. const blob =await zip.generateAsync({type:'blob'});
  33. downloadBlob(blob, "project.zip");
  34. }
  35. function downloadBlob(blob: Blob, filename: string) {
  36. let link = document.createElement('a')
  37. link.href = window.URL.createObjectURL(blob)
  38. link.download = filename
  39. document.body.appendChild(link);
  40. link.click()
  41. document.body.removeChild(link);
  42. }
  43. async function newProject() {
  44. newProjectDisabled.value = true;
  45. store.addressSpace = new AddressSpace([] as UANodeSet[])
  46. let nodesets: string[] = [];
  47. switch (projectType.value) {
  48. case "emco":
  49. nodesets.push("nodesets/emco_machine_tools.xml");
  50. case "machinetool":
  51. nodesets.push("nodesets/Opc.Ua.MachineTool.Nodeset2.xml");
  52. nodesets.push("nodesets/Opc.Ua.Machinery.NodeSet2.xml");
  53. nodesets.push("nodesets/Opc.Ua.Ia.NodeSet2.xml");
  54. nodesets.push("nodesets/Opc.Ua.Di.NodeSet2.xml");
  55. case "ua":
  56. nodesets.push("nodesets/Opc.Ua.NodeSet2.xml");
  57. default:
  58. break;
  59. }
  60. let as = await AddressSpace.load(nodesets.reverse());
  61. store.setAddressSpace(as);
  62. newProjectOpen.value = false;
  63. newProjectDisabled.value = false;
  64. }
  65. async function handleDrop(e: DragEvent) {
  66. if (!e.dataTransfer)
  67. return;
  68. if(e.dataTransfer.files.length==1 && e.dataTransfer.files[0].name.endsWith(".zip")) {
  69. loadZip(e.dataTransfer.files[0]);
  70. }
  71. else {
  72. for (const file of e.dataTransfer.files) {
  73. const xmlString = await file.text();
  74. assert(store.addressSpace!=null);
  75. store.addNodeset(await UANodeSet.parse(xmlString, file.name, store.addressSpace));
  76. }
  77. }
  78. }
  79. async function loadZip(file: File){
  80. const zip= await JSZip.loadAsync(file.arrayBuffer())
  81. const config=ServerConfig.load(JSON.parse(await zip.files["project.json"].async("string")));
  82. const as=new AddressSpace([]);
  83. for(const fileName of config.configData.nodesets||[]) {
  84. as.addNodeset(await UANodeSet.parse(await zip.files[fileName].async("string"), fileName, as));
  85. }
  86. const mlist=YAML.parse(await zip.files['mapping.yaml'].async("string")) as IMappingValue[];
  87. const mapping=new Map<string, IMappingValue>();
  88. for(const entry of mlist) {
  89. mapping.set(entry.path, entry);
  90. }
  91. as.mapping=mapping;
  92. store.serverConfig=config;
  93. store.setAddressSpace(as);
  94. }
  95. </script>
  96. <template>
  97. <div v-if="store.addressSpace">
  98. <div class="card">
  99. <div class="card-body" v-if="store.addressSpace">
  100. <div class="d-flex justify-content-between align-items-center mb-3">
  101. <h5 class="card-title d-inline">Models</h5>
  102. <div class="btn-group">
  103. <button class="btn btn-light" @click="serverConfigOpen = true">Settings</button>
  104. <button class="btn btn-light" @click.prevent="exportProject()">Export</button>
  105. <button class="btn btn-light" @click="newProjectOpen = true">New</button>
  106. </div>
  107. </div>
  108. <p class="card-text">
  109. <ul class="list-group list-group-flush" v-for="nodeset in store.addressSpace.nodesets"
  110. v-bind:key="nodeset.models[0].modelUri">
  111. <div v-for="model in nodeset.models" v-bind:key="model.modelUri">
  112. <li class="list-group-item">{{ model.modelUri }}</li>
  113. </div>
  114. </ul>
  115. <ul class="list-group list-group-flush" @drop.prevent="handleDrop($event)" dropzone="copy"
  116. @dragover="$event.preventDefault();">
  117. <div>
  118. <li class="list-group-item">(drop .xml nodeset or .zip project file here)</li>
  119. </div>
  120. </ul>
  121. </p>
  122. </div>
  123. </div>
  124. </div>
  125. <v-dialog :open="newProjectOpen" @cancel="newProjectOpen = false">
  126. <h5>New project</h5>
  127. <p></p>
  128. <div class="form-check">
  129. <input class="form-check-input" type="radio" name="projectType" id="empty" value="empty" v-model="projectType">
  130. <label class="form-check-label" for="empty">Empty Namespace</label>
  131. </div>
  132. <div class="form-check">
  133. <input class="form-check-input" type="radio" name="projectType" id="ua" value="ua" v-model="projectType" checked>
  134. <label class="form-check-label" for="ua">http://opcfoundation.org/UA/ Namespace</label>
  135. </div>
  136. <div class="form-check">
  137. <input class="form-check-input" type="radio" name="projectType" id="machinetool" value="machinetool"
  138. v-model="projectType">
  139. <label class="form-check-label" for="machinetool">Machinetool Namespace +dependencies</label>
  140. </div>
  141. <div class="form-check">
  142. <input class="form-check-input" type="radio" name="projectType" id="emco" value="emco" v-model="projectType">
  143. <label class="form-check-label" for="emco">Full EMCO Project</label>
  144. </div>
  145. <div class="btn-group d-flex justify-content-end">
  146. <button class="btn btn-light" @click="newProjectOpen = false" :disabled="newProjectDisabled">Cancel</button>
  147. <button class="btn btn-light" @click="newProject()" :disabled="newProjectDisabled">OK</button>
  148. </div>
  149. </v-dialog>
  150. <v-dialog :open="serverConfigOpen" @cancel="serverConfigOpen = false">
  151. <h5>Server Configuration</h5>
  152. <p></p>
  153. <div v-if="serverConfig">
  154. <div class="input-group mb-3">
  155. <div class="input-group-prepend">
  156. <span class="input-group-text" id="inputGroup-sizing-default">Manufacturer Name</span>
  157. </div>
  158. <input type="text" class="form-control" aria-label="Default" aria-describedby="inputGroup-sizing-default" v-model="serverConfig.configData.manufacturerName">
  159. </div>
  160. <div class="input-group mb-3">
  161. <div class="input-group-prepend">
  162. <span class="input-group-text" id="inputGroup-sizing-default">Product Name</span>
  163. </div>
  164. <input type="text" class="form-control" aria-label="Default" aria-describedby="inputGroup-sizing-default" v-model="serverConfig.configData.productName">
  165. </div>
  166. <div class="input-group mb-3">
  167. <div class="input-group-prepend">
  168. <span class="input-group-text" id="inputGroup-sizing-default">Software Version</span>
  169. </div>
  170. <input type="text" class="form-control" aria-label="Default" aria-describedby="inputGroup-sizing-default" v-model="serverConfig.configData.softwareVersion">
  171. </div>
  172. <div class="input-group mb-3">
  173. <div class="input-group-prepend">
  174. <span class="input-group-text" id="inputGroup-sizing-default">Application Name</span>
  175. </div>
  176. <input type="text" class="form-control" aria-label="Default" aria-describedby="inputGroup-sizing-default" v-model="serverConfig.configData.applicationName">
  177. </div>
  178. <div class="input-group mb-3">
  179. <div class="input-group-prepend">
  180. <span class="input-group-text" id="inputGroup-sizing-default">Application Uri</span>
  181. </div>
  182. <input type="text" class="form-control" aria-label="Default" aria-describedby="inputGroup-sizing-default" v-model="serverConfig.configData.applicationUri">
  183. </div>
  184. <div class="input-group mb-3">
  185. <div class="input-group-prepend">
  186. <span class="input-group-text" id="inputGroup-sizing-default">Product Uri</span>
  187. </div>
  188. <input type="text" class="form-control" aria-label="Default" aria-describedby="inputGroup-sizing-default" v-model="serverConfig.configData.productUri">
  189. </div>
  190. <div class="input-group mb-3">
  191. <div class="input-group-prepend">
  192. <span class="input-group-text" id="inputGroup-sizing-default">Port</span>
  193. </div>
  194. <input type="text" class="form-control" aria-label="Default" aria-describedby="inputGroup-sizing-default" v-model="serverConfig.configData.port">
  195. </div>
  196. <div class="input-group mb-3">
  197. <div class="input-group-prepend">
  198. <span class="input-group-text" id="inputGroup-sizing-default">Max Connections Per Endpoint</span>
  199. </div>
  200. <input type="text" class="form-control" aria-label="Default" aria-describedby="inputGroup-sizing-default" v-model="serverConfig.configData.maxConnections">
  201. </div>
  202. <div class="input-group mb-3">
  203. <div class="input-group-prepend form-check">
  204. <input class="form-check-input" type="checkbox" id="checkAllowAnon" v-model="serverConfig.configData.allowAnonymous" />
  205. <label class="form-check-label" for="checkAllowAnon">Allow Anonymous</label>
  206. </div>
  207. </div>
  208. </div>
  209. <div class="btn-group d-flex justify-content-end">
  210. <button class="btn btn-light" @click="serverConfigOpen = false">OK</button>
  211. </div>
  212. </v-dialog>
  213. </template>
  214. <style></style>