Spaces:
Running
Running
IEC 63339 references added
Browse files- 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
|
| 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 |
-
|
| 76 |
-
.
|
| 77 |
-
.card {
|
| 78 |
-
.
|
| 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:
|
| 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 |
-
<!--
|
| 99 |
<div class="deck-row deck-row-top">
|
| 100 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
-
<
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
-
<!--
|
| 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 |
-
<
|
| 172 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 173 |
<div id="tree-container"></div>
|
| 174 |
</div>
|
| 175 |
|
|
|
|
| 176 |
<div class="card" style="flex: 1;">
|
| 177 |
-
<h4>
|
| 178 |
-
|
| 179 |
-
|
| 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'
|
| 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 |
-
|
| 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}`;
|