Spaces:
Configuration error
Configuration error
| // JSON-based Plugin Support for Custom Calculations | |
| // Extensible calculation engine with user-defined formulas and plugins | |
| interface PluginMetadata { | |
| id: string; | |
| name: string; | |
| version: string; | |
| author: string; | |
| description: string; | |
| category: string; | |
| tags: string[]; | |
| compatibility: string[]; | |
| lastUpdated: Date; | |
| } | |
| interface PluginInput { | |
| name: string; | |
| type: 'number' | 'string' | 'boolean' | 'select' | 'file'; | |
| label: string; | |
| description?: string; | |
| required: boolean; | |
| defaultValue?: any; | |
| validation?: { | |
| min?: number; | |
| max?: number; | |
| pattern?: string; | |
| options?: string[]; | |
| }; | |
| unit?: string; | |
| } | |
| interface PluginOutput { | |
| name: string; | |
| type: 'number' | 'string' | 'object' | 'array'; | |
| label: string; | |
| description?: string; | |
| unit?: string; | |
| format?: string; | |
| } | |
| interface PluginCalculation { | |
| formula: string; | |
| conditions?: Array<{ | |
| condition: string; | |
| formula: string; | |
| }>; | |
| constants?: Record<string, number>; | |
| functions?: Record<string, string>; | |
| } | |
| interface Plugin { | |
| metadata: PluginMetadata; | |
| inputs: PluginInput[]; | |
| outputs: PluginOutput[]; | |
| calculations: PluginCalculation[]; | |
| documentation?: { | |
| overview: string; | |
| examples: Array<{ | |
| title: string; | |
| description: string; | |
| inputs: Record<string, any>; | |
| expectedOutput: Record<string, any>; | |
| }>; | |
| references: string[]; | |
| }; | |
| } | |
| interface PluginExecutionResult { | |
| success: boolean; | |
| outputs: Record<string, any>; | |
| errors?: string[]; | |
| warnings?: string[]; | |
| executionTime: number; | |
| pluginId: string; | |
| } | |
| class PluginSystem { | |
| private plugins: Map<string, Plugin> = new Map(); | |
| private readonly PLUGIN_STORAGE_KEY = 'prithvi_plugins'; | |
| private readonly EXECUTION_TIMEOUT = 10000; // 10 seconds | |
| constructor() { | |
| this.loadPlugins(); | |
| this.initializeBuiltinPlugins(); | |
| } | |
| // Plugin Management | |
| loadPlugin(pluginData: string | Plugin): boolean { | |
| try { | |
| let plugin: Plugin; | |
| if (typeof pluginData === 'string') { | |
| plugin = JSON.parse(pluginData); | |
| } else { | |
| plugin = pluginData; | |
| } | |
| // Validate plugin structure | |
| this.validatePlugin(plugin); | |
| // Check for conflicts | |
| if (this.plugins.has(plugin.metadata.id)) { | |
| const existingPlugin = this.plugins.get(plugin.metadata.id)!; | |
| if (existingPlugin.metadata.version >= plugin.metadata.version) { | |
| throw new Error(`Plugin ${plugin.metadata.id} version ${plugin.metadata.version} is not newer than existing version ${existingPlugin.metadata.version}`); | |
| } | |
| } | |
| // Install plugin | |
| plugin.metadata.lastUpdated = new Date(); | |
| this.plugins.set(plugin.metadata.id, plugin); | |
| this.savePlugins(); | |
| return true; | |
| } catch (error) { | |
| throw new Error(`Failed to load plugin: ${error instanceof Error ? error.message : 'Unknown error'}`); | |
| } | |
| } | |
| unloadPlugin(pluginId: string): boolean { | |
| try { | |
| if (!this.plugins.has(pluginId)) { | |
| throw new Error(`Plugin ${pluginId} not found`); | |
| } | |
| this.plugins.delete(pluginId); | |
| this.savePlugins(); | |
| return true; | |
| } catch (error) { | |
| throw new Error(`Failed to unload plugin: ${error instanceof Error ? error.message : 'Unknown error'}`); | |
| } | |
| } | |
| getPlugin(pluginId: string): Plugin | undefined { | |
| return this.plugins.get(pluginId); | |
| } | |
| getAllPlugins(): Plugin[] { | |
| return Array.from(this.plugins.values()).sort((a, b) => | |
| a.metadata.name.localeCompare(b.metadata.name) | |
| ); | |
| } | |
| getPluginsByCategory(category: string): Plugin[] { | |
| return this.getAllPlugins().filter(plugin => | |
| plugin.metadata.category.toLowerCase() === category.toLowerCase() | |
| ); | |
| } | |
| searchPlugins(query: string): Plugin[] { | |
| if (!query || query.trim().length === 0) { | |
| return this.getAllPlugins(); | |
| } | |
| const searchTerm = query.toLowerCase(); | |
| return this.getAllPlugins().filter(plugin => | |
| plugin.metadata.name.toLowerCase().includes(searchTerm) || | |
| plugin.metadata.description.toLowerCase().includes(searchTerm) || | |
| plugin.metadata.tags.some(tag => tag.toLowerCase().includes(searchTerm)) | |
| ); | |
| } | |
| // Plugin Execution | |
| executePlugin(pluginId: string, inputs: Record<string, any>): Promise<PluginExecutionResult> { | |
| return new Promise((resolve, reject) => { | |
| const startTime = Date.now(); | |
| try { | |
| const plugin = this.plugins.get(pluginId); | |
| if (!plugin) { | |
| reject(new Error(`Plugin ${pluginId} not found`)); | |
| return; | |
| } | |
| // Set execution timeout | |
| const timeoutId = setTimeout(() => { | |
| reject(new Error(`Plugin execution timed out after ${this.EXECUTION_TIMEOUT}ms`)); | |
| }, this.EXECUTION_TIMEOUT); | |
| // Validate inputs | |
| const validationResult = this.validateInputs(plugin, inputs); | |
| if (!validationResult.valid) { | |
| clearTimeout(timeoutId); | |
| resolve({ | |
| success: false, | |
| outputs: {}, | |
| errors: validationResult.errors, | |
| executionTime: Date.now() - startTime, | |
| pluginId | |
| }); | |
| return; | |
| } | |
| // Execute calculations | |
| const executionResult = this.executeCalculations(plugin, inputs); | |
| clearTimeout(timeoutId); | |
| resolve({ | |
| success: executionResult.success, | |
| outputs: executionResult.outputs, | |
| errors: executionResult.errors, | |
| warnings: executionResult.warnings, | |
| executionTime: Date.now() - startTime, | |
| pluginId | |
| }); | |
| } catch (error) { | |
| reject(error); | |
| } | |
| }); | |
| } | |
| // Plugin Creation Helper | |
| createPlugin(metadata: PluginMetadata, config: { | |
| inputs: PluginInput[]; | |
| outputs: PluginOutput[]; | |
| calculations: PluginCalculation[]; | |
| documentation?: Plugin['documentation']; | |
| }): Plugin { | |
| try { | |
| const plugin: Plugin = { | |
| metadata: { | |
| ...metadata, | |
| lastUpdated: new Date() | |
| }, | |
| inputs: config.inputs, | |
| outputs: config.outputs, | |
| calculations: config.calculations, | |
| documentation: config.documentation | |
| }; | |
| this.validatePlugin(plugin); | |
| return plugin; | |
| } catch (error) { | |
| throw new Error(`Failed to create plugin: ${error instanceof Error ? error.message : 'Unknown error'}`); | |
| } | |
| } | |
| // Export/Import | |
| exportPlugin(pluginId: string): string { | |
| try { | |
| const plugin = this.plugins.get(pluginId); | |
| if (!plugin) { | |
| throw new Error(`Plugin ${pluginId} not found`); | |
| } | |
| return JSON.stringify(plugin, null, 2); | |
| } catch (error) { | |
| throw new Error(`Failed to export plugin: ${error instanceof Error ? error.message : 'Unknown error'}`); | |
| } | |
| } | |
| exportAllPlugins(): string { | |
| try { | |
| const allPlugins = Array.from(this.plugins.values()); | |
| return JSON.stringify({ | |
| plugins: allPlugins, | |
| exportDate: new Date().toISOString(), | |
| version: '1.0' | |
| }, null, 2); | |
| } catch (error) { | |
| throw new Error(`Failed to export plugins: ${error instanceof Error ? error.message : 'Unknown error'}`); | |
| } | |
| } | |
| importPlugins(importData: string): { successful: number; failed: number; errors: string[] } { | |
| try { | |
| const data = JSON.parse(importData); | |
| const plugins = data.plugins || [data]; // Handle single plugin or multiple | |
| let successful = 0; | |
| let failed = 0; | |
| const errors: string[] = []; | |
| plugins.forEach((plugin: Plugin, index: number) => { | |
| try { | |
| this.loadPlugin(plugin); | |
| successful++; | |
| } catch (error) { | |
| failed++; | |
| errors.push(`Plugin ${index + 1}: ${error instanceof Error ? error.message : 'Unknown error'}`); | |
| } | |
| }); | |
| return { successful, failed, errors }; | |
| } catch (error) { | |
| throw new Error(`Failed to import plugins: ${error instanceof Error ? error.message : 'Unknown error'}`); | |
| } | |
| } | |
| // Private Methods | |
| private validatePlugin(plugin: Plugin): void { | |
| // Validate metadata | |
| if (!plugin.metadata || !plugin.metadata.id || !plugin.metadata.name) { | |
| throw new Error('Plugin metadata is incomplete'); | |
| } | |
| if (!plugin.metadata.version || !this.isValidVersion(plugin.metadata.version)) { | |
| throw new Error('Plugin version is invalid'); | |
| } | |
| // Validate inputs | |
| if (!Array.isArray(plugin.inputs)) { | |
| throw new Error('Plugin inputs must be an array'); | |
| } | |
| plugin.inputs.forEach((input, index) => { | |
| if (!input.name || !input.type || !input.label) { | |
| throw new Error(`Input ${index} is incomplete`); | |
| } | |
| if (!['number', 'string', 'boolean', 'select', 'file'].includes(input.type)) { | |
| throw new Error(`Input ${index} has invalid type: ${input.type}`); | |
| } | |
| }); | |
| // Validate outputs | |
| if (!Array.isArray(plugin.outputs) || plugin.outputs.length === 0) { | |
| throw new Error('Plugin must have at least one output'); | |
| } | |
| plugin.outputs.forEach((output, index) => { | |
| if (!output.name || !output.type || !output.label) { | |
| throw new Error(`Output ${index} is incomplete`); | |
| } | |
| }); | |
| // Validate calculations | |
| if (!Array.isArray(plugin.calculations) || plugin.calculations.length === 0) { | |
| throw new Error('Plugin must have at least one calculation'); | |
| } | |
| plugin.calculations.forEach((calc, index) => { | |
| if (!calc.formula || typeof calc.formula !== 'string') { | |
| throw new Error(`Calculation ${index} must have a valid formula`); | |
| } | |
| }); | |
| } | |
| private validateInputs(plugin: Plugin, inputs: Record<string, any>): { | |
| valid: boolean; | |
| errors: string[]; | |
| } { | |
| const errors: string[] = []; | |
| plugin.inputs.forEach(inputDef => { | |
| const value = inputs[inputDef.name]; | |
| // Check required fields | |
| if (inputDef.required && (value === undefined || value === null || value === '')) { | |
| errors.push(`${inputDef.label} is required`); | |
| return; | |
| } | |
| if (value === undefined || value === null) { | |
| return; // Skip validation for optional empty fields | |
| } | |
| // Type validation | |
| switch (inputDef.type) { | |
| case 'number': | |
| if (typeof value !== 'number' || isNaN(value)) { | |
| errors.push(`${inputDef.label} must be a valid number`); | |
| } else { | |
| if (inputDef.validation?.min !== undefined && value < inputDef.validation.min) { | |
| errors.push(`${inputDef.label} must be at least ${inputDef.validation.min}`); | |
| } | |
| if (inputDef.validation?.max !== undefined && value > inputDef.validation.max) { | |
| errors.push(`${inputDef.label} must be at most ${inputDef.validation.max}`); | |
| } | |
| } | |
| break; | |
| case 'string': | |
| if (typeof value !== 'string') { | |
| errors.push(`${inputDef.label} must be a string`); | |
| } else { | |
| if (inputDef.validation?.pattern) { | |
| const regex = new RegExp(inputDef.validation.pattern); | |
| if (!regex.test(value)) { | |
| errors.push(`${inputDef.label} format is invalid`); | |
| } | |
| } | |
| } | |
| break; | |
| case 'select': | |
| if (inputDef.validation?.options && !inputDef.validation.options.includes(value)) { | |
| errors.push(`${inputDef.label} must be one of: ${inputDef.validation.options.join(', ')}`); | |
| } | |
| break; | |
| case 'boolean': | |
| if (typeof value !== 'boolean') { | |
| errors.push(`${inputDef.label} must be true or false`); | |
| } | |
| break; | |
| } | |
| }); | |
| return { | |
| valid: errors.length === 0, | |
| errors | |
| }; | |
| } | |
| private executeCalculations(plugin: Plugin, inputs: Record<string, any>): { | |
| success: boolean; | |
| outputs: Record<string, any>; | |
| errors?: string[]; | |
| warnings?: string[]; | |
| } { | |
| try { | |
| const outputs: Record<string, any> = {}; | |
| const warnings: string[] = []; | |
| for (const calculation of plugin.calculations) { | |
| try { | |
| // Create calculation context | |
| const context = { | |
| ...inputs, | |
| ...(calculation.constants || {}), | |
| Math: Math, | |
| abs: Math.abs, | |
| sqrt: Math.sqrt, | |
| pow: Math.pow, | |
| exp: Math.exp, | |
| log: Math.log, | |
| sin: Math.sin, | |
| cos: Math.cos, | |
| tan: Math.tan, | |
| max: Math.max, | |
| min: Math.min, | |
| round: Math.round, | |
| floor: Math.floor, | |
| ceil: Math.ceil | |
| }; | |
| // Execute formula | |
| let formula = calculation.formula; | |
| // Check conditions | |
| if (calculation.conditions) { | |
| for (const condition of calculation.conditions) { | |
| if (this.evaluateCondition(condition.condition, context)) { | |
| formula = condition.formula; | |
| break; | |
| } | |
| } | |
| } | |
| // Replace variables in formula | |
| const result = this.evaluateFormula(formula, context); | |
| // Map results to outputs | |
| plugin.outputs.forEach(outputDef => { | |
| if (outputDef.name in result) { | |
| outputs[outputDef.name] = result[outputDef.name]; | |
| } else if (typeof result === 'number' && plugin.outputs.length === 1) { | |
| outputs[outputDef.name] = result; | |
| } | |
| }); | |
| } catch (error) { | |
| warnings.push(`Calculation error: ${error instanceof Error ? error.message : 'Unknown error'}`); | |
| } | |
| } | |
| return { | |
| success: Object.keys(outputs).length > 0, | |
| outputs, | |
| warnings: warnings.length > 0 ? warnings : undefined | |
| }; | |
| } catch (error) { | |
| return { | |
| success: false, | |
| outputs: {}, | |
| errors: [`Execution failed: ${error instanceof Error ? error.message : 'Unknown error'}`] | |
| }; | |
| } | |
| } | |
| private evaluateFormula(formula: string, context: Record<string, any>): any { | |
| try { | |
| // Basic safety checks | |
| if (formula.includes('eval') || formula.includes('Function') || formula.includes('require')) { | |
| throw new Error('Unsafe formula detected'); | |
| } | |
| // Create a safe evaluation context | |
| const safeContext = { ...context }; | |
| delete (safeContext as any).constructor; | |
| delete (safeContext as any).__proto__; | |
| // Simple formula evaluation using Function constructor (safer than eval) | |
| const func = new Function(...Object.keys(safeContext), `return ${formula}`); | |
| return func(...Object.values(safeContext)); | |
| } catch (error) { | |
| throw new Error(`Formula evaluation failed: ${error instanceof Error ? error.message : 'Unknown error'}`); | |
| } | |
| } | |
| private evaluateCondition(condition: string, context: Record<string, any>): boolean { | |
| try { | |
| const result = this.evaluateFormula(condition, context); | |
| return Boolean(result); | |
| } catch (error) { | |
| return false; | |
| } | |
| } | |
| private isValidVersion(version: string): boolean { | |
| return /^\d+\.\d+\.\d+$/.test(version); | |
| } | |
| private loadPlugins(): void { | |
| try { | |
| const data = localStorage.getItem(this.PLUGIN_STORAGE_KEY); | |
| if (data) { | |
| const plugins = JSON.parse(data); | |
| plugins.forEach((plugin: Plugin) => { | |
| plugin.metadata.lastUpdated = new Date(plugin.metadata.lastUpdated); | |
| this.plugins.set(plugin.metadata.id, plugin); | |
| }); | |
| } | |
| } catch (error) { | |
| console.error('Failed to load plugins from storage:', error); | |
| } | |
| } | |
| private savePlugins(): void { | |
| try { | |
| const plugins = Array.from(this.plugins.values()); | |
| localStorage.setItem(this.PLUGIN_STORAGE_KEY, JSON.stringify(plugins)); | |
| } catch (error) { | |
| throw new Error(`Failed to save plugins: ${error instanceof Error ? error.message : 'Storage quota exceeded'}`); | |
| } | |
| } | |
| private initializeBuiltinPlugins(): void { | |
| // Water Quality Index Calculator Plugin | |
| const wqiPlugin: Plugin = { | |
| metadata: { | |
| id: 'builtin_wqi_calculator', | |
| name: 'Water Quality Index Calculator', | |
| version: '1.0.0', | |
| author: 'VBharat AI', | |
| description: 'Comprehensive Water Quality Index calculation based on multiple parameters', | |
| category: 'Water Quality', | |
| tags: ['water', 'quality', 'index', 'pollution'], | |
| compatibility: ['v1.0'], | |
| lastUpdated: new Date() | |
| }, | |
| inputs: [ | |
| { | |
| name: 'ph', | |
| type: 'number', | |
| label: 'pH', | |
| description: 'pH value of water sample', | |
| required: true, | |
| validation: { min: 0, max: 14 }, | |
| unit: 'pH units' | |
| }, | |
| { | |
| name: 'do', | |
| type: 'number', | |
| label: 'Dissolved Oxygen', | |
| description: 'Dissolved oxygen concentration', | |
| required: true, | |
| validation: { min: 0, max: 20 }, | |
| unit: 'mg/L' | |
| }, | |
| { | |
| name: 'bod', | |
| type: 'number', | |
| label: 'BOD', | |
| description: 'Biochemical Oxygen Demand', | |
| required: true, | |
| validation: { min: 0 }, | |
| unit: 'mg/L' | |
| }, | |
| { | |
| name: 'nitrate', | |
| type: 'number', | |
| label: 'Nitrate', | |
| description: 'Nitrate concentration', | |
| required: true, | |
| validation: { min: 0 }, | |
| unit: 'mg/L' | |
| }, | |
| { | |
| name: 'phosphate', | |
| type: 'number', | |
| label: 'Phosphate', | |
| description: 'Phosphate concentration', | |
| required: true, | |
| validation: { min: 0 }, | |
| unit: 'mg/L' | |
| }, | |
| { | |
| name: 'temperature', | |
| type: 'number', | |
| label: 'Temperature Deviation', | |
| description: 'Temperature deviation from normal', | |
| required: true, | |
| validation: { min: -10, max: 10 }, | |
| unit: '°C' | |
| } | |
| ], | |
| outputs: [ | |
| { | |
| name: 'wqi', | |
| type: 'number', | |
| label: 'Water Quality Index', | |
| description: 'Overall water quality index value', | |
| unit: 'WQI' | |
| }, | |
| { | |
| name: 'category', | |
| type: 'string', | |
| label: 'Quality Category', | |
| description: 'Water quality classification' | |
| }, | |
| { | |
| name: 'recommendation', | |
| type: 'string', | |
| label: 'Recommendation', | |
| description: 'Treatment or action recommendation' | |
| } | |
| ], | |
| calculations: [ | |
| { | |
| formula: ` | |
| // Individual quality indices | |
| var qi_ph = ph >= 6.5 && ph <= 8.5 ? 100 : max(0, 100 - abs(7 - ph) * 15); | |
| var qi_do = min(100, (do / 14.6) * 100); | |
| var qi_bod = max(0, 100 - (bod * 5)); | |
| var qi_nitrate = max(0, 100 - (nitrate * 2)); | |
| var qi_phosphate = max(0, 100 - (phosphate * 10)); | |
| var qi_temp = max(0, 100 - abs(temperature) * 5); | |
| // Weights (sum = 1.0) | |
| var w_ph = 0.15; | |
| var w_do = 0.25; | |
| var w_bod = 0.25; | |
| var w_nitrate = 0.15; | |
| var w_phosphate = 0.10; | |
| var w_temp = 0.10; | |
| // Weighted WQI calculation | |
| var wqi_value = (qi_ph * w_ph + qi_do * w_do + qi_bod * w_bod + | |
| qi_nitrate * w_nitrate + qi_phosphate * w_phosphate + qi_temp * w_temp); | |
| // Determine category and recommendation | |
| var category = wqi_value > 90 ? 'Excellent' : | |
| wqi_value > 70 ? 'Good' : | |
| wqi_value > 50 ? 'Medium' : | |
| wqi_value > 25 ? 'Bad' : 'Very Bad'; | |
| var recommendation = wqi_value > 70 ? 'Water is suitable for drinking with standard treatment' : | |
| wqi_value > 50 ? 'Requires advanced treatment before use' : | |
| 'Extensive treatment required, monitor pollution sources'; | |
| ({ wqi: round(wqi_value * 100) / 100, category: category, recommendation: recommendation }) | |
| `, | |
| constants: { | |
| IDEAL_DO: 14.6, | |
| NEUTRAL_PH: 7.0 | |
| } | |
| } | |
| ], | |
| documentation: { | |
| overview: 'Calculates comprehensive Water Quality Index based on six key parameters with appropriate weightings according to Indian standards.', | |
| examples: [ | |
| { | |
| title: 'Clean River Water', | |
| description: 'Typical values for unpolluted river water', | |
| inputs: { | |
| ph: 7.2, | |
| do: 8.5, | |
| bod: 2.0, | |
| nitrate: 5.0, | |
| phosphate: 0.1, | |
| temperature: 1.0 | |
| }, | |
| expectedOutput: { | |
| wqi: 85.5, | |
| category: 'Good', | |
| recommendation: 'Water is suitable for drinking with standard treatment' | |
| } | |
| } | |
| ], | |
| references: [ | |
| 'CPCB Water Quality Criteria', | |
| 'IS 10500:2012 Drinking Water Standards', | |
| 'WHO Water Quality Guidelines' | |
| ] | |
| } | |
| }; | |
| this.plugins.set(wqiPlugin.metadata.id, wqiPlugin); | |
| // Structural Load Calculator Plugin | |
| const loadPlugin: Plugin = { | |
| metadata: { | |
| id: 'builtin_structural_load', | |
| name: 'Structural Load Calculator', | |
| version: '1.0.0', | |
| author: 'VBharat AI', | |
| description: 'Calculate design loads for structures as per IS 875', | |
| category: 'Structural Engineering', | |
| tags: ['structural', 'load', 'design', 'IS875'], | |
| compatibility: ['v1.0'], | |
| lastUpdated: new Date() | |
| }, | |
| inputs: [ | |
| { | |
| name: 'dead_load', | |
| type: 'number', | |
| label: 'Dead Load', | |
| description: 'Permanent structural load', | |
| required: true, | |
| validation: { min: 0 }, | |
| unit: 'kN/m²' | |
| }, | |
| { | |
| name: 'live_load', | |
| type: 'number', | |
| label: 'Live Load', | |
| description: 'Variable/imposed load', | |
| required: true, | |
| validation: { min: 0 }, | |
| unit: 'kN/m²' | |
| }, | |
| { | |
| name: 'wind_load', | |
| type: 'number', | |
| label: 'Wind Load', | |
| description: 'Wind pressure load', | |
| required: false, | |
| defaultValue: 0, | |
| validation: { min: 0 }, | |
| unit: 'kN/m²' | |
| }, | |
| { | |
| name: 'seismic_load', | |
| type: 'number', | |
| label: 'Seismic Load', | |
| description: 'Earthquake load', | |
| required: false, | |
| defaultValue: 0, | |
| validation: { min: 0 }, | |
| unit: 'kN/m²' | |
| }, | |
| { | |
| name: 'load_combination', | |
| type: 'select', | |
| label: 'Load Combination', | |
| description: 'Design load combination type', | |
| required: true, | |
| validation: { | |
| options: ['basic', 'wind', 'seismic', 'all'] | |
| } | |
| } | |
| ], | |
| outputs: [ | |
| { | |
| name: 'design_load', | |
| type: 'number', | |
| label: 'Design Load', | |
| description: 'Factored design load', | |
| unit: 'kN/m²' | |
| }, | |
| { | |
| name: 'safety_factor', | |
| type: 'number', | |
| label: 'Safety Factor', | |
| description: 'Overall safety factor achieved' | |
| }, | |
| { | |
| name: 'governing_combination', | |
| type: 'string', | |
| label: 'Governing Combination', | |
| description: 'Critical load combination' | |
| } | |
| ], | |
| calculations: [ | |
| { | |
| formula: ` | |
| // Load combinations as per IS 875 | |
| var basic_combo = 1.5 * (dead_load + live_load); | |
| var wind_combo = 1.2 * (dead_load + live_load + wind_load); | |
| var seismic_combo = 1.2 * (dead_load + live_load + seismic_load); | |
| var wind_uplift = 0.9 * dead_load + 1.5 * wind_load; | |
| var combinations = { | |
| 'basic': basic_combo, | |
| 'wind': max(basic_combo, wind_combo, wind_uplift), | |
| 'seismic': max(basic_combo, seismic_combo), | |
| 'all': max(basic_combo, wind_combo, seismic_combo, wind_uplift) | |
| }; | |
| var design_load_value = combinations[load_combination]; | |
| var safety_factor_value = design_load_value / (dead_load + live_load); | |
| var governing = design_load_value === basic_combo ? 'Basic (1.5DL+1.5LL)' : | |
| design_load_value === wind_combo ? 'Wind (1.2DL+1.2LL+1.2WL)' : | |
| design_load_value === seismic_combo ? 'Seismic (1.2DL+1.2LL+1.2EL)' : | |
| 'Wind Uplift (0.9DL+1.5WL)'; | |
| ({ | |
| design_load: round(design_load_value * 100) / 100, | |
| safety_factor: round(safety_factor_value * 100) / 100, | |
| governing_combination: governing | |
| }) | |
| ` | |
| } | |
| ], | |
| documentation: { | |
| overview: 'Calculates structural design loads according to IS 875 load combinations for different loading scenarios.', | |
| examples: [ | |
| { | |
| title: 'Residential Building', | |
| description: 'Typical residential structure loads', | |
| inputs: { | |
| dead_load: 4.0, | |
| live_load: 2.0, | |
| wind_load: 1.0, | |
| seismic_load: 0.8, | |
| load_combination: 'all' | |
| }, | |
| expectedOutput: { | |
| design_load: 9.6, | |
| safety_factor: 1.6, | |
| governing_combination: 'Wind (1.2DL+1.2LL+1.2WL)' | |
| } | |
| } | |
| ], | |
| references: [ | |
| 'IS 875-1987 Design Loads for Buildings', | |
| 'IS 1893-2016 Earthquake Resistant Design', | |
| 'IS 456-2000 Plain and Reinforced Concrete' | |
| ] | |
| } | |
| }; | |
| this.plugins.set(loadPlugin.metadata.id, loadPlugin); | |
| } | |
| } | |
| export const pluginSystem = new PluginSystem(); | |
| export { Plugin, PluginInput, PluginOutput, PluginExecutionResult, PluginMetadata }; |