Skip to main content
Glama
visum-controller.ts38.8 kB
// Visum COM API Integration for MCP Server import { spawn } from "child_process"; import * as fs from "fs"; import * as path from "path"; interface VisumConfig { knownInstallations: Array<{ path: string; version: string; lastVerified: string; }>; preferredPath?: string; lastUpdated: string; } // Visum integration class using PowerShell COM export class VisumController { private visumPaths = [ // Known H: drive paths (discovered from user's system) 'H:\\Program Files\\PTV Vision\\PTV Visum 2025\\Exe\\Visum250.exe', 'H:\\Program Files\\PTV Vision\\PTV Visum 2024\\Exe\\Visum240.exe', 'H:\\Program Files\\PTV Vision\\PTV Visum 2023\\Exe\\Visum230.exe', 'H:\\Program Files\\PTV Vision\\PTV Visum 2022\\Exe\\Visum220.exe', 'H:\\Program Files\\PTV Vision\\PTV Visum 2021\\Exe\\Visum210.exe', // Standard C: drive paths 'C:\\Program Files\\PTV Vision\\PTV Visum 2025\\Exe\\Visum250.exe', 'C:\\Program Files\\PTV Vision\\PTV Visum 2024\\Exe\\Visum240.exe', 'C:\\Program Files\\PTV Vision\\PTV Visum 2023\\Exe\\Visum230.exe', 'C:\\Program Files\\PTV Vision\\PTV Visum 2022\\Exe\\Visum220.exe', 'C:\\Program Files\\PTV Vision\\PTV Visum 2021\\Exe\\Visum210.exe', 'C:\\Program Files (x86)\\PTV Vision\\PTV Visum 2025\\Exe\\Visum250.exe', 'C:\\Program Files (x86)\\PTV Vision\\PTV Visum 2024\\Exe\\Visum240.exe', 'C:\\Program Files (x86)\\PTV Vision\\PTV Visum 2023\\Exe\\Visum230.exe', 'C:\\Program Files (x86)\\PTV Vision\\PTV Visum 2022\\Exe\\Visum220.exe', 'C:\\Program Files (x86)\\PTV Vision\\PTV Visum 2021\\Exe\\Visum210.exe' ]; private configPath = path.join(process.cwd(), 'visum-config.json'); private currentModel: string | null = null; private visumInstance: any = null; private comAvailable: boolean | null = null; private demoMode: boolean = false; // Enable demo mode when Visum is not available private customVisumPath: string | null = null; // Store custom Visum path private visumLogDirs: { log: string; temp: string; work: string } | null = null; // Store configured directories // Create comprehensive log directories for Visum to prevent crashes private async createVisumDirectories(): Promise<{ log: string; temp: string; work: string }> { const tempDir = process.env.TEMP || 'C:\\temp'; const baseDir = path.join(tempDir, 'VisumMCP'); const logDir = path.join(baseDir, 'logs'); const tempVisumDir = path.join(baseDir, 'temp'); const workDir = path.join(baseDir, 'work'); try { // Create all directories await fs.promises.mkdir(logDir, { recursive: true }); await fs.promises.mkdir(tempVisumDir, { recursive: true }); await fs.promises.mkdir(workDir, { recursive: true }); // Set permissions (Windows) if (process.platform === 'win32') { try { const { spawn } = await import('child_process'); const icacls = spawn('icacls', [baseDir, '/grant', `${process.env.USERNAME}:F`, '/T'], { stdio: 'ignore' }); icacls.on('exit', () => { console.error(`Visum directories created with full permissions: ${baseDir}`); }); } catch (permError) { // Continue without setting permissions console.error('Note: Could not set directory permissions, Visum may have limited access'); } } this.visumLogDirs = { log: logDir, temp: tempVisumDir, work: workDir }; return this.visumLogDirs; } catch (error) { console.error(`Error creating Visum directories: ${error}`); throw error; } } // Load configuration from file private loadConfig(): VisumConfig { try { if (fs.existsSync(this.configPath)) { const configData = fs.readFileSync(this.configPath, 'utf8'); const config = JSON.parse(configData) as VisumConfig; // Verify stored installations are still valid config.knownInstallations = config.knownInstallations.filter(install => { return fs.existsSync(install.path); }); return config; } } catch (error) { console.error('Error loading Visum config:', error); } return { knownInstallations: [], lastUpdated: new Date().toISOString() }; } // Save configuration to file private saveConfig(config: VisumConfig): void { try { config.lastUpdated = new Date().toISOString(); fs.writeFileSync(this.configPath, JSON.stringify(config, null, 2), 'utf8'); } catch (error) { console.error('Error saving Visum config:', error); } } // Add a new installation to the known list private addKnownInstallation(path: string, version: string): void { const config = this.loadConfig(); // Check if installation already exists const existingIndex = config.knownInstallations.findIndex(install => install.path === path); if (existingIndex >= 0) { // Update existing installation config.knownInstallations[existingIndex] = { path, version, lastVerified: new Date().toISOString() }; } else { // Add new installation config.knownInstallations.push({ path, version, lastVerified: new Date().toISOString() }); } // Set as preferred if it's the first one or if no preferred exists if (!config.preferredPath || config.knownInstallations.length === 1) { config.preferredPath = path; } this.saveConfig(config); } // Get all known installations, prioritizing preferred path private getKnownInstallations(): Array<{path: string, version: string}> { const config = this.loadConfig(); const installations = config.knownInstallations.map(install => ({ path: install.path, version: install.version })); // Sort to put preferred path first if (config.preferredPath) { installations.sort((a, b) => { if (a.path === config.preferredPath) return -1; if (b.path === config.preferredPath) return 1; return 0; }); } return installations; } // Check if Visum is installed (enhanced version with custom path support) async isVisumAvailable(customPath?: string): Promise<{ available: boolean; path?: string; version?: string; comRegistered?: boolean; installations?: Array<{path: string, version: string}>; error?: string; suggestCustomPath?: boolean; pathSource?: 'learned-preferred' | 'learned-known' | 'discovered'; totalKnownPaths?: number; lastConfigUpdate?: string; }> { try { // Start with known installations from config const installations: Array<{path: string, version: string}> = []; const knownInstallations = this.getKnownInstallations(); // Check known installations first (they're sorted with preferred first) for (const known of knownInstallations) { if (fs.existsSync(known.path)) { installations.push(known); } } // If custom path is provided, validate and prioritize it if (customPath) { console.error(`Checking custom Visum path: ${customPath}`); if (await this.validateVisumPath(customPath)) { const version = this.extractVersionFromPath(customPath); const customInstallation = { path: customPath, version }; // Remove if it already exists in the list const existingIndex = installations.findIndex(inst => inst.path === customPath); if (existingIndex >= 0) { installations.splice(existingIndex, 1); } // Add to front of list installations.unshift(customInstallation); this.customVisumPath = customPath; // Store for later use // Save this as a known installation this.addKnownInstallation(customPath, version); console.error(`✅ Valid Visum installation found at: ${customPath}`); } else { return { available: false, comRegistered: (await this.checkComRegistration()).registered, error: `Invalid Visum installation at custom path: ${customPath}. Please check the path and ensure it points to a Visum executable.`, suggestCustomPath: false }; } } // Check standard paths for new installations for (const visumPath of this.visumPaths) { if (fs.existsSync(visumPath)) { const version = this.extractVersionFromPath(visumPath); if (!installations.some(i => i.path === visumPath)) { installations.push({ path: visumPath, version }); // Save this as a new known installation this.addKnownInstallation(visumPath, version); } } } // Check for custom installations in PTV Vision folders const basePaths = [ 'C:\\Program Files\\PTV Vision', 'C:\\Program Files (x86)\\PTV Vision' ]; for (const basePath of basePaths) { if (fs.existsSync(basePath)) { try { const dirs = fs.readdirSync(basePath, { withFileTypes: true }); for (const dir of dirs) { if (dir.isDirectory() && dir.name.includes('Visum')) { const exePath = path.join(basePath, dir.name, 'Exe'); if (fs.existsSync(exePath)) { const exeFiles = fs.readdirSync(exePath).filter(f => f.startsWith('Visum') && f.endsWith('.exe')); for (const exeFile of exeFiles) { const fullPath = path.join(exePath, exeFile); const version = this.extractVersionFromPath(fullPath) || dir.name; if (!installations.some(i => i.path === fullPath)) { installations.push({ path: fullPath, version }); // Save this as a new known installation this.addKnownInstallation(fullPath, version); } } } } } } catch (scanError) { // Continue if we can't scan a directory } } } // Check COM registration const comCheck = await this.checkComRegistration(); if (installations.length > 0) { // Sort installations by preference (custom path first, then newest versions) installations.sort((a, b) => { if (a.path === this.customVisumPath) return -1; if (b.path === this.customVisumPath) return 1; return b.version.localeCompare(a.version); }); // Check if this path was learned from previous interactions const config = this.loadConfig(); const isLearnedPath = config.knownInstallations.some(known => known.path === installations[0].path); const isPreferredPath = config.preferredPath === installations[0].path; return { available: true, path: installations[0].path, version: installations[0].version, comRegistered: comCheck.registered, installations, error: comCheck.registered ? undefined : 'COM objects not registered - you may need to run Visum as Administrator once to register COM components', suggestCustomPath: false, // Add metadata about learned information pathSource: isLearnedPath ? (isPreferredPath ? 'learned-preferred' : 'learned-known') : 'discovered', totalKnownPaths: config.knownInstallations.length, lastConfigUpdate: config.lastUpdated }; } else { return { available: false, comRegistered: comCheck.registered, error: 'No Visum installations found in standard directories', suggestCustomPath: true // Suggest user to provide custom path }; } } catch (error) { return { available: false, comRegistered: false, error: `Error checking Visum availability: ${error instanceof Error ? error.message : String(error)}`, suggestCustomPath: true }; } } private extractVersionFromPath(path: string): string { const match = path.match(/Visum(\d{3})/); if (match) { const year = 2000 + parseInt(match[1].substring(0, 2)); return `${year}`; } return 'Unknown'; } // Validate if a given path is a valid Visum installation private async validateVisumPath(customPath: string): Promise<boolean> { try { // Check if the path exists if (!fs.existsSync(customPath)) { return false; } // Check if it's a file and ends with .exe const stats = fs.statSync(customPath); if (!stats.isFile() || !customPath.toLowerCase().endsWith('.exe')) { return false; } // Check if the filename contains "Visum" const filename = path.basename(customPath).toLowerCase(); if (!filename.includes('visum')) { return false; } // Try to get file version information (optional - just log if fails) try { const fileSize = stats.size; console.error(`Visum executable found: ${customPath} (${(fileSize / 1024 / 1024).toFixed(2)} MB)`); } catch (versionError) { // Continue validation even if version check fails } return true; } catch (error) { console.error(`Error validating Visum path: ${error instanceof Error ? error.message : String(error)}`); return false; } } // Check COM registration private async checkComRegistration(): Promise<{ registered: boolean; error?: string }> { const script = ` try { # Try to find Visum COM objects in registry $comObjects = Get-WmiObject -Class Win32_ClassicCOMClass -ErrorAction SilentlyContinue | Where-Object { $_.ProgId -like "*Visum*" -or $_.LocalServer32 -like "*Visum*" } if ($comObjects) { $result = @{ "registered" = $true "objects" = @() } foreach ($obj in $comObjects) { $result.objects += @{ "progId" = $obj.ProgId "server" = $obj.LocalServer32 } } $result | ConvertTo-Json -Depth 3 } else { @{"registered" = $false} | ConvertTo-Json } } catch { @{"registered" = $false; "error" = $_.Exception.Message} | ConvertTo-Json } `; try { const result = await this.executePowerShellCommand(script); if (result.success) { if (typeof result.result === 'string') { const parsed = JSON.parse(result.result); return { registered: parsed.registered || false, error: parsed.error }; } return { registered: result.result?.registered || false }; } else { return { registered: false, error: result.error }; } } catch (error) { return { registered: false, error: error instanceof Error ? error.message : String(error) }; } } // Execute PowerShell COM commands to control Visum async executePowerShellCommand(script: string): Promise<{ success: boolean; result?: any; error?: string }> { return new Promise((resolve) => { const powershell = spawn('powershell', [ '-Command', '-', // Read from stdin ], { stdio: ['pipe', 'pipe', 'pipe'] }); let output = ''; let errorOutput = ''; powershell.stdout.on('data', (data) => { output += data.toString(); }); powershell.stderr.on('data', (data) => { errorOutput += data.toString(); }); powershell.on('close', (code) => { if (code === 0) { try { // Try to parse JSON output if possible let result = output.trim(); if (result.startsWith('{') || result.startsWith('[')) { result = JSON.parse(result); } resolve({ success: true, result }); } catch { resolve({ success: true, result: output.trim() }); } } else { resolve({ success: false, error: errorOutput || `PowerShell exited with code ${code}` }); } }); // Send the PowerShell script powershell.stdin.write(script); powershell.stdin.end(); }); } // Initialize Visum COM object (enhanced with error handling and demo mode) async initializeVisum(): Promise<{ success: boolean; error?: string; details?: any }> { // First check if COM is available if (this.comAvailable === false) { // Enable demo mode for systems without Visum this.demoMode = true; return { success: true, error: undefined, details: { success: true, message: "Demo mode enabled - Visum COM objects are not available. All operations will be simulated.", demoMode: true, logDir: process.env.TEMP || 'C:\\temp' } }; } const script = ` try { Write-Host "=== Visum COM Initialization with Anti-Close Protection ===" # Step 1: Kill any existing Visum processes that might interfere Write-Host "Cleaning up any existing Visum processes..." Get-Process -Name "Visum*" -ErrorAction SilentlyContinue | Stop-Process -Force -ErrorAction SilentlyContinue Start-Sleep -Milliseconds 1000 # Step 2: Create comprehensive log directory structure $tempDir = $env:TEMP $logDir = Join-Path $tempDir "VisumMCP" $visumLogDir = Join-Path $logDir "logs" $visumTempDir = Join-Path $logDir "temp" $visumWorkDir = Join-Path $logDir "work" @($logDir, $visumLogDir, $visumTempDir, $visumWorkDir) | ForEach-Object { if (-not (Test-Path $_)) { New-Item -ItemType Directory -Path $_ -Force | Out-Null Write-Host "Created: $_" } # Ensure write permissions try { $testFile = Join-Path $_ "test.tmp" "test" | Out-File -FilePath $testFile -Force Remove-Item $testFile -Force -ErrorAction SilentlyContinue Write-Host "Verified write access: $_" } catch { Write-Host "Warning: Limited write access to $_" } } # Step 3: Configure comprehensive environment to prevent crashes $originalEnv = @{ TEMP = $env:TEMP TMP = $env:TMP APPDATA = $env:APPDATA LOCALAPPDATA = $env:LOCALAPPDATA } # Set all possible Visum environment variables $env:VISUM_LOG_DIR = $visumLogDir $env:VISUM_TEMP_DIR = $visumTempDir $env:VISUM_WORK_DIR = $visumWorkDir $env:VISUM_USER_DIR = $logDir $env:VISUM_SYSTEM_DIR = $logDir $env:VISUM_INI_DIR = $logDir $env:VISUM_DATA_DIR = $logDir $env:VISUM_CONFIG_DIR = $logDir $env:TMP = $visumTempDir $env:TEMP = $visumTempDir # Create Visum-specific folders that it expects $visumFolders = @( (Join-Path $env:APPDATA "PTV AG"), (Join-Path $env:APPDATA "PTV AG\\PTV Visum"), (Join-Path $env:LOCALAPPDATA "PTV AG"), (Join-Path $env:LOCALAPPDATA "PTV AG\\PTV Visum"), (Join-Path $visumLogDir "PTV Visum") ) foreach ($folder in $visumFolders) { try { if (-not (Test-Path $folder)) { New-Item -ItemType Directory -Path $folder -Force | Out-Null Write-Host "Created Visum folder: $folder" } } catch { Write-Host "Could not create $folder : $($_.Exception.Message)" } } Write-Host "Environment configured for maximum stability" # Step 4: Registry configuration for stability try { $regPaths = @( "HKCU:\\Software\\PTV AG", "HKCU:\\Software\\PTV AG\\PTV Visum" ) foreach ($regPath in $regPaths) { if (-not (Test-Path $regPath)) { New-Item -Path $regPath -Force | Out-Null Write-Host "Created registry path: $regPath" } } # Set critical registry values to prevent crashes $regValues = @{ "LogDir" = $visumLogDir "TempDir" = $visumTempDir "WorkingDirectory" = $visumWorkDir "UserDirectory" = $logDir "DisableErrorReporting" = 1 "SuppressDialogs" = 1 "AutomationMode" = 1 } foreach ($key in $regValues.Keys) { try { Set-ItemProperty -Path "HKCU:\\Software\\PTV AG\\PTV Visum" -Name $key -Value $regValues[$key] -ErrorAction SilentlyContinue Write-Host "Set registry: $key = $($regValues[$key])" } catch { Write-Host "Could not set registry $key : $($_.Exception.Message)" } } } catch { Write-Host "Registry configuration failed (continuing): $($_.Exception.Message)" } # Step 5: Enhanced COM object creation with persistent connection Write-Host "Creating Visum COM object with persistence logic..." $visum = $null $initSuccess = $false $attempts = 0 $maxAttempts = 5 $comCreated = $false while (-not $initSuccess -and $attempts -lt $maxAttempts) { $attempts++ Write-Host "=== Attempt $attempts of $maxAttempts ===" try { # Force cleanup before each attempt [System.GC]::Collect() [System.GC]::WaitForPendingFinalizers() [System.GC]::Collect() Write-Host "Creating COM object..." $visum = New-Object -ComObject "Visum.Visum" -ErrorAction Stop $comCreated = $true Write-Host "COM object created successfully" # CRITICAL: Immediate persistence check if ($visum -ne $null) { Write-Host "Testing COM object persistence..." # Test 1: Basic version access $version = "Unknown" try { $version = $visum.VersionNumber Write-Host "Version retrieved: $version" } catch { Write-Host "Version test failed: $($_.Exception.Message)" throw "Version test failed" } # Test 2: Network object access try { $netExists = ($visum.Net -ne $null) Write-Host "Network object accessible: $netExists" if (-not $netExists) { throw "Network object not accessible" } } catch { Write-Host "Network test failed: $($_.Exception.Message)" throw "Network test failed" } # Test 3: Set automation mode IMMEDIATELY try { $visum.SetAttValue('AppMode', 1) Write-Host "Automation mode set successfully" } catch { Write-Host "Automation mode failed: $($_.Exception.Message)" # Don't fail here, continue } # Test 4: Configure directories via COM BEFORE any other operation try { if ($visum.IO -ne $null) { $visum.IO.SetTempPath($visumTempDir) Write-Host "IO temp path set: $visumTempDir" } # Set all system directories $visum.SetSysAttValue("TempDir", $visumTempDir) $visum.SetSysAttValue("LogDir", $visumLogDir) $visum.SetSysAttValue("WorkingDir", $visumWorkDir) Write-Host "System directories configured via COM" } catch { Write-Host "Directory config failed: $($_.Exception.Message)" # Don't fail here as some Visum versions don't support this } # Test 5: CRITICAL PERSISTENCE TEST - wait and retest Write-Host "Performing persistence test (waiting 2 seconds)..." Start-Sleep -Seconds 2 try { # Test if object is still responsive after wait $testVersion = $visum.VersionNumber $testNet = ($visum.Net -ne $null) if ($testVersion -eq $version -and $testNet) { Write-Host "PERSISTENCE TEST PASSED - Object is stable" $initSuccess = $true } else { Write-Host "PERSISTENCE TEST FAILED - Object became unstable" throw "Object lost stability" } } catch { Write-Host "Persistence test failed: $($_.Exception.Message)" throw "Stability check failed" } } else { throw "COM object creation returned null" } } catch { $error = $_.Exception.Message Write-Host "Attempt $attempts failed: $error" # Cleanup failed attempt if ($visum -ne $null) { try { [System.Runtime.InteropServices.Marshal]::ReleaseComObject($visum) | Out-Null Write-Host "Released failed COM object" } catch { Write-Host "Could not release COM object: $($_.Exception.Message)" } $visum = $null } # Longer wait before retry for stability if ($attempts -lt $maxAttempts) { $waitTime = $attempts * 1000 # Increasing wait time Write-Host "Waiting $waitTime ms before retry..." Start-Sleep -Milliseconds $waitTime } } } # Restore original environment foreach ($key in $originalEnv.Keys) { Set-Item -Path "Env:$key" -Value $originalEnv[$key] } if ($initSuccess -and $visum -ne $null) { Write-Host "SUCCESS: Visum COM object is stable and persistent" # Final verification try { $finalVersion = $visum.VersionNumber $finalNet = ($visum.Net -ne $null) Write-Host "Final verification - Version: $finalVersion, Network: $finalNet" } catch { Write-Host "Warning: Final verification had issues: $($_.Exception.Message)" } @{ "success" = $true "message" = "Visum COM initialized with anti-close protection" "version" = $version "logDir" = $visumLogDir "tempDir" = $visumTempDir "workDir" = $visumWorkDir "attempts" = $attempts "demoMode" = $false "stabilityTest" = "passed" "persistence" = "verified" } | ConvertTo-Json } else { Write-Host "FAILED: Could not create persistent Visum COM object" @{ "success" = $false "error" = "Failed to create persistent COM object after $maxAttempts attempts" "attempts" = $attempts "comCreated" = $comCreated "troubleshooting" = @( "1. **License Issue**: Check if Visum license is valid and not expired", "2. **Admin Rights**: Run PowerShell as Administrator and try again", "3. **COM Registration**: Run 'regsvr32 [VisumPath]\\VisumCom.dll' as Admin", "4. **Process Cleanup**: Restart Windows to clear any stuck Visum processes", "5. **Antivirus**: Temporarily disable antivirus COM blocking", "6. **Manual Test**: Try opening Visum manually to verify it works", "7. **Version**: Some Visum versions have COM issues - try different version" ) "demoMode" = $false } | ConvertTo-Json exit 1 } } catch { $criticalError = $_.Exception.Message Write-Host "CRITICAL ERROR: $criticalError" @{ "success" = $false "error" = "Critical initialization error: $criticalError" "troubleshooting" = @( "1. Verify Visum is properly installed and licensed", "2. Run this script as Administrator", "3. Check Windows Event Viewer for detailed error information", "4. Temporarily disable antivirus and firewall", "5. Try rebooting after fresh Visum installation" ) "demoMode" = $false } | ConvertTo-Json exit 1 } `; const result = await this.executePowerShellCommand(script); if (!result.success) { // Enable demo mode on failure this.demoMode = true; this.comAvailable = false; return { success: true, error: undefined, details: { success: true, message: "Demo mode enabled - Visum COM initialization failed. All operations will be simulated.", demoMode: true, originalError: result.error, logDir: process.env.TEMP || 'C:\\temp' } }; } // Cache COM availability result this.comAvailable = result.success; if (result.success && typeof result.result === 'string') { try { const parsed = JSON.parse(result.result); return { success: parsed.success, error: parsed.error, details: parsed }; } catch { return result; } } return result; } // Load a Visum model (enhanced with demo mode and log directory configuration) async loadModel(modelPath: string): Promise<{ success: boolean; modelInfo?: any; error?: string }> { // Demo mode simulation if (this.demoMode) { await new Promise(resolve => setTimeout(resolve, 1000)); // Simulate loading time const demoModelInfo = { modelPath: modelPath, nodes: 1247, links: 3156, zones: 89, loadedAt: new Date().toString(), demoMode: true }; this.currentModel = modelPath; return { success: true, modelInfo: demoModelInfo }; } if (!fs.existsSync(modelPath)) { return { success: false, error: `Model file not found: ${modelPath}` }; } // Ensure Visum directories are created before loading model let dirs; try { dirs = await this.createVisumDirectories(); } catch (dirError) { console.error('Warning: Could not create Visum directories, continuing anyway'); dirs = { log: 'C:\\temp\\VisumMCP\\logs', temp: 'C:\\temp\\VisumMCP\\temp', work: 'C:\\temp\\VisumMCP\\work' }; } const script = ` try { # Set environment variables for safe directory access $env:VISUM_LOG_DIR = "${dirs.log.replace(/\\/g, '\\\\')}" $env:VISUM_TEMP_DIR = "${dirs.temp.replace(/\\/g, '\\\\')}" $env:VISUM_WORK_DIR = "${dirs.work.replace(/\\/g, '\\\\')}" $env:TMP = "${dirs.temp.replace(/\\/g, '\\\\')}" $env:TEMP = "${dirs.temp.replace(/\\/g, '\\\\')}" $visum = New-Object -ComObject "Visum.Visum" # Configure Visum directories before loading model try { if ($visum.IO) { $visum.IO.SetTempPath("${dirs.temp.replace(/\\/g, '\\\\')}") } $visum.SetSysAttValue("TempDir", "${dirs.temp.replace(/\\/g, '\\\\')}") $visum.SetSysAttValue("LogDir", "${dirs.log.replace(/\\/g, '\\\\')}") Write-Host "Configured Visum directories before model loading" } catch { Write-Host "Note: Could not pre-configure Visum directories: $($_.Exception.Message)" } Write-Host "Loading model: ${modelPath}" $visum.LoadVersion("${modelPath.replace(/\\/g, '\\\\')}") # Get basic model information $nodeCount = $visum.Net.Nodes.Count $linkCount = $visum.Net.Links.Count $zoneCount = $visum.Net.Zones.Count $modelInfo = @{ "modelPath" = "${modelPath}" "nodes" = $nodeCount "links" = $linkCount "zones" = $zoneCount "loadedAt" = (Get-Date).ToString() "logDir" = "${dirs.log.replace(/\\/g, '\\\\')}" "tempDir" = "${dirs.temp.replace(/\\/g, '\\\\')}" "demoMode" = $false } $modelInfo | ConvertTo-Json } catch { Write-Error "Failed to load model: $_" exit 1 } `; const result = await this.executePowerShellCommand(script); if (result.success) { this.currentModel = modelPath; return { success: true, modelInfo: result.result }; } return result; } // Get network statistics (enhanced with demo mode) async getNetworkStats(): Promise<{ success: boolean; stats?: any; error?: string }> { // Demo mode simulation if (this.demoMode) { await new Promise(resolve => setTimeout(resolve, 500)); // Simulate processing time const demoStats = { nodes: 1247, links: 3156, zones: 89, lines: 45, stops: 234, timeProfiles: 12, vehicleJourneys: 1678, demoMode: true }; return { success: true, stats: demoStats }; } const script = ` try { $visum = New-Object -ComObject "Visum.Visum" if ($visum.Net -eq $null) { Write-Error "No network loaded" exit 1 } $stats = @{ "nodes" = $visum.Net.Nodes.Count "links" = $visum.Net.Links.Count "zones" = $visum.Net.Zones.Count "lines" = $visum.Net.Lines.Count "stops" = $visum.Net.Stops.Count "timeProfiles" = $visum.Net.TimeProfiles.Count "vehicleJourneys" = $visum.Net.VehicleJourneys.Count "demoMode" = $false } $stats | ConvertTo-Json } catch { Write-Error "Failed to get network stats: $_" exit 1 } `; return await this.executePowerShellCommand(script); } // Run procedure (enhanced with demo mode) async runProcedure(procedureNumber: number, parameters?: Record<string, any>): Promise<{ success: boolean; result?: any; error?: string }> { // Demo mode simulation if (this.demoMode) { await new Promise(resolve => setTimeout(resolve, 2000)); // Simulate procedure execution time const procedureNames: { [key: number]: string } = { 1: "Traffic Assignment", 2: "Public Transport Assignment", 3: "Dynamic Traffic Assignment", 4: "Route Search", 5: "Network Analysis" }; const procedureName = procedureNames[procedureNumber] || `Procedure ${procedureNumber}`; return { success: true, result: `Demo: ${procedureName} completed successfully (simulated)` }; } const script = ` try { $visum = New-Object -ComObject "Visum.Visum" Write-Output "Running procedure ${procedureNumber}..." $visum.Procedures.Execute(${procedureNumber}) Write-Output "Procedure ${procedureNumber} completed successfully" } catch { Write-Error "Failed to run procedure ${procedureNumber}: $_" exit 1 } `; return await this.executePowerShellCommand(script); } // Execute custom VBScript (enhanced with demo mode) async executeCustomScript(vbScript: string): Promise<{ success: boolean; result?: any; error?: string }> { // Demo mode simulation if (this.demoMode) { await new Promise(resolve => setTimeout(resolve, 1000)); // Simulate script execution time return { success: true, result: `Demo: Custom VBScript executed successfully (simulated)\nScript length: ${vbScript.length} characters` }; } // Create temporary VBS file const tempPath = path.join(process.cwd(), `visum_script_${Date.now()}.vbs`); try { fs.writeFileSync(tempPath, vbScript); const script = ` try { $visum = New-Object -ComObject "Visum.Visum" $visum.Procedures.ExecuteVBSFile("${tempPath.replace(/\\/g, '\\\\')}") Write-Output "Custom script executed successfully" } catch { Write-Error "Failed to execute custom script: $_" exit 1 } `; const result = await this.executePowerShellCommand(script); // Cleanup temp file if (fs.existsSync(tempPath)) { fs.unlinkSync(tempPath); } return result; } catch (error) { // Cleanup temp file on error if (fs.existsSync(tempPath)) { fs.unlinkSync(tempPath); } return { success: false, error: `Failed to create/execute script: ${error instanceof Error ? error.message : String(error)}` }; } } // Get current model info getCurrentModel(): string | null { return this.currentModel; } // Check if running in demo mode isDemoMode(): boolean { return this.demoMode; } // Get demo status information getDemoStatus(): { demoMode: boolean; reason?: string; comAvailable?: boolean } { return { demoMode: this.demoMode, reason: this.demoMode ? 'Visum COM objects not available' : undefined, comAvailable: this.comAvailable ?? undefined }; } // Get information about configured Visum directories getVisumDirectories(): { log: string; temp: string; work: string } | null { return this.visumLogDirs; } // Get comprehensive status including directories getStatus(): { demoMode: boolean; comAvailable: boolean | null; currentModel: string | null; directories: { log: string; temp: string; work: string } | null; customPath: string | null; } { return { demoMode: this.demoMode, comAvailable: this.comAvailable, currentModel: this.currentModel, directories: this.visumLogDirs, customPath: this.customVisumPath }; } }

Implementation Reference

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/multiluca2020/visum-thinker-mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server