Alef1986 commited on
Commit
3970c80
·
verified ·
1 Parent(s): d444c41

IEC 63339 references added

Browse files
Files changed (1) hide show
  1. index.html +136 -182
index.html CHANGED
@@ -2,15 +2,13 @@
2
  <html lang="sv">
3
  <head>
4
  <meta charset="UTF-8">
5
- <title>Knowledge Architecture Workbench (BFO vs ISO 15926 | LPG vs RDF)</title>
6
- <!-- Third-party CDN libraries: Mermaid.js & Vis.js -->
7
  <script src="https://cdn.jsdelivr.net/npm/mermaid@10.9.8/dist/mermaid.min.js"></script>
8
  <script src="https://unpkg.com/vis-network/standalone/umd/vis-network.min.js"></script>
9
  <style>
10
  * { box-sizing: border-box; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; }
11
  body { margin: 0; padding: 12px; background: #f8fafc; color: #0f172a; display: flex; flex-direction: column; height: 100vh; }
12
 
13
- /* Dual-tier control console */
14
  .control-deck { background: #ffffff; border: 1px solid #cbd5e1; border-radius: 8px; margin-bottom: 10px; box-shadow: 0 1px 3px rgba(0,0,0,0.05); }
15
  .deck-row { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; padding: 8px 14px; }
16
  .deck-row-top { background: #f1f5f9; border-bottom: 1px solid #e2e8f0; border-top-left-radius: 7px; border-top-right-radius: 7px; }
@@ -18,6 +16,56 @@
18
  .badge { background: #0284c7; color: #ffffff; font-weight: bold; padding: 4px 10px; border-radius: 6px; font-size: 13px; }
19
  .section-label { font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.05em; color: #475569; display: flex; align-items: center; gap: 4px; }
20
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
21
  button { background: #0f172a; color: #ffffff; border: none; padding: 5px 12px; border-radius: 6px; cursor: pointer; font-size: 12px; font-weight: 500; }
22
  button:hover { background: #334155; }
23
  button.secondary { background: #e2e8f0; color: #0f172a; }
@@ -40,7 +88,6 @@
40
  #mermaid-container { width: 100%; height: 100%; position: absolute; top: 0; left: 0; overflow: auto; padding: 24px 24px 140px 24px; display: flex; justify-content: center; align-items: flex-start; background: #ffffff; }
41
  #mermaid-container svg { width: auto; height: auto; max-width: none; }
42
 
43
- /* Dynamic graph legend badge */
44
  .graph-legend {
45
  position: absolute;
46
  bottom: 12px;
@@ -61,7 +108,6 @@
61
  .legend-section-title { font-weight: 600; color: #64748b; font-size: 10px; margin-bottom: 3px; text-transform: uppercase; }
62
  .legend-item { display: flex; align-items: center; margin-bottom: 3px; line-height: 1.25; }
63
 
64
- /* Legend glyph swatches */
65
  .swatch-box-sys { width: 13px; height: 11px; background: #0284c7; border: 1px solid #0369a1; border-radius: 2px; margin-right: 8px; flex-shrink: 0; }
66
  .swatch-box-elem { width: 13px; height: 11px; background: #64748b; border: 1px solid #475569; border-radius: 2px; margin-right: 8px; flex-shrink: 0; }
67
  .swatch-box-4d { width: 13px; height: 11px; background: #4f46e5; border: 1px solid #3730a3; border-radius: 2px; margin-right: 8px; flex-shrink: 0; }
@@ -72,32 +118,34 @@
72
  .swatch-line-past { width: 16px; height: 0; border-top: 2px dashed #94a3b8; margin-right: 8px; flex-shrink: 0; }
73
  .swatch-line-transition { width: 16px; height: 2px; background: #f59e0b; margin-right: 8px; flex-shrink: 0; }
74
 
75
- /* Right sidebar container */
76
- .sidebar { flex: 1; display: flex; flex-direction: column; gap: 8px; min-width: 350px; }
77
- .card { background: #ffffff; border: 1px solid #e2e8f0; border-radius: 8px; padding: 10px 12px; overflow-y: auto; }
78
- .card h4 { margin: 0 0 6px 0; font-size: 11px; text-transform: uppercase; letter-spacing: 0.05em; color: #64748b; display: flex; justify-content: space-between; }
79
-
80
- ul.tree, ul.tree ul { list-style-type: none; padding-left: 14px; margin: 2px 0; }
81
- ul.tree li { position: relative; padding: 2px 0; font-size: 12px; }
82
  ul.tree li::before { content: "├─ "; color: #94a3b8; }
83
  ul.tree li:last-child::before { content: "└─ "; color: #94a3b8; }
84
-
85
  table { width: 100%; border-collapse: collapse; font-size: 11px; }
86
- th, td { text-align: left; padding: 4px 6px; border-bottom: 1px solid #f1f5f9; }
87
  th { color: #64748b; font-weight: 600; }
88
  .active-row { background: #f0fdf4; }
89
  .closed-row { color: #94a3b8; text-decoration: line-through; }
90
-
91
- pre.code-block { margin: 0; font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace; font-size: 11px; color: #1e293b; line-height: 1.35; background: #f8fafc; padding: 8px; border-radius: 4px; border: 1px solid #e2e8f0; max-height: 140px; overflow-y: auto; white-space: pre-wrap; }
92
  </style>
93
  </head>
94
  <body>
95
 
96
- <!-- Dual-tier control console -->
97
  <div class="control-deck">
98
- <!-- Tier 1: Architectural paradigms (Ontology + Graph Model + Rendering Engine) -->
99
  <div class="deck-row deck-row-top">
100
- <div class="section-label">🏛️ <span id="lbl-onto-title">Ontologi:</span></div>
 
 
 
 
 
 
 
101
  <div class="btn-group">
102
  <button id="onto-bfo" class="active" onclick="setOntology('BFO')">BFO (3D Continuant)</button>
103
  <button id="onto-iso" onclick="setOntology('ISO15926')">ISO 15926 (4D Perdurant)</button>
@@ -105,7 +153,13 @@
105
 
106
  <div class="divider"></div>
107
 
108
- <div class="section-label">💾 <span id="lbl-graph-title">Datamodell:</span></div>
 
 
 
 
 
 
109
  <div class="btn-group">
110
  <button id="mode-lpg" class="active" onclick="setGraphMode('LPG')">LPG</button>
111
  <button id="mode-rdf" onclick="setGraphMode('RDF')">RDF (W3C)</button>
@@ -113,6 +167,7 @@
113
 
114
  <div class="divider"></div>
115
 
 
116
  <div class="section-label">🎨 <span id="lbl-engine-title">Motor:</span></div>
117
  <div class="btn-group">
118
  <button id="eng-vis" class="active" onclick="setEngine('vis')">🌐 Vis.js</button>
@@ -126,13 +181,21 @@
126
  </div>
127
  </div>
128
 
129
- <!-- Tier 2: Operational engineering actions & file persistence -->
130
  <div class="deck-row">
131
  <span class="badge" id="time-display">T = 0</span>
132
  <button id="step-btn" onclick="advanceTime()"></button>
133
 
134
  <div class="divider"></div>
135
 
 
 
 
 
 
 
 
 
136
  <label id="lbl-parent" style="font-size: 11px; color: #64748b; font-weight: 600;"></label>
137
  <select id="parent-select"></select>
138
 
@@ -148,38 +211,34 @@
148
  <label style="font-size: 11px; color: #475569; display: flex; align-items: center; gap: 4px;">
149
  <input type="checkbox" id="history-toggle" checked onchange="render()"> <span id="lbl-history"></span>
150
  </label>
151
-
152
- <!-- Client-side export and import controls -->
153
- <div style="margin-left: auto; display: flex; gap: 6px;">
154
- <button id="btn-export" class="secondary" onclick="exportStateToFile()">💾 <span id="lbl-export">Exportera</span></button>
155
- <button id="btn-import" class="secondary" onclick="document.getElementById('file-importer').click()">📂 <span id="lbl-import">Importera</span></button>
156
- <input type="file" id="file-importer" accept=".json" style="display: none;" onchange="importStateFromFile(event)">
157
- </div>
158
  </div>
159
  </div>
160
 
161
  <div class="main-container">
162
- <!-- Visualisation canvas with dual rendering container -->
163
  <div id="canvas-container">
164
  <div id="vis-container"></div>
165
  <div id="mermaid-container" style="display: none;"></div>
166
  <div id="graph-legend" class="graph-legend"></div>
167
  </div>
168
 
169
- <!-- Sidebar: 3 inspection cards -->
170
  <div class="sidebar">
171
- <div class="card" style="flex: 0 0 25%;">
172
- <h4><span id="title-bom"></span> <span style="color:#0284c7; font-weight: normal;">URMSM / Viewpoint</span></h4>
 
 
 
 
 
 
173
  <div id="tree-container"></div>
174
  </div>
175
 
 
176
  <div class="card" style="flex: 1;">
177
- <h4><span id="title-query"></span> <span id="query-lang-tag" style="color:#0284c7; font-weight: normal;"></span></h4>
178
- <div id="query-content"></div>
179
- </div>
180
-
181
- <div class="card" style="flex: 1;">
182
- <h4><span id="title-storage"></span> <span id="storage-paradigm-tag" style="color:#0284c7; font-weight: normal;"></span></h4>
183
  <div id="storage-content"></div>
184
  </div>
185
  </div>
@@ -204,16 +263,20 @@
204
  addBtn: "+ Lägg till",
205
  removeBtn: "Uteslut relation",
206
  history: "Historik",
207
- exportBtn: "Exportera JSON",
208
- importBtn: "Importera JSON",
209
  titleBom: "Struktur (BOM-träd)",
210
- titleQuery: "Genererad databasfråga",
211
  titleStorage: "Under huven",
212
  activeStatus: "Aktiv",
213
  closedStatus: "Avslutad",
214
  sourceCol: "Källa",
215
  targetCol: "Mål",
216
  statusCol: "Status",
 
 
 
 
 
 
 
217
  legend: {
218
  title: "Teckenförklaring",
219
  nodes: "Noder",
@@ -242,16 +305,20 @@
242
  addBtn: "+ Add",
243
  removeBtn: "Sever relation",
244
  history: "History",
245
- exportBtn: "Export JSON",
246
- importBtn: "Import JSON",
247
  titleBom: "BOM Tree Structure",
248
- titleQuery: "Generated Database Query",
249
  titleStorage: "Under the Bonnet",
250
  activeStatus: "Active",
251
  closedStatus: "Closed",
252
  sourceCol: "Source",
253
  targetCol: "Target",
254
  statusCol: "Status",
 
 
 
 
 
 
 
255
  legend: {
256
  title: "Graph Legend",
257
  nodes: "Node Types",
@@ -280,16 +347,20 @@
280
  addBtn: "+ Добавить",
281
  removeBtn: "Исключить связь",
282
  history: "История",
283
- exportBtn: "Экспорт JSON",
284
- importBtn: "Импорт JSON",
285
  titleBom: "Состав изделия (BOM)",
286
- titleQuery: "Генерируемый запрос к БД",
287
  titleStorage: "Под капотом",
288
  activeStatus: "Активна",
289
  closedStatus: "Закрыта",
290
  sourceCol: "Источник",
291
  targetCol: "Цель",
292
  statusCol: "Статус",
 
 
 
 
 
 
 
293
  legend: {
294
  title: "Легенда графа",
295
  nodes: "Типы узлов",
@@ -310,29 +381,24 @@
310
  }
311
  };
312
 
313
- // System state variables
314
  let currentLang = 'sv';
315
- let currentOntology = 'BFO'; // 'BFO' (3D) | 'ISO15926' (4D)
316
  let currentMode = 'LPG'; // 'LPG' | 'RDF'
317
  let currentEngine = 'vis'; // 'vis' | 'mermaid'
318
  let currentTime = 0;
319
  const ROOT_ID = 'System_A';
320
 
321
- // Canonical inventory of items
322
  const baseNodes = [
323
  { id: ROOT_ID, label: 'System_A', type: 'system' }
324
  ];
325
 
326
- // Master temporal edge storage
327
  let edgesStore = [];
328
  let edgeSeq = 1;
329
  let mermaidCounter = 0;
330
 
331
- // Concurrency queue for Mermaid rendering
332
  let isRenderingMermaid = false;
333
  let pendingMermaidDef = null;
334
 
335
- // Vis.js dataset instances
336
  const visNodes = new vis.DataSet();
337
  const visEdges = new vis.DataSet();
338
  const visContainer = document.getElementById('vis-container');
@@ -392,11 +458,15 @@
392
  document.getElementById('add-btn').innerText = t.addBtn;
393
  document.getElementById('remove-btn').innerText = t.removeBtn;
394
  document.getElementById('lbl-history').innerText = t.history;
395
- document.getElementById('lbl-export').innerText = t.exportBtn;
396
- document.getElementById('lbl-import').innerText = t.importBtn;
397
  document.getElementById('title-bom').innerText = t.titleBom;
398
- document.getElementById('title-query').innerText = t.titleQuery;
399
  document.getElementById('title-storage').innerText = t.titleStorage;
 
 
 
 
 
 
 
400
 
401
  render();
402
  }
@@ -479,64 +549,6 @@
479
  render();
480
  }
481
 
482
- // === Client-side JSON state persistence ===
483
- function exportStateToFile() {
484
- const statePayload = {
485
- version: "1.0",
486
- exportedAt: new Date().toISOString(),
487
- currentTime,
488
- currentOntology,
489
- currentMode,
490
- edgeSeq,
491
- baseNodes,
492
- edgesStore
493
- };
494
-
495
- const jsonBlob = new Blob([JSON.stringify(statePayload, null, 2)], { type: "application/json" });
496
- const downloadUrl = URL.createObjectURL(jsonBlob);
497
- const downloadLink = document.createElement("a");
498
- downloadLink.href = downloadUrl;
499
- downloadLink.download = `workbench_state_T${currentTime}_${currentOntology}_${currentMode}.json`;
500
- downloadLink.click();
501
- URL.revokeObjectURL(downloadUrl);
502
- }
503
-
504
- function importStateFromFile(event) {
505
- const selectedFile = event.target.files[0];
506
- if (!selectedFile) return;
507
-
508
- const fileReader = new FileReader();
509
- fileReader.onload = function(e) {
510
- try {
511
- const parsed = JSON.parse(e.target.result);
512
- if (!parsed.baseNodes || !parsed.edgesStore) {
513
- throw new Error("Invalid schema format: missing baseNodes or edgesStore.");
514
- }
515
-
516
- currentTime = parsed.currentTime || 0;
517
- currentOntology = parsed.currentOntology || 'BFO';
518
- currentMode = parsed.currentMode || 'LPG';
519
- edgeSeq = parsed.edgeSeq || (parsed.edgesStore.length + 1);
520
-
521
- baseNodes.length = 0;
522
- parsed.baseNodes.forEach(node => baseNodes.push(node));
523
-
524
- edgesStore.length = 0;
525
- parsed.edgesStore.forEach(edge => edgesStore.push(edge));
526
-
527
- document.getElementById('time-display').innerText = `T = ${currentTime}`;
528
- setOntology(currentOntology);
529
- setGraphMode(currentMode);
530
- render();
531
- } catch (err) {
532
- alert("Error importing workbench configuration: " + err.message);
533
- }
534
- };
535
-
536
- fileReader.readAsText(selectedFile);
537
- event.target.value = '';
538
- }
539
-
540
  function buildTreeHTML(nodeId, activeEdges) {
541
  const children = activeEdges.filter(e => e.from === nodeId).map(e => e.to);
542
  if (children.length === 0) return '';
@@ -608,6 +620,7 @@
608
  });
609
  }
610
  } else {
 
611
  baseNodes.forEach(n => {
612
  visNodes.add({
613
  id: `4d_${n.id}`,
@@ -648,7 +661,6 @@
648
  color: e.isActive ? { color: '#10b981' } : { color: '#cbd5e1' },
649
  width: e.isActive ? 2 : 1
650
  });
651
-
652
  if (!parentStates[e.from]) parentStates[e.from] = [];
653
  parentStates[e.from].push({ id: parentStateId, edge: e });
654
  } else {
@@ -728,6 +740,7 @@
728
  });
729
  }
730
  } else {
 
731
  baseNodes.forEach(n => {
732
  const wId = `whole_${safeId(n.id)}`;
733
  def += ` ${wId}["<b>${n.label}_4D</b><br/>iso:ActualIndividual"]:::iso4d\n`;
@@ -830,6 +843,7 @@
830
  `;
831
  }
832
  } else {
 
833
  const extraLpgEdge = (currentMode === 'LPG')
834
  ? `<div class="legend-item"><span class="swatch-line-transition"></span>${leg.isoTransitionRel}</div>`
835
  : '';
@@ -851,68 +865,6 @@
851
  }
852
  }
853
 
854
- function generateDatabaseQuery() {
855
- const queryTag = document.getElementById('query-lang-tag');
856
- const queryContainer = document.getElementById('query-content');
857
-
858
- if (currentOntology === 'BFO') {
859
- if (currentMode === 'LPG') {
860
- queryTag.innerText = 'Cypher (Neo4j / LPG)';
861
- const cypher = `// Retrieve entire active BOM hierarchy of ${ROOT_ID} at T = ${currentTime}\n` +
862
- `MATCH path = (root:System {id: '${ROOT_ID}'})-[:HAS_PART*]->(leaf:Element)\n` +
863
- `WHERE ALL(r IN relationships(path)\n` +
864
- ` WHERE r.valid_from <= ${currentTime} AND (r.valid_to > ${currentTime} OR r.valid_to IS NULL))\n` +
865
- `RETURN [n IN nodes(path) | n.id] AS bom_path,\n` +
866
- ` leaf.id AS component_id;\n` +
867
- `// Complexity: O(k^d) traversal with in-memory relationship filtering`;
868
- queryContainer.innerHTML = `<pre class="code-block">${cypher}</pre>`;
869
- } else {
870
- queryTag.innerText = 'SPARQL 1.1 (W3C RDF)';
871
- const sparql = `# Retrieve active BOM for :${ROOT_ID} at T = ${currentTime} via reified statements\n` +
872
- `PREFIX : <http://example.org/engineering#>\n` +
873
- `PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>\n\n` +
874
- `SELECT ?parent ?child\n` +
875
- `WHERE {\n` +
876
- ` ?rel a :TemporalPartMembership ;\n` +
877
- ` :hasParent ?parent ;\n` +
878
- ` :hasChild ?child ;\n` +
879
- ` :validFrom ?validFrom .\n` +
880
- ` OPTIONAL { ?rel :validTo ?validTo . }\n` +
881
- ` FILTER (?validFrom <= ${currentTime} && (!BOUND(?validTo) || ?validTo > ${currentTime}))\n` +
882
- `}\n` +
883
- `# Complexity: Join-intensive index scan over intermediate statement nodes`;
884
- queryContainer.innerHTML = `<pre class="code-block">${sparql}</pre>`;
885
- }
886
- } else {
887
- if (currentMode === 'LPG') {
888
- queryTag.innerText = 'Cypher (ISO 15926 in LPG)';
889
- const cypher = `// Traverse 4D states intersecting T = ${currentTime}\n` +
890
- `MATCH (root4D:ActualIndividual {id: '4d_${ROOT_ID}'})<-[:temporalPartOf]-(rootState:State)\n` +
891
- `WHERE rootState.valid_from <= ${currentTime} AND (rootState.valid_to > ${currentTime} OR rootState.valid_to IS NULL)\n` +
892
- `MATCH path = (rootState)-[:COMPOSED_OF*]->(leafState:State)-[:temporalPartOf]->(leaf4D:ActualIndividual)\n` +
893
- `RETURN root4D.id AS root, leaf4D.id AS component, leafState.id AS active_slice;\n` +
894
- `// Benefit: Native fast traversal over timeless composition links`;
895
- queryContainer.innerHTML = `<pre class="code-block">${cypher}</pre>`;
896
- } else {
897
- queryTag.innerText = 'SPARQL 1.1 (ISO 15926 Pure Binary Triples)';
898
- const sparql = `# Pure binary 4D navigation for :${ROOT_ID} at T = ${currentTime}\n` +
899
- `PREFIX iso: <http://standards.iso.org/iso/15926/part2#>\n` +
900
- `PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>\n\n` +
901
- `SELECT ?component4D ?activeStateSlice\n` +
902
- `WHERE {\n` +
903
- ` :${ROOT_ID}_4D ^iso:temporalPartOf ?rootState .\n` +
904
- ` ?rootState iso:validFrom ?from .\n` +
905
- ` OPTIONAL { ?rootState iso:validTo ?to . }\n` +
906
- ` FILTER (?from <= ${currentTime} && (!BOUND(?to) || ?to > ${currentTime}))\n\n` +
907
- ` ?rootState (iso:hasPart)+ ?activeStateSlice .\n` +
908
- ` ?activeStateSlice iso:temporalPartOf ?component4D .\n` +
909
- `}\n` +
910
- `# Benefit: No reification; uses standard property path (iso:hasPart)+`;
911
- queryContainer.innerHTML = `<pre class="code-block">${sparql}</pre>`;
912
- }
913
- }
914
- }
915
-
916
  function render() {
917
  const t = i18n[currentLang];
918
  const showHistory = document.getElementById('history-toggle').checked;
@@ -934,9 +886,11 @@
934
 
935
  updateLegend();
936
 
 
937
  const treeContainer = document.getElementById('tree-container');
938
  treeContainer.innerHTML = `<strong>${ROOT_ID}</strong>` + buildTreeHTML(ROOT_ID, activeEdges);
939
 
 
940
  const parentSelect = document.getElementById('parent-select');
941
  const removeSelect = document.getElementById('remove-select');
942
  parentSelect.innerHTML = '';
@@ -957,11 +911,10 @@
957
  removeSelect.appendChild(opt);
958
  });
959
 
960
- generateDatabaseQuery();
961
-
962
  const storageTag = document.getElementById('storage-paradigm-tag');
963
  const storageContent = document.getElementById('storage-content');
964
- storageTag.innerText = `${currentOntology} / ${currentMode}`;
965
 
966
  if (currentOntology === 'BFO') {
967
  if (currentMode === 'LPG') {
@@ -977,7 +930,7 @@
977
  html += '</tbody></table>';
978
  storageContent.innerHTML = html;
979
  } else {
980
- let ttl = `# BFO 3D: RDF Turtle with Reification\n`;
981
  edgesStore.forEach(e => {
982
  const isActive = (e.valid_to === null || edge.valid_to > currentTime) && edge.valid_from <= currentTime;
983
  if (!isActive && !showHistory) return;
@@ -989,6 +942,7 @@
989
  storageContent.innerHTML = `<pre class="code-block">${ttl}</pre>`;
990
  }
991
  } else {
 
992
  if (currentMode === 'LPG') {
993
  let html = `<table><thead><tr><th>Parent Slice</th><th>Child Slice</th><th>LPG Rel Properties</th><th>4D Extent</th></tr></thead><tbody>`;
994
  visibleEdges.forEach(e => {
@@ -1002,7 +956,7 @@
1002
  html += '</tbody></table>';
1003
  storageContent.innerHTML = html;
1004
  } else {
1005
- let ttl = `# ISO 15926-2: Pure Binary RDF Triples (Tenseless Composition)\n`;
1006
  visibleEdges.forEach(e => {
1007
  const pState = `:${e.from}_state_${e.id}`;
1008
  const cState = `:${e.to}_state_${e.id}`;
 
2
  <html lang="sv">
3
  <head>
4
  <meta charset="UTF-8">
5
+ <title>IEC 63339 Knowledge Architecture Workbench</title>
 
6
  <script src="https://cdn.jsdelivr.net/npm/mermaid@10.9.8/dist/mermaid.min.js"></script>
7
  <script src="https://unpkg.com/vis-network/standalone/umd/vis-network.min.js"></script>
8
  <style>
9
  * { box-sizing: border-box; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; }
10
  body { margin: 0; padding: 12px; background: #f8fafc; color: #0f172a; display: flex; flex-direction: column; height: 100vh; }
11
 
 
12
  .control-deck { background: #ffffff; border: 1px solid #cbd5e1; border-radius: 8px; margin-bottom: 10px; box-shadow: 0 1px 3px rgba(0,0,0,0.05); }
13
  .deck-row { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; padding: 8px 14px; }
14
  .deck-row-top { background: #f1f5f9; border-bottom: 1px solid #e2e8f0; border-top-left-radius: 7px; border-top-right-radius: 7px; }
 
16
  .badge { background: #0284c7; color: #ffffff; font-weight: bold; padding: 4px 10px; border-radius: 6px; font-size: 13px; }
17
  .section-label { font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.05em; color: #475569; display: flex; align-items: center; gap: 4px; }
18
 
19
+ /* Интерактивные бейджи стандарта IEC 63339 с подсказками */
20
+ .iec-badge {
21
+ display: inline-flex;
22
+ align-items: center;
23
+ gap: 3px;
24
+ background: #e0f2fe;
25
+ color: #0369a1;
26
+ border: 1px solid #7dd3fc;
27
+ padding: 2px 7px;
28
+ border-radius: 4px;
29
+ font-size: 10px;
30
+ font-weight: 700;
31
+ letter-spacing: 0.04em;
32
+ cursor: help;
33
+ position: relative;
34
+ }
35
+ .iec-badge:hover .iec-tooltip {
36
+ visibility: visible;
37
+ opacity: 1;
38
+ }
39
+ .iec-tooltip {
40
+ visibility: hidden;
41
+ opacity: 0;
42
+ position: absolute;
43
+ top: 130%;
44
+ left: 0;
45
+ z-index: 100;
46
+ background: #0f172a;
47
+ color: #f8fafc;
48
+ padding: 8px 12px;
49
+ border-radius: 6px;
50
+ font-size: 11px;
51
+ font-weight: 400;
52
+ line-height: 1.35;
53
+ min-width: 250px;
54
+ max-width: 320px;
55
+ box-shadow: 0 4px 14px rgba(0,0,0,0.2);
56
+ transition: opacity 0.15s ease-in-out;
57
+ pointer-events: none;
58
+ }
59
+ .iec-tooltip::after {
60
+ content: "";
61
+ position: absolute;
62
+ bottom: 100%;
63
+ left: 14px;
64
+ border-width: 5px;
65
+ border-style: solid;
66
+ border-color: transparent transparent #0f172a transparent;
67
+ }
68
+
69
  button { background: #0f172a; color: #ffffff; border: none; padding: 5px 12px; border-radius: 6px; cursor: pointer; font-size: 12px; font-weight: 500; }
70
  button:hover { background: #334155; }
71
  button.secondary { background: #e2e8f0; color: #0f172a; }
 
88
  #mermaid-container { width: 100%; height: 100%; position: absolute; top: 0; left: 0; overflow: auto; padding: 24px 24px 140px 24px; display: flex; justify-content: center; align-items: flex-start; background: #ffffff; }
89
  #mermaid-container svg { width: auto; height: auto; max-width: none; }
90
 
 
91
  .graph-legend {
92
  position: absolute;
93
  bottom: 12px;
 
108
  .legend-section-title { font-weight: 600; color: #64748b; font-size: 10px; margin-bottom: 3px; text-transform: uppercase; }
109
  .legend-item { display: flex; align-items: center; margin-bottom: 3px; line-height: 1.25; }
110
 
 
111
  .swatch-box-sys { width: 13px; height: 11px; background: #0284c7; border: 1px solid #0369a1; border-radius: 2px; margin-right: 8px; flex-shrink: 0; }
112
  .swatch-box-elem { width: 13px; height: 11px; background: #64748b; border: 1px solid #475569; border-radius: 2px; margin-right: 8px; flex-shrink: 0; }
113
  .swatch-box-4d { width: 13px; height: 11px; background: #4f46e5; border: 1px solid #3730a3; border-radius: 2px; margin-right: 8px; flex-shrink: 0; }
 
118
  .swatch-line-past { width: 16px; height: 0; border-top: 2px dashed #94a3b8; margin-right: 8px; flex-shrink: 0; }
119
  .swatch-line-transition { width: 16px; height: 2px; background: #f59e0b; margin-right: 8px; flex-shrink: 0; }
120
 
121
+ .sidebar { flex: 1; display: flex; flex-direction: column; gap: 10px; min-width: 350px; }
122
+ .card { background: #ffffff; border: 1px solid #e2e8f0; border-radius: 8px; padding: 12px; overflow-y: auto; flex: 1; }
123
+ .card h4 { margin: 0 0 8px 0; font-size: 11px; text-transform: uppercase; letter-spacing: 0.05em; color: #64748b; display: flex; justify-content: space-between; align-items: center; }
124
+ ul.tree, ul.tree ul { list-style-type: none; padding-left: 16px; margin: 4px 0; }
125
+ ul.tree li { position: relative; padding: 2px 0; font-size: 13px; }
 
 
126
  ul.tree li::before { content: "├─ "; color: #94a3b8; }
127
  ul.tree li:last-child::before { content: "└─ "; color: #94a3b8; }
 
128
  table { width: 100%; border-collapse: collapse; font-size: 11px; }
129
+ th, td { text-align: left; padding: 5px 6px; border-bottom: 1px solid #f1f5f9; }
130
  th { color: #64748b; font-weight: 600; }
131
  .active-row { background: #f0fdf4; }
132
  .closed-row { color: #94a3b8; text-decoration: line-through; }
133
+ pre.code-block { margin: 0; font-family: monospace; font-size: 11px; color: #334155; line-height: 1.4; background: #f8fafc; padding: 8px; border-radius: 4px; border: 1px solid #e2e8f0; max-height: 180px; overflow-y: auto; }
 
134
  </style>
135
  </head>
136
  <body>
137
 
 
138
  <div class="control-deck">
139
+ <!-- ЯРУС 1: Архитектурные уровни IEC 63339 (SMIM & SMS) -->
140
  <div class="deck-row deck-row-top">
141
+
142
+ <!-- SMIM: Top-Level Ontology -->
143
+ <div class="section-label">
144
+ <span class="iec-badge">IEC 63339 / SMIM
145
+ <span class="iec-tooltip" id="tt-smim"></span>
146
+ </span>
147
+ <span id="lbl-onto-title">Ontologi:</span>
148
+ </div>
149
  <div class="btn-group">
150
  <button id="onto-bfo" class="active" onclick="setOntology('BFO')">BFO (3D Continuant)</button>
151
  <button id="onto-iso" onclick="setOntology('ISO15926')">ISO 15926 (4D Perdurant)</button>
 
153
 
154
  <div class="divider"></div>
155
 
156
+ <!-- SMS: Graph Database Storage -->
157
+ <div class="section-label">
158
+ <span class="iec-badge">IEC 63339 / SMS
159
+ <span class="iec-tooltip" id="tt-sms"></span>
160
+ </span>
161
+ <span id="lbl-graph-title">Datamodell:</span>
162
+ </div>
163
  <div class="btn-group">
164
  <button id="mode-lpg" class="active" onclick="setGraphMode('LPG')">LPG</button>
165
  <button id="mode-rdf" onclick="setGraphMode('RDF')">RDF (W3C)</button>
 
167
 
168
  <div class="divider"></div>
169
 
170
+ <!-- Визуализатор -->
171
  <div class="section-label">🎨 <span id="lbl-engine-title">Motor:</span></div>
172
  <div class="btn-group">
173
  <button id="eng-vis" class="active" onclick="setEngine('vis')">🌐 Vis.js</button>
 
181
  </div>
182
  </div>
183
 
184
+ <!-- ЯРУС 2: SMRM Concept Repository & Ввод действий -->
185
  <div class="deck-row">
186
  <span class="badge" id="time-display">T = 0</span>
187
  <button id="step-btn" onclick="advanceTime()"></button>
188
 
189
  <div class="divider"></div>
190
 
191
+ <!-- Зафиксированная Domain Ontology (SMRM) -->
192
+ <span class="iec-badge" style="background: #f1f5f9; color: #475569; border-color: #cbd5e1;">
193
+ IEC 63339 / SMRM: <span id="lbl-smrm-status" style="font-weight: normal; margin-left: 3px;">Domain Ontology (Fixed: System/IDO)</span>
194
+ <span class="iec-tooltip" id="tt-smrm"></span>
195
+ </span>
196
+
197
+ <div class="divider"></div>
198
+
199
  <label id="lbl-parent" style="font-size: 11px; color: #64748b; font-weight: 600;"></label>
200
  <select id="parent-select"></select>
201
 
 
211
  <label style="font-size: 11px; color: #475569; display: flex; align-items: center; gap: 4px;">
212
  <input type="checkbox" id="history-toggle" checked onchange="render()"> <span id="lbl-history"></span>
213
  </label>
 
 
 
 
 
 
 
214
  </div>
215
  </div>
216
 
217
  <div class="main-container">
 
218
  <div id="canvas-container">
219
  <div id="vis-container"></div>
220
  <div id="mermaid-container" style="display: none;"></div>
221
  <div id="graph-legend" class="graph-legend"></div>
222
  </div>
223
 
 
224
  <div class="sidebar">
225
+ <!-- URMSM: Viewpoint & Concerns -->
226
+ <div class="card" style="flex: 0 0 35%;">
227
+ <h4>
228
+ <span id="title-bom"></span>
229
+ <span class="iec-badge">IEC 63339 / URMSM
230
+ <span class="iec-tooltip" id="tt-urmsm"></span>
231
+ </span>
232
+ </h4>
233
  <div id="tree-container"></div>
234
  </div>
235
 
236
+ <!-- SMS: Low-Level Storage Details -->
237
  <div class="card" style="flex: 1;">
238
+ <h4>
239
+ <span id="title-storage"></span>
240
+ <span id="storage-paradigm-tag" style="color:#0284c7; font-weight: 600;"></span>
241
+ </h4>
 
 
242
  <div id="storage-content"></div>
243
  </div>
244
  </div>
 
263
  addBtn: "+ Lägg till",
264
  removeBtn: "Uteslut relation",
265
  history: "Historik",
 
 
266
  titleBom: "Struktur (BOM-träd)",
 
267
  titleStorage: "Under huven",
268
  activeStatus: "Aktiv",
269
  closedStatus: "Avslutad",
270
  sourceCol: "Källa",
271
  targetCol: "Mål",
272
  statusCol: "Status",
273
+ smrmStatus: "Domänontologi (Låst: System/IDO)",
274
+ tooltips: {
275
+ urmsm: "<b>URMSM (Dimension & Aspect)</b><br/>Definierar intressentens perspektiv (ISO 42010 Viewpoint). Användaren ser bara BOM-strukturen vid tidpunkten T, oavsett underliggande IT-lager.",
276
+ smrm: "<b>SMRM (Concept Repository)</b><br/>Domänontologi och gemensam begreppsbank (System, Component, Physical/Functional Object). Här låst till standardiserad produktnedbrytning.",
277
+ smim: "<b>SMIM (Information Elements & Rules)</b><br/>Top-Level Ontology (TLO). Formella regler för hur begreppen existerar i tiden: 3D-objekt med tidsintervall (BFO) eller 4D-rumtidsindivider (ISO 15926).",
278
+ sms: "<b>SMS (Data in Tool)</b><br/>Den faktiska IT-implementationen och datalagringen (Property Graph / Neo4j kontra W3C RDF Triples)."
279
+ },
280
  legend: {
281
  title: "Teckenförklaring",
282
  nodes: "Noder",
 
305
  addBtn: "+ Add",
306
  removeBtn: "Sever relation",
307
  history: "History",
 
 
308
  titleBom: "BOM Tree Structure",
 
309
  titleStorage: "Under the Bonnet",
310
  activeStatus: "Active",
311
  closedStatus: "Closed",
312
  sourceCol: "Source",
313
  targetCol: "Target",
314
  statusCol: "Status",
315
+ smrmStatus: "Domain Ontology (Fixed: System/IDO)",
316
+ tooltips: {
317
+ urmsm: "<b>URMSM (Dimension & Aspect)</b><br/>Captures the stakeholder concern and viewpoint (ISO 42010). The engineer always sees a unified BOM snapshot at time T, decoupled from graph mechanics.",
318
+ smrm: "<b>SMRM (Concept Repository)</b><br/>Domain Ontology: the shared engineering lexicon (System, System Element, Physical/Functional Object). Fixed here to ISO-aligned decomposition.",
319
+ smim: "<b>SMIM (Information Elements & Rules)</b><br/>Top-Level Ontology (TLO). Axiomatic rules governing time and identity: 3D Continuants (BFO) vs 4D Spatio-Temporal Extents (ISO 15926).",
320
+ sms: "<b>SMS (Data in Tool)</b><br/>Low-level physical IT implementation: Labeled Property Graphs (Neo4j style) vs W3C RDF Triples and serialized statements."
321
+ },
322
  legend: {
323
  title: "Graph Legend",
324
  nodes: "Node Types",
 
347
  addBtn: "+ Добавить",
348
  removeBtn: "Исключить связь",
349
  history: "История",
 
 
350
  titleBom: "Состав изделия (BOM)",
 
351
  titleStorage: "Под капотом",
352
  activeStatus: "Активна",
353
  closedStatus: "Закрыта",
354
  sourceCol: "Источник",
355
  targetCol: "Цель",
356
  statusCol: "Статус",
357
+ smrmStatus: "Domain Ontology (Зафиксирована: System/IDO)",
358
+ tooltips: {
359
+ urmsm: "<b>URMSM (Dimension & Aspect)</b><br/>Уровень точки зрения пользователя (ISO 42010). Инженер всегда видит чистый срез BOM на момент времени T, абстрагированный от структуры базы данных.",
360
+ smrm: "<b>SMRM (Concept Repository)</b><br/>Онтология предметной области (Domain Ontology). Общий словарь понятий проекта (System, Component, Physical Object). Зафиксирована на базовой декомпозиции.",
361
+ smim: "<b>SMIM (Information Elements & Rules)</b><br/>Верхнеуровневая онтология (TLO). Математические аксиомы бытия и времени: 3D-тела с интервалами связей (BFO) или 4D-объемы пространства-времени (ISO 15926).",
362
+ sms: "<b>SMS (Data in Tool)</b><br/>Физическое IT-хранилище в конкретной СУБД: узлы и свойства на ребрах (LPG) против атомарных триплетов URI (W3C RDF)."
363
+ },
364
  legend: {
365
  title: "Легенда графа",
366
  nodes: "Типы узлов",
 
381
  }
382
  };
383
 
 
384
  let currentLang = 'sv';
385
+ let currentOntology = 'BFO'; // 'BFO' | 'ISO15926'
386
  let currentMode = 'LPG'; // 'LPG' | 'RDF'
387
  let currentEngine = 'vis'; // 'vis' | 'mermaid'
388
  let currentTime = 0;
389
  const ROOT_ID = 'System_A';
390
 
 
391
  const baseNodes = [
392
  { id: ROOT_ID, label: 'System_A', type: 'system' }
393
  ];
394
 
 
395
  let edgesStore = [];
396
  let edgeSeq = 1;
397
  let mermaidCounter = 0;
398
 
 
399
  let isRenderingMermaid = false;
400
  let pendingMermaidDef = null;
401
 
 
402
  const visNodes = new vis.DataSet();
403
  const visEdges = new vis.DataSet();
404
  const visContainer = document.getElementById('vis-container');
 
458
  document.getElementById('add-btn').innerText = t.addBtn;
459
  document.getElementById('remove-btn').innerText = t.removeBtn;
460
  document.getElementById('lbl-history').innerText = t.history;
 
 
461
  document.getElementById('title-bom').innerText = t.titleBom;
 
462
  document.getElementById('title-storage').innerText = t.titleStorage;
463
+ document.getElementById('lbl-smrm-status').innerText = t.smrmStatus;
464
+
465
+ // Локализация тултипов IEC 63339
466
+ document.getElementById('tt-urmsm').innerHTML = t.tooltips.urmsm;
467
+ document.getElementById('tt-smrm').innerHTML = t.tooltips.smrm;
468
+ document.getElementById('tt-smim').innerHTML = t.tooltips.smim;
469
+ document.getElementById('tt-sms').innerHTML = t.tooltips.sms;
470
 
471
  render();
472
  }
 
549
  render();
550
  }
551
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
552
  function buildTreeHTML(nodeId, activeEdges) {
553
  const children = activeEdges.filter(e => e.from === nodeId).map(e => e.to);
554
  if (children.length === 0) return '';
 
620
  });
621
  }
622
  } else {
623
+ // ISO 15926
624
  baseNodes.forEach(n => {
625
  visNodes.add({
626
  id: `4d_${n.id}`,
 
661
  color: e.isActive ? { color: '#10b981' } : { color: '#cbd5e1' },
662
  width: e.isActive ? 2 : 1
663
  });
 
664
  if (!parentStates[e.from]) parentStates[e.from] = [];
665
  parentStates[e.from].push({ id: parentStateId, edge: e });
666
  } else {
 
740
  });
741
  }
742
  } else {
743
+ // ISO 15926
744
  baseNodes.forEach(n => {
745
  const wId = `whole_${safeId(n.id)}`;
746
  def += ` ${wId}["<b>${n.label}_4D</b><br/>iso:ActualIndividual"]:::iso4d\n`;
 
843
  `;
844
  }
845
  } else {
846
+ // ISO 15926
847
  const extraLpgEdge = (currentMode === 'LPG')
848
  ? `<div class="legend-item"><span class="swatch-line-transition"></span>${leg.isoTransitionRel}</div>`
849
  : '';
 
865
  }
866
  }
867
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
868
  function render() {
869
  const t = i18n[currentLang];
870
  const showHistory = document.getElementById('history-toggle').checked;
 
886
 
887
  updateLegend();
888
 
889
+ // URMSM BOM View
890
  const treeContainer = document.getElementById('tree-container');
891
  treeContainer.innerHTML = `<strong>${ROOT_ID}</strong>` + buildTreeHTML(ROOT_ID, activeEdges);
892
 
893
+ // Контролы
894
  const parentSelect = document.getElementById('parent-select');
895
  const removeSelect = document.getElementById('remove-select');
896
  parentSelect.innerHTML = '';
 
911
  removeSelect.appendChild(opt);
912
  });
913
 
914
+ // Storage Panel (SMS)
 
915
  const storageTag = document.getElementById('storage-paradigm-tag');
916
  const storageContent = document.getElementById('storage-content');
917
+ storageTag.innerText = `SMS / ${currentOntology} / ${currentMode}`;
918
 
919
  if (currentOntology === 'BFO') {
920
  if (currentMode === 'LPG') {
 
930
  html += '</tbody></table>';
931
  storageContent.innerHTML = html;
932
  } else {
933
+ let ttl = `# BFO 3D in SMS: RDF Turtle with Reification\n`;
934
  edgesStore.forEach(e => {
935
  const isActive = (e.valid_to === null || edge.valid_to > currentTime) && edge.valid_from <= currentTime;
936
  if (!isActive && !showHistory) return;
 
942
  storageContent.innerHTML = `<pre class="code-block">${ttl}</pre>`;
943
  }
944
  } else {
945
+ // ISO 15926
946
  if (currentMode === 'LPG') {
947
  let html = `<table><thead><tr><th>Parent Slice</th><th>Child Slice</th><th>LPG Rel Properties</th><th>4D Extent</th></tr></thead><tbody>`;
948
  visibleEdges.forEach(e => {
 
956
  html += '</tbody></table>';
957
  storageContent.innerHTML = html;
958
  } else {
959
+ let ttl = `# ISO 15926-2 in SMS: Pure Binary RDF Triples (Tenseless Composition)\n`;
960
  visibleEdges.forEach(e => {
961
  const pState = `:${e.from}_state_${e.id}`;
962
  const cState = `:${e.to}_state_${e.id}`;