TheModels.vue 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234
  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(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(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="dropdown">
  103. <button class="btn btn-secondary dropdown-toggle"
  104. type="button"
  105. data-bs-toggle="dropdown"
  106. aria-expanded="false">Project
  107. </button>
  108. <ul class="dropdown-menu">
  109. <li><a class="dropdown-item" href="#" @click="serverConfigOpen = true">Edit Settings</a></li>
  110. <li><a class="dropdown-item" href="#" @click.prevent="exportProject()">Export Project</a></li>
  111. <li><a class="dropdown-item" href="#" @click="newProjectOpen = true">New Project</a></li>
  112. </ul>
  113. </div>
  114. </div>
  115. <p class="card-text">
  116. <ul class="list-group list-group-flush" v-for="nodeset in store.addressSpace.nodesets"
  117. v-bind:key="nodeset.models[0].modelUri">
  118. <div v-for="model in nodeset.models" v-bind:key="model.modelUri">
  119. <li class="list-group-item">{{ model.modelUri }}</li>
  120. </div>
  121. </ul>
  122. <ul class="list-group list-group-flush" @drop.prevent="handleDrop($event)" dropzone="copy"
  123. @dragover="$event.preventDefault();">
  124. <div>
  125. <li class="list-group-item">(drop .xml nodeset or .zip project file here)</li>
  126. </div>
  127. </ul>
  128. </p>
  129. </div>
  130. </div>
  131. </div>
  132. <v-dialog :open="newProjectOpen" @cancel="newProjectOpen = false">
  133. <h5>New project</h5>
  134. <p></p>
  135. <div class="form-check">
  136. <input class="form-check-input" type="radio" name="projectType" id="empty" value="empty" v-model="projectType">
  137. <label class="form-check-label" for="empty">Empty Namespace</label>
  138. </div>
  139. <div class="form-check">
  140. <input class="form-check-input" type="radio" name="projectType" id="ua" value="ua" v-model="projectType" checked>
  141. <label class="form-check-label" for="ua">http://opcfoundation.org/UA/ Namespace</label>
  142. </div>
  143. <div class="form-check">
  144. <input class="form-check-input" type="radio" name="projectType" id="machinetool" value="machinetool"
  145. v-model="projectType">
  146. <label class="form-check-label" for="machinetool">Machinetool Namespace +dependencies</label>
  147. </div>
  148. <div class="form-check">
  149. <input class="form-check-input" type="radio" name="projectType" id="emco" value="emco" v-model="projectType">
  150. <label class="form-check-label" for="emco">Full EMCO Project</label>
  151. </div>
  152. <div class="btn-group d-flex justify-content-end">
  153. <button class="btn btn-light" @click="newProjectOpen = false" :disabled="newProjectDisabled">Cancel</button>
  154. <button class="btn btn-light" @click="newProject()" :disabled="newProjectDisabled">OK</button>
  155. </div>
  156. </v-dialog>
  157. <v-dialog :open="serverConfigOpen" @cancel="serverConfigOpen = false">
  158. <h5>Server Configuration</h5>
  159. <p></p>
  160. <div v-if="serverConfig">
  161. <div class="input-group mb-3">
  162. <div class="input-group-prepend">
  163. <span class="input-group-text" id="inputGroup-sizing-default">Manufacturer Name</span>
  164. </div>
  165. <input type="text" class="form-control" aria-label="Default" aria-describedby="inputGroup-sizing-default" v-model="serverConfig.configData.manufacturerName">
  166. </div>
  167. <div class="input-group mb-3">
  168. <div class="input-group-prepend">
  169. <span class="input-group-text" id="inputGroup-sizing-default">Product Name</span>
  170. </div>
  171. <input type="text" class="form-control" aria-label="Default" aria-describedby="inputGroup-sizing-default" v-model="serverConfig.configData.productName">
  172. </div>
  173. <div class="input-group mb-3">
  174. <div class="input-group-prepend">
  175. <span class="input-group-text" id="inputGroup-sizing-default">Software Version</span>
  176. </div>
  177. <input type="text" class="form-control" aria-label="Default" aria-describedby="inputGroup-sizing-default" v-model="serverConfig.configData.softwareVersion">
  178. </div>
  179. <div class="input-group mb-3">
  180. <div class="input-group-prepend">
  181. <span class="input-group-text" id="inputGroup-sizing-default">Application Name</span>
  182. </div>
  183. <input type="text" class="form-control" aria-label="Default" aria-describedby="inputGroup-sizing-default" v-model="serverConfig.configData.applicationName">
  184. </div>
  185. <div class="input-group mb-3">
  186. <div class="input-group-prepend">
  187. <span class="input-group-text" id="inputGroup-sizing-default">Application Uri</span>
  188. </div>
  189. <input type="text" class="form-control" aria-label="Default" aria-describedby="inputGroup-sizing-default" v-model="serverConfig.configData.applicationUri">
  190. </div>
  191. <div class="input-group mb-3">
  192. <div class="input-group-prepend">
  193. <span class="input-group-text" id="inputGroup-sizing-default">Product Uri</span>
  194. </div>
  195. <input type="text" class="form-control" aria-label="Default" aria-describedby="inputGroup-sizing-default" v-model="serverConfig.configData.productUri">
  196. </div>
  197. <div class="input-group mb-3">
  198. <div class="input-group-prepend">
  199. <span class="input-group-text" id="inputGroup-sizing-default">Port</span>
  200. </div>
  201. <input type="text" class="form-control" aria-label="Default" aria-describedby="inputGroup-sizing-default" v-model="serverConfig.configData.port">
  202. </div>
  203. <div class="input-group mb-3">
  204. <div class="input-group-prepend">
  205. <span class="input-group-text" id="inputGroup-sizing-default">Max Connections Per Endpoint</span>
  206. </div>
  207. <input type="text" class="form-control" aria-label="Default" aria-describedby="inputGroup-sizing-default" v-model="serverConfig.configData.maxConnections">
  208. </div>
  209. <div class="input-group mb-3">
  210. <div class="input-group-prepend form-check">
  211. <input class="form-check-input" type="checkbox" id="checkAllowAnon" v-model="serverConfig.configData.allowAnonymous" />
  212. <label class="form-check-label" for="checkAllowAnon">Allow Anonymous</label>
  213. </div>
  214. </div>
  215. </div>
  216. <div class="btn-group d-flex justify-content-end">
  217. <button class="btn btn-light" @click="serverConfigOpen = false">OK</button>
  218. </div>
  219. </v-dialog>
  220. </template>
  221. <style></style>