Skip to content
Snippets Groups Projects
Commit 53d7f02a authored by Benedikt's avatar Benedikt
Browse files

aded first viewer

parent eb3ca792
Branches
No related tags found
No related merge requests found
# Default ignored files
/shelf/
/workspace.xml
# Editor-based HTTP Client requests
/httpRequests/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml
<?xml version="1.0" encoding="UTF-8"?>
<module type="WEB_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$">
<excludeFolder url="file://$MODULE_DIR$/.tmp" />
<excludeFolder url="file://$MODULE_DIR$/temp" />
<excludeFolder url="file://$MODULE_DIR$/tmp" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>
\ No newline at end of file
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="Eslint" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="PyPackageRequirementsInspection" enabled="true" level="WARNING" enabled_by_default="true">
<option name="ignoredPackages">
<value>
<list size="1">
<item index="0" class="java.lang.String" itemvalue="bokeh" />
</list>
</value>
</option>
</inspection_tool>
</profile>
</component>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/dccviewertypescript.iml" filepath="$PROJECT_DIR$/.idea/dccviewertypescript.iml" />
</modules>
</component>
</project>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
</component>
</project>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="AutoImportSettings">
<option name="autoReloadType" value="SELECTIVE" />
</component>
<component name="ChangeListManager">
<list default="true" id="c3d759c9-e9f8-41d4-b9b5-ef12db68e70c" name="Changes" comment="">
<change beforePath="$PROJECT_DIR$/../src/app.js" beforeDir="false" afterPath="$PROJECT_DIR$/../src/app.js" afterDir="false" />
<change beforePath="$PROJECT_DIR$/../src/main.js" beforeDir="false" afterPath="$PROJECT_DIR$/../src/main.js" afterDir="false" />
<change beforePath="$PROJECT_DIR$/../src/renderers/AdminRenderer.js" beforeDir="false" afterPath="$PROJECT_DIR$/../src/renderers/AdminRenderer.js" afterDir="false" />
<change beforePath="$PROJECT_DIR$/../src/renderers/MeasurementRenderer.js" beforeDir="false" afterPath="$PROJECT_DIR$/../src/renderers/MeasurementRenderer.js" afterDir="false" />
<change beforePath="$PROJECT_DIR$/../src/renderers/SelectRenderer.js" beforeDir="false" afterPath="$PROJECT_DIR$/../src/renderers/SelectRenderer.js" afterDir="false" />
<change beforePath="$PROJECT_DIR$/../src/styles.css" beforeDir="false" afterPath="$PROJECT_DIR$/../src/styles.css" afterDir="false" />
<change beforePath="$PROJECT_DIR$/../vite.config.js" beforeDir="false" afterPath="$PROJECT_DIR$/../vite.config.js" afterDir="false" />
</list>
<option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" />
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
<option name="LAST_RESOLUTION" value="IGNORE" />
</component>
<component name="FileTemplateManagerImpl">
<option name="RECENT_TEMPLATES">
<list>
<option value="JavaScript File" />
</list>
</option>
</component>
<component name="Git.Settings">
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$/.." />
</component>
<component name="ProjectColorInfo">{
&quot;associatedIndex&quot;: 6
}</component>
<component name="ProjectId" id="2tLWdqlIz5X4SCSwK5EjWvfiZtG" />
<component name="ProjectViewState">
<option name="hideEmptyMiddlePackages" value="true" />
<option name="showLibraryContents" value="true" />
</component>
<component name="PropertiesComponent"><![CDATA[{
"keyToString": {
"JavaScript Debug.Debug Vite.executor": "Run",
"JavaScript Debug.dccViewer.executor": "Debug",
"JavaScript Debug.index.html.executor": "Run",
"Node.js.vite.config.js.executor": "Run",
"RunOnceActivity.ShowReadmeOnStart": "true",
"RunOnceActivity.git.unshallow": "true",
"git-widget-placeholder": "main",
"last_opened_file_path": "/home/seeger01/repos/dccviewertypescript",
"node.js.detected.package.eslint": "true",
"node.js.detected.package.tslint": "true",
"node.js.selected.package.eslint": "(autodetect)",
"node.js.selected.package.tslint": "(autodetect)",
"nodejs_package_manager_path": "npm",
"settings.editor.selected.configurable": "http.proxy",
"ts.external.directory.path": "/home/seeger01/Downloads/WebStorm-243.24978.60/plugins/javascript-plugin/jsLanguageServicesImpl/external",
"vue.rearranger.settings.migration": "true"
}
}]]></component>
<component name="RunManager">
<configuration name="dccViewer" type="JavascriptDebugType" uri="http://localhost:5173/">
<method v="2" />
</configuration>
</component>
<component name="SharedIndexes">
<attachedChunks>
<set>
<option value="bundled-js-predefined-d6986cc7102b-76f8388c3a79-JavaScript-WS-243.24978.60" />
</set>
</attachedChunks>
</component>
<component name="SpellCheckerSettings" RuntimeDictionaries="0" Folders="0" CustomDictionaries="0" DefaultDictionary="application-level" UseSingleDictionary="true" transferred="true" />
<component name="TaskManager">
<task active="true" id="Default" summary="Default task">
<changelist id="c3d759c9-e9f8-41d4-b9b5-ef12db68e70c" name="Changes" comment="" />
<created>1740131925655</created>
<option name="number" value="Default" />
<option name="presentableId" value="Default" />
<updated>1740131925655</updated>
<workItem from="1740131927264" duration="102000" />
<workItem from="1740132052064" duration="10000" />
<workItem from="1740132146315" duration="24000" />
<workItem from="1740132175271" duration="89000" />
<workItem from="1740158794192" duration="1155000" />
<workItem from="1740380748268" duration="5753000" />
</task>
<servers />
</component>
<component name="TypeScriptGeneratedFilesManager">
<option name="version" value="3" />
</component>
</project>
\ No newline at end of file
This diff is collapsed.
import { parseString } from 'xml2js';
import JSONEditor from 'jsoneditor';
// Import renderer functions
import { renderAdminData } from './renderers/AdminRenderer.js';
// Import our interactive measurement renderer
import { renderMeasurementResults } from './renderers/MeasurementRenderer.js';
import { renderAdminData } from './renderers/AdminRenderer.js';
// Global configuration
const selectableLanguages = ['en', 'de', 'fr', 'es'];
......@@ -28,7 +27,7 @@ export function initApp() {
});
appContainer.appendChild(langSelect);
// Create containers for administrative data and measurement results
// Create containers for admin data and measurement results
const adminContainer = document.createElement('div');
adminContainer.id = 'adminData';
appContainer.appendChild(adminContainer);
......@@ -37,11 +36,10 @@ export function initApp() {
measContainer.id = 'measurementResults';
appContainer.appendChild(measContainer);
// Load the embedded XML file (for testing) from the data folder
// Load the XML file from the data folder
fetch('/data/sin_acceleration_example_dcc_WithExampleConformatyStatment.xml')
.then(response => response.text())
.then(xmlText => {
// Convert XML to JSON using xml2js
parseString(xmlText, { explicitArray: false }, (err, result) => {
if (err) {
console.error('Error parsing XML:', err);
......@@ -56,12 +54,9 @@ export function initApp() {
function renderAll() {
// Clear containers
const adminContainer = document.getElementById('adminData');
adminContainer.innerHTML = '';
const measContainer = document.getElementById('measurementResults');
measContainer.innerHTML = '';
document.getElementById('adminData').innerHTML = '';
document.getElementById('measurementResults').innerHTML = '';
// Render the content if XML has been loaded and converted
if (dccData && dccData['dcc:digitalCalibrationCertificate']) {
const cert = dccData['dcc:digitalCalibrationCertificate'];
if (cert['dcc:administrativeData']) {
......
import { initApp } from './app.js';
// Wait for the DOM to be loaded before initializing
// Initialize after DOM loads
document.addEventListener('DOMContentLoaded', () => {
initApp();
});
......@@ -3,20 +3,16 @@ import 'jsoneditor/dist/jsoneditor.css';
export function renderAdminData(adminData, language) {
const container = document.getElementById('adminData');
// Title for admin data
const title = document.createElement('h2');
title.textContent = 'Administrative Data (' + language + ')';
container.appendChild(title);
// JSONEditor options (read-only view)
const options = {
mode: 'view',
mainMenuBar: false,
navigationBar: false,
statusBar: false
};
// Create a new JSONEditor instance to display the adminData
const editor = new JSONEditor(container, options);
editor.set(adminData);
}
import { selectRenderer } from './SelectRenderer.js';
import Plotly from 'plotly.js-dist';
export function renderMeasurementResults(measurementResults, language) {
console.debug('renderMeasurementResults called with:', measurementResults);
const container = document.getElementById('measurementResults');
const title = document.createElement('h2');
title.textContent = 'Measurement Results (' + language + ')';
container.appendChild(title);
container.innerHTML = '';
if (!measurementResults) {
console.error('No measurementResults provided.');
return;
}
// measurementResult can be an object or an array
let results = measurementResults['dcc:measurementResult'];
console.debug('Raw dcc:measurementResult:', results);
if (!results) {
console.error("Missing 'dcc:measurementResult' in measurementResults");
return;
}
if (!Array.isArray(results)) {
results = [results];
}
console.debug('Processed measurement result array:', results);
results.forEach(result => {
// Render the measurement result title
const resultTitle = document.createElement('h3');
if (result['dcc:name'] && result['dcc:name']['dcc:content']) {
let nameContent = result['dcc:name']['dcc:content'];
if (Array.isArray(nameContent)) {
// Find the content matching the selected language or fallback
const match = nameContent.find(item => item.$ && item.$.lang === language) || nameContent[0];
resultTitle.textContent = match._ || match;
} else {
resultTitle.textContent = nameContent._ || nameContent;
// Use the first measurementResult object
const measurementResult = results[0];
console.debug('Using measurementResult:', measurementResult);
// Extract the result object from dcc:results -> dcc:result
let resultObj = measurementResult['dcc:results'] && measurementResult['dcc:results']['dcc:result'];
if (!resultObj) {
console.error("Missing 'dcc:results' or 'dcc:result' in measurementResult:", measurementResult);
return;
}
if (Array.isArray(resultObj)) {
resultObj = resultObj[0];
}
console.debug('Using result object:', resultObj);
// Get the measurement result name from dcc:name (from measurementResult)
let resultName = 'Measurement Result';
if (measurementResult['dcc:name'] && measurementResult['dcc:name']['dcc:content']) {
let content = measurementResult['dcc:name']['dcc:content'];
if (Array.isArray(content)) {
const match = content.find(item => item.$ && item.$.lang === language) || content[0];
resultName = match._ || match;
} else {
resultName = content._ || content;
}
}
console.debug('Result name:', resultName);
const tabTitle = document.createElement('h2');
tabTitle.textContent = resultName + ' (' + language + ')';
container.appendChild(tabTitle);
// Now extract dcc:data -> dcc:list from the resultObj
if (!resultObj['dcc:data'] || !resultObj['dcc:data']['dcc:list']) {
console.error("Missing 'dcc:data' or 'dcc:list' in result object:", resultObj);
return;
}
const listData = resultObj['dcc:data']['dcc:list'];
console.debug('List data:', listData);
// Flatten all quantities from the list
let quantities = [];
if (listData['dcc:quantity']) {
quantities = Array.isArray(listData['dcc:quantity']) ? listData['dcc:quantity'] : [listData['dcc:quantity']];
}
console.debug('Quantities from list:', quantities);
// Also add quantities from measurementMetaData if available
if (listData['dcc:measurementMetaData'] && listData['dcc:measurementMetaData']['dcc:metaData']) {
let metaData = listData['dcc:measurementMetaData']['dcc:metaData'];
if (!Array.isArray(metaData)) metaData = [metaData];
metaData.forEach(md => {
if (md['dcc:data'] && md['dcc:data']['dcc:quantity']) {
let qs = md['dcc:data']['dcc:quantity'];
if (!Array.isArray(qs)) qs = [qs];
quantities = quantities.concat(qs);
}
});
}
console.debug('Combined quantities:', quantities);
// Separate index quantities and data quantities
const indexQuantities = [];
const dataQuantities = [];
quantities.forEach(q => {
if (q.$ && q.$.refType && q.$.refType.match(/basic_tableIndex/)) {
indexQuantities.push(q);
} else {
resultTitle.textContent = 'Measurement Result';
}
container.appendChild(resultTitle);
// Render results using dcc:results -> dcc:result
if (result['dcc:results'] && result['dcc:results']['dcc:result']) {
let resItems = result['dcc:results']['dcc:result'];
if (!Array.isArray(resItems)) {
resItems = [resItems];
}
resItems.forEach(res => {
// Check if the result contains a dcc:data with a dcc:list that should be rendered as plot/table
if (res['dcc:data'] && res['dcc:data']['dcc:list']) {
const dataContainer = document.createElement('div');
dataContainer.className = 'plot-table';
// Use the global selectRenderer to decide how to render this data
const renderedContent = selectRenderer(res['dcc:data']['dcc:list'], language);
dataContainer.appendChild(renderedContent);
container.appendChild(dataContainer);
dataQuantities.push(q);
}
});
console.debug('Index Quantities:', indexQuantities);
console.debug('Data Quantities:', dataQuantities);
// Create radio buttons for X-axis selection (from indexQuantities)
const xAxisContainer = document.createElement('div');
xAxisContainer.innerHTML = '<strong>Select X-Axis:</strong> ';
indexQuantities.forEach((q, idx) => {
let nameStr = 'Index ' + idx;
if (q['dcc:name'] && q['dcc:name']['dcc:content']) {
let content = q['dcc:name']['dcc:content'];
if (Array.isArray(content)) {
const match = content.find(item => item.$ && item.$.lang === language) || content[0];
nameStr = match._ || match;
} else {
nameStr = content._ || content;
}
}
const radio = document.createElement('input');
radio.type = 'radio';
radio.name = 'xAxisSelect';
radio.value = idx;
if (idx === 0) radio.checked = true;
const label = document.createElement('label');
label.textContent = nameStr;
label.style.marginRight = '10px';
xAxisContainer.appendChild(radio);
xAxisContainer.appendChild(label);
});
container.appendChild(xAxisContainer);
// Tolerance toggle (placeholder)
const toleranceToggle = document.createElement('input');
toleranceToggle.type = 'checkbox';
toleranceToggle.id = 'toleranceToggle';
const tolLabel = document.createElement('label');
tolLabel.htmlFor = 'toleranceToggle';
tolLabel.textContent = ' Enable tolerance markings';
const tolContainer = document.createElement('div');
tolContainer.appendChild(toleranceToggle);
tolContainer.appendChild(tolLabel);
container.appendChild(tolContainer);
// Create containers for plots and table
const plotsContainer = document.createElement('div');
plotsContainer.id = 'plotsContainer';
container.appendChild(plotsContainer);
const tableContainer = document.createElement('div');
tableContainer.id = 'tableContainer';
container.appendChild(tableContainer);
// Function to update the visualization
function updateVisualization() {
const selectedRadio = document.querySelector('input[name="xAxisSelect"]:checked');
if (!selectedRadio) {
console.error('No X-Axis selection found.');
return;
}
const selectedIndex = selectedRadio.value;
const xQuantity = indexQuantities[selectedIndex];
let xValues = [];
if (xQuantity && xQuantity['si:realListXMLList'] && xQuantity['si:realListXMLList']['si:valueXMLList']) {
xValues = xQuantity['si:realListXMLList']['si:valueXMLList'].trim().split(/\s+/).map(v => parseFloat(v));
}
console.debug('Selected X-Axis values:', xValues);
// Build table headers and values
const headers = [];
let xHeader = 'X-Axis';
if (xQuantity['dcc:name'] && xQuantity['dcc:name']['dcc:content']) {
let content = xQuantity['dcc:name']['dcc:content'];
if (Array.isArray(content)) {
const match = content.find(item => item.$ && item.$.lang === language) || content[0];
xHeader = match._ || match;
} else {
xHeader = content._ || content;
}
}
headers.push(xHeader);
const dataValues = [];
dataQuantities.forEach(q => {
let header = 'Data';
if (q['dcc:name'] && q['dcc:name']['dcc:content']) {
let content = q['dcc:name']['dcc:content'];
if (Array.isArray(content)) {
const match = content.find(item => item.$ && item.$.lang === language) || content[0];
header = match._ || match;
} else {
// Fallback: simple placeholder
const defaultContainer = document.createElement('div');
defaultContainer.className = 'default-render';
defaultContainer.textContent = 'Default rendering for measurement result.';
container.appendChild(defaultContainer);
header = content._ || content;
}
}
headers.push(header);
let values = [];
if (q['si:realListXMLList'] && q['si:realListXMLList']['si:valueXMLList']) {
values = q['si:realListXMLList']['si:valueXMLList'].trim().split(/\s+/).map(v => parseFloat(v));
}
if (values.length === 1 && xValues.length > 1) {
values = new Array(xValues.length).fill(values[0]);
}
dataValues.push(values);
});
// Build table rows
const tableData = [headers];
for (let i = 0; i < xValues.length; i++) {
const row = [];
row.push(xValues[i]);
dataValues.forEach(values => {
row.push(values[i] !== undefined ? values[i] : '');
});
tableData.push(row);
}
console.debug('Table data:', tableData);
renderTable(tableData);
// Group data quantities by unit for plotting
const unitGroups = {};
dataQuantities.forEach((q, idx) => {
let unit = '';
if (q['si:realListXMLList'] && q['si:realListXMLList']['si:unitXMLList']) {
unit = q['si:realListXMLList']['si:unitXMLList'].trim();
}
if (!unitGroups[unit]) {
unitGroups[unit] = [];
}
let header = headers[idx + 1];
let values = [];
if (q['si:realListXMLList'] && q['si:realListXMLList']['si:valueXMLList']) {
values = q['si:realListXMLList']['si:valueXMLList'].trim().split(/\s+/).map(v => parseFloat(v));
}
if (values.length === 1 && xValues.length > 1) {
values = new Array(xValues.length).fill(values[0]);
}
unitGroups[unit].push({ name: header, y: values });
});
console.debug('Unit groups for plots:', unitGroups);
// Clear and render plots
plotsContainer.innerHTML = '';
Object.keys(unitGroups).forEach(unit => {
const graphDiv = document.createElement('div');
graphDiv.style.width = '100%';
graphDiv.style.height = '300px';
plotsContainer.appendChild(graphDiv);
const traces = unitGroups[unit].map(trace => {
return {
x: xValues,
y: trace.y,
type: 'scatter',
mode: 'lines+markers',
name: trace.name
};
});
const layout = {
title: 'Plot (' + unit + ')',
xaxis: { title: xHeader },
yaxis: { title: unit },
hovermode: 'closest'
};
Plotly.newPlot(graphDiv, traces, layout).then(() => {
console.debug('Plot rendered for unit:', unit);
});
// Coupled mouseover events for table row highlighting
graphDiv.on('plotly_hover', function(data) {
if (data.points && data.points.length > 0) {
const pointIndex = data.points[0].pointIndex + 1; // offset for header row
highlightTableRow(pointIndex);
}
});
graphDiv.on('plotly_unhover', function() {
clearTableRowHighlights();
});
});
}
// Render table from a 2D array
function renderTable(tableData) {
tableContainer.innerHTML = '';
const table = document.createElement('table');
tableData.forEach((rowData, rowIndex) => {
const tr = document.createElement('tr');
rowData.forEach(cellData => {
const cell = document.createElement(rowIndex === 0 ? 'th' : 'td');
cell.textContent = cellData;
cell.style.padding = '4px';
cell.style.border = '1px solid #ccc';
tr.appendChild(cell);
});
tr.addEventListener('mouseover', () => { tr.style.backgroundColor = '#eef'; });
tr.addEventListener('mouseout', () => { tr.style.backgroundColor = ''; });
table.appendChild(tr);
});
tableContainer.appendChild(table);
}
// Functions for coupled table row highlights
function highlightTableRow(rowIndex) {
const rows = tableContainer.querySelectorAll('tr');
if (rows[rowIndex]) {
rows[rowIndex].style.backgroundColor = '#fee';
}
}
function clearTableRowHighlights() {
const rows = tableContainer.querySelectorAll('tr');
rows.forEach(row => row.style.backgroundColor = '');
}
// Initial update
updateVisualization();
// Update visualization when X-axis selection changes
const radios = document.querySelectorAll('input[name="xAxisSelect"]');
radios.forEach(radio => {
radio.addEventListener('change', updateVisualization);
});
// Tolerance toggle event (placeholder)
toleranceToggle.addEventListener('change', () => {
console.log('Tolerance toggle:', toleranceToggle.checked);
// Future: update plot/table for tolerance markings and color coding
});
}
export function selectRenderer(json, language) {
// If the JSON node has a refType matching basic_\dIndexTable, render as plot and table
if (json && json.$ && json.$.refType && /^basic_\dIndexTable/.test(json.$.refType)) {
return renderPlotAndTable(json, language);
}
// Fallback: render a simple JSON string
const container = document.createElement('pre');
container.textContent = JSON.stringify(json, null, 2);
return container;
}
function renderPlotAndTable(json, language) {
// Placeholder implementation for plot and table rendering
// This function is not used in the interactive measurement renderer
const container = document.createElement('div');
container.className = 'plot-and-table';
container.textContent = 'Plot and Table renderer placeholder for language: ' + language;
container.textContent = 'Interactive Measurement Renderer in use.';
return container;
}
......@@ -13,7 +13,21 @@ h2, h3 {
padding: 10px;
}
.plot-table, .plot-and-table {
table {
width: 100%;
border-collapse: collapse;
}
table, th, td {
border: 1px solid #ccc;
}
th, td {
padding: 4px;
text-align: center;
}
.plot-and-table, .plot-table {
margin: 10px 0;
padding: 10px;
border: 1px dashed #999;
......
......@@ -3,13 +3,11 @@ import { defineConfig } from 'vite';
export default defineConfig({
resolve: {
alias: {
// Ensure that Node's events module is polyfilled
events: 'events'
}
},
optimizeDeps: {
esbuildOptions: {
// Define global as globalThis to help with some Node packages
define: { global: 'globalThis' }
}
}
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment