471 lines
15 KiB
JavaScript
471 lines
15 KiB
JavaScript
/**
|
|
* MYP Professional Network Diagram - Interactive Script
|
|
* Mercedes-Benz 3D-Druck-Management-System
|
|
*/
|
|
|
|
class MYPNetworkDiagram {
|
|
constructor() {
|
|
this.nodes = document.querySelectorAll('.node');
|
|
this.connections = document.querySelectorAll('.connection');
|
|
this.isInteractive = true;
|
|
|
|
this.init();
|
|
}
|
|
|
|
init() {
|
|
this.setupNodeInteractions();
|
|
this.setupConnectionHighlighting();
|
|
this.logSystemInfo();
|
|
|
|
// Add keyboard shortcuts
|
|
this.setupKeyboardControls();
|
|
}
|
|
|
|
/**
|
|
* Setup interactive node behaviors
|
|
*/
|
|
setupNodeInteractions() {
|
|
this.nodes.forEach(node => {
|
|
// Mouse enter - highlight connections
|
|
node.addEventListener('mouseenter', (e) => {
|
|
if (!this.isInteractive) return;
|
|
this.highlightNodeConnections(e.target);
|
|
});
|
|
|
|
// Mouse leave - reset highlights
|
|
node.addEventListener('mouseleave', () => {
|
|
if (!this.isInteractive) return;
|
|
this.resetHighlights();
|
|
});
|
|
|
|
// Click - show node details
|
|
node.addEventListener('click', (e) => {
|
|
this.showNodeDetails(e.target);
|
|
});
|
|
|
|
// Make nodes keyboard accessible
|
|
node.setAttribute('tabindex', '0');
|
|
node.setAttribute('role', 'button');
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Highlight connections related to the active node
|
|
*/
|
|
highlightNodeConnections(activeNode) {
|
|
const nodeId = activeNode.id;
|
|
const nodeType = this.getNodeType(activeNode);
|
|
|
|
// Get connected node IDs
|
|
const connections = this.getNodeConnections(nodeId, nodeType);
|
|
|
|
// Dim all nodes first
|
|
this.nodes.forEach(node => {
|
|
if (node !== activeNode && !connections.includes(node.id)) {
|
|
node.style.opacity = '0.3';
|
|
node.style.filter = 'blur(1px)';
|
|
}
|
|
});
|
|
|
|
// Highlight connected nodes
|
|
connections.forEach(connectedId => {
|
|
const connectedNode = document.getElementById(connectedId);
|
|
if (connectedNode) {
|
|
connectedNode.style.opacity = '1';
|
|
connectedNode.style.filter = 'none';
|
|
connectedNode.style.transform = 'translateY(-4px) scale(1.02)';
|
|
}
|
|
});
|
|
|
|
// Highlight relevant connections
|
|
this.highlightConnections(nodeId, connections);
|
|
}
|
|
|
|
/**
|
|
* Reset all visual highlights
|
|
*/
|
|
resetHighlights() {
|
|
this.nodes.forEach(node => {
|
|
node.style.opacity = '';
|
|
node.style.filter = '';
|
|
node.style.transform = '';
|
|
});
|
|
|
|
this.connections.forEach(conn => {
|
|
conn.style.opacity = '';
|
|
conn.style.strokeWidth = '';
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Highlight specific connections
|
|
*/
|
|
highlightConnections(nodeId, connectedIds) {
|
|
this.connections.forEach(conn => {
|
|
conn.style.opacity = '0.2';
|
|
});
|
|
|
|
// This would need more sophisticated logic to match
|
|
// connections to specific nodes based on coordinates
|
|
// For now, we'll use a simplified approach
|
|
}
|
|
|
|
/**
|
|
* Get node type from CSS classes
|
|
*/
|
|
getNodeType(node) {
|
|
if (node.classList.contains('frontend-node')) return 'frontend';
|
|
if (node.classList.contains('backend-node')) return 'backend';
|
|
if (node.classList.contains('smartplug-node')) return 'smartplug';
|
|
if (node.classList.contains('printer-node')) return 'printer';
|
|
return 'unknown';
|
|
}
|
|
|
|
/**
|
|
* Get connected node IDs for a given node
|
|
*/
|
|
getNodeConnections(nodeId, nodeType) {
|
|
const connectionMap = {
|
|
// Frontend connections
|
|
'kiosk': ['webapp', 'server'],
|
|
'webapp': ['kiosk', 'auth', 'flask'],
|
|
'auth': ['webapp', 'security'],
|
|
|
|
// Backend connections
|
|
'server': ['kiosk', 'flask'],
|
|
'flask': ['webapp', 'server', 'database'],
|
|
'database': ['flask', 'scheduler'],
|
|
'scheduler': ['database', 'security', 'plug1', 'plug2', 'plug3', 'plug4', 'plug6'],
|
|
'security': ['auth', 'scheduler', 'monitor'],
|
|
'monitor': ['security', 'plug6', 'printer5'],
|
|
|
|
// Smart plug connections
|
|
'plug1': ['scheduler', 'printer1'],
|
|
'plug2': ['scheduler', 'printer2'],
|
|
'plug3': ['scheduler', 'printer3'],
|
|
'plug4': ['scheduler', 'printer4'],
|
|
'plug6': ['scheduler', 'printer5', 'monitor'],
|
|
|
|
// Printer connections
|
|
'printer1': ['plug1'],
|
|
'printer2': ['plug2'],
|
|
'printer3': ['plug3'],
|
|
'printer4': ['plug4'],
|
|
'printer5': ['plug6', 'monitor']
|
|
};
|
|
|
|
return connectionMap[nodeId] || [];
|
|
}
|
|
|
|
/**
|
|
* Show detailed information about a node
|
|
*/
|
|
showNodeDetails(node) {
|
|
const nodeLabel = node.querySelector('.node-label').textContent;
|
|
const nodeDetail = node.querySelector('.node-detail').textContent;
|
|
const nodeType = this.getNodeType(node);
|
|
|
|
const details = this.getNodeDetailedInfo(node.id, nodeType);
|
|
|
|
console.log(`🔧 MYP Node: ${nodeLabel}`);
|
|
console.log(`📋 Details: ${nodeDetail}`);
|
|
console.log(`🔗 Type: ${nodeType}`);
|
|
console.log(`📊 Info:`, details);
|
|
|
|
// Visual feedback
|
|
node.style.transform = 'scale(0.95)';
|
|
setTimeout(() => {
|
|
node.style.transform = '';
|
|
}, 150);
|
|
|
|
// Could show a modal or tooltip here
|
|
this.showTooltip(node, details);
|
|
}
|
|
|
|
/**
|
|
* Get detailed information for a node
|
|
*/
|
|
getNodeDetailedInfo(nodeId, nodeType) {
|
|
const detailsMap = {
|
|
'kiosk': {
|
|
description: 'Mercedes-Benz Kiosk Terminal im TBA Marienfelde',
|
|
specs: ['Touch-Interface', 'HTTPS-only', 'Corporate Design'],
|
|
status: 'Online'
|
|
},
|
|
'webapp': {
|
|
description: 'Next.js Progressive Web App mit Flask Backend',
|
|
specs: ['React/Next.js', 'PWA Features', 'Responsive Design'],
|
|
status: 'Active'
|
|
},
|
|
'auth': {
|
|
description: 'Authentifizierungssystem mit bcrypt und OTP',
|
|
specs: ['bcrypt Hashing', 'OTP Guest Access', 'Session Management'],
|
|
status: 'Secure'
|
|
},
|
|
'server': {
|
|
description: 'Raspberry Pi 4B Production Server',
|
|
specs: ['ARM Cortex-A72', '8GB RAM', 'Debian OS'],
|
|
status: 'Online - 192.168.0.100'
|
|
},
|
|
'flask': {
|
|
description: 'Flask Backend mit 15+ Blueprint Modulen',
|
|
specs: ['Python Flask', 'SQLAlchemy ORM', 'REST API'],
|
|
status: 'Running'
|
|
},
|
|
'database': {
|
|
description: 'SQLite WAL Database mit Thread-Pool',
|
|
specs: ['WAL Mode', 'Thread-Safe', 'Auto-Backup'],
|
|
status: 'Synchronized'
|
|
},
|
|
'scheduler': {
|
|
description: 'APScheduler Job Queue System',
|
|
specs: ['Background Jobs', 'Cron Tasks', 'Queue Management'],
|
|
status: 'Active'
|
|
},
|
|
'security': {
|
|
description: 'Security Suite mit SSL/TLS Management',
|
|
specs: ['SSL/TLS', 'Audit Logging', 'CSRF Protection'],
|
|
status: 'Protected'
|
|
},
|
|
'monitor': {
|
|
description: 'System Monitoring & Analytics',
|
|
specs: ['Performance Tracking', 'Error Logging', 'Health Checks'],
|
|
status: 'Monitoring'
|
|
}
|
|
};
|
|
|
|
// Smart Plugs
|
|
if (nodeId.startsWith('plug')) {
|
|
const plugNumber = nodeId.replace('plug', '');
|
|
return {
|
|
description: `TP-Link Tapo P110 Smart Plug #${plugNumber}`,
|
|
specs: ['Energy Monitoring', '230V AC Control', 'Remote Management'],
|
|
status: `Online - 192.168.0.10${plugNumber}`
|
|
};
|
|
}
|
|
|
|
// Printers
|
|
if (nodeId.startsWith('printer')) {
|
|
const printerNumber = nodeId.replace('printer', '');
|
|
const printerModels = {
|
|
'1': 'Prusa MK3S+',
|
|
'2': 'Ender 3 Pro',
|
|
'3': 'Bambu Lab A1',
|
|
'4': 'Prusa MINI+',
|
|
'5': 'Artillery X1'
|
|
};
|
|
return {
|
|
description: `3D-Drucker Arbeitsplatz ${printerNumber}`,
|
|
specs: [printerModels[printerNumber], 'Smart-Plug Controlled', 'Job Queue Ready'],
|
|
status: 'Ready for Production'
|
|
};
|
|
}
|
|
|
|
return detailsMap[nodeId] || {
|
|
description: 'MYP System Component',
|
|
specs: ['Network Connected', 'Monitored'],
|
|
status: 'Online'
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Show tooltip with node information
|
|
*/
|
|
showTooltip(node, details) {
|
|
// Remove existing tooltip
|
|
const existingTooltip = document.querySelector('.node-tooltip');
|
|
if (existingTooltip) {
|
|
existingTooltip.remove();
|
|
}
|
|
|
|
const tooltip = document.createElement('div');
|
|
tooltip.className = 'node-tooltip';
|
|
tooltip.innerHTML = `
|
|
<div class="tooltip-header">${details.description}</div>
|
|
<div class="tooltip-specs">
|
|
${details.specs.map(spec => `<div class="tooltip-spec">• ${spec}</div>`).join('')}
|
|
</div>
|
|
<div class="tooltip-status">Status: ${details.status}</div>
|
|
`;
|
|
|
|
// Style the tooltip
|
|
tooltip.style.cssText = `
|
|
position: absolute;
|
|
background: rgba(0, 0, 0, 0.95);
|
|
color: white;
|
|
padding: 12px;
|
|
border-radius: 8px;
|
|
font-size: 0.8rem;
|
|
max-width: 250px;
|
|
z-index: 1000;
|
|
pointer-events: none;
|
|
backdrop-filter: blur(10px);
|
|
border: 1px solid rgba(255, 255, 255, 0.2);
|
|
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.5);
|
|
`;
|
|
|
|
// Position tooltip
|
|
const rect = node.getBoundingClientRect();
|
|
tooltip.style.left = (rect.left + rect.width + 10) + 'px';
|
|
tooltip.style.top = rect.top + 'px';
|
|
|
|
document.body.appendChild(tooltip);
|
|
|
|
// Auto-remove after 3 seconds
|
|
setTimeout(() => {
|
|
tooltip.remove();
|
|
}, 3000);
|
|
}
|
|
|
|
/**
|
|
* Setup connection highlighting on hover
|
|
*/
|
|
setupConnectionHighlighting() {
|
|
this.connections.forEach(conn => {
|
|
conn.addEventListener('mouseenter', () => {
|
|
if (!this.isInteractive) return;
|
|
conn.style.strokeWidth = '4';
|
|
conn.style.opacity = '1';
|
|
});
|
|
|
|
conn.addEventListener('mouseleave', () => {
|
|
if (!this.isInteractive) return;
|
|
conn.style.strokeWidth = '';
|
|
conn.style.opacity = '';
|
|
});
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Setup keyboard controls
|
|
*/
|
|
setupKeyboardControls() {
|
|
document.addEventListener('keydown', (e) => {
|
|
switch(e.key) {
|
|
case 'Escape':
|
|
this.resetHighlights();
|
|
break;
|
|
case 'i':
|
|
case 'I':
|
|
this.toggleInteractivity();
|
|
break;
|
|
case 'r':
|
|
case 'R':
|
|
this.resetDiagram();
|
|
break;
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Toggle interactivity mode
|
|
*/
|
|
toggleInteractivity() {
|
|
this.isInteractive = !this.isInteractive;
|
|
console.log(`🎮 Interactivity ${this.isInteractive ? 'enabled' : 'disabled'}`);
|
|
|
|
if (!this.isInteractive) {
|
|
this.resetHighlights();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Reset diagram to initial state
|
|
*/
|
|
resetDiagram() {
|
|
this.resetHighlights();
|
|
|
|
// Remove any tooltips
|
|
document.querySelectorAll('.node-tooltip').forEach(tooltip => {
|
|
tooltip.remove();
|
|
});
|
|
|
|
console.log('🔄 Diagram reset');
|
|
}
|
|
|
|
/**
|
|
* Log system information
|
|
*/
|
|
logSystemInfo() {
|
|
console.log('🚀 MYP Professional Network Diagram loaded');
|
|
console.log('📊 System Components:', this.nodes.length);
|
|
console.log('🔗 Connections:', this.connections.length);
|
|
console.log('🏭 Location: Mercedes-Benz TBA Marienfelde');
|
|
console.log('🖥️ Server: Raspberry Pi 4B (192.168.0.100)');
|
|
console.log('🔌 Smart Plugs: 5x Tapo P110 (192.168.0.101-104, 106)');
|
|
console.log('🖨️ 3D Printers: 5 Arbeitsplätze');
|
|
console.log('⌨️ Keyboard: [I] Toggle Interactivity, [R] Reset, [ESC] Clear');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Utility class for diagram export and screenshot optimization
|
|
*/
|
|
class DiagramExport {
|
|
static enableScreenshotMode() {
|
|
document.body.classList.add('screenshot-mode');
|
|
|
|
const style = document.createElement('style');
|
|
style.id = 'screenshot-styles';
|
|
style.textContent = `
|
|
.screenshot-mode .node {
|
|
animation: none !important;
|
|
}
|
|
.screenshot-mode .node:hover {
|
|
transform: none !important;
|
|
box-shadow: inherit !important;
|
|
}
|
|
.screenshot-mode .connection {
|
|
animation: none !important;
|
|
}
|
|
`;
|
|
document.head.appendChild(style);
|
|
|
|
console.log('📸 Screenshot mode enabled');
|
|
}
|
|
|
|
static disableScreenshotMode() {
|
|
document.body.classList.remove('screenshot-mode');
|
|
const screenshotStyles = document.getElementById('screenshot-styles');
|
|
if (screenshotStyles) {
|
|
screenshotStyles.remove();
|
|
}
|
|
|
|
console.log('🎮 Interactive mode restored');
|
|
}
|
|
|
|
static printDiagram() {
|
|
window.print();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Initialize when DOM is ready
|
|
*/
|
|
document.addEventListener('DOMContentLoaded', () => {
|
|
// Initialize the network diagram
|
|
const networkDiagram = new MYPNetworkDiagram();
|
|
|
|
// Make utilities globally available
|
|
window.MYP = {
|
|
diagram: networkDiagram,
|
|
export: DiagramExport
|
|
};
|
|
|
|
// Check URL parameters for special modes
|
|
const urlParams = new URLSearchParams(window.location.search);
|
|
|
|
if (urlParams.get('screenshot') === 'true') {
|
|
DiagramExport.enableScreenshotMode();
|
|
}
|
|
|
|
if (urlParams.get('print') === 'true') {
|
|
setTimeout(() => {
|
|
DiagramExport.printDiagram();
|
|
}, 1000);
|
|
}
|
|
|
|
console.log('✅ MYP Professional Network Diagram initialized');
|
|
console.log('🔧 Access via: window.MYP.diagram');
|
|
console.log('📸 Screenshot mode: ?screenshot=true');
|
|
}); |