using System; using System.Runtime.InteropServices; using System.Windows.Forms; using Newtonsoft.Json; using System.IO; using System.Linq; using System.Collections.Generic; namespace HMIWeb { // Important: Allows JavaScript to see this class [ClassInterface(ClassInterfaceType.AutoDual)] [ComVisible(true)] public class MachineBridge { // Reference to the main form to update logic private MainForm _host; // Data folder paths private readonly string _dataFolder; private readonly string _settingsPath; private readonly string _recipeFolder; public MachineBridge(MainForm host) { _host = host; // Initialize data folder paths _dataFolder = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "data"); _settingsPath = Path.Combine(_dataFolder, "settings.json"); _recipeFolder = Path.Combine(_dataFolder, "recipe"); // Ensure folders exist EnsureDataFoldersExist(); } private void EnsureDataFoldersExist() { try { if (!Directory.Exists(_dataFolder)) Directory.CreateDirectory(_dataFolder); if (!Directory.Exists(_recipeFolder)) Directory.CreateDirectory(_recipeFolder); // Initialize default settings if not exists if (!File.Exists(_settingsPath)) InitializeDefaultSettings(); } catch (Exception ex) { Console.WriteLine($"[ERROR] Failed to create data folders: {ex.Message}"); } } private void InitializeDefaultSettings() { var defaultSettings = new List { new { Key = "Site Name", Value = "Smart Factory A-1", Group = "System Information", Type = "String", Description = "The display name of the factory site." }, new { Key = "Line ID", Value = "L-2024-001", Group = "System Information", Type = "String", Description = "Unique identifier for this production line." }, new { Key = "API Endpoint", Value = "https://api.factory.local/v1", Group = "Network Configuration", Type = "String", Description = "Base URL for the backend API." }, new { Key = "Support Contact", Value = "010-1234-5678", Group = "System Information", Type = "String", Description = "Emergency contact number for maintenance." }, new { Key = "Debug Mode", Value = "false", Group = "System Information", Type = "Boolean", Description = "Enable detailed logging for debugging." }, new { Key = "Max Speed", Value = "1500", Group = "Motion Control", Type = "Number", Description = "Maximum velocity in mm/s." }, new { Key = "Acceleration", Value = "500", Group = "Motion Control", Type = "Number", Description = "Acceleration ramp in mm/s²." } }; for (int i = 1; i <= 5; i++) { defaultSettings.Add(new { Key = $"Sensor_{i}_Threshold", Value = (i * 10).ToString(), Group = "Sensor Calibration", Type = "Number", Description = $"Trigger threshold for Sensor {i}." }); } for (int i = 1; i <= 3; i++) { defaultSettings.Add(new { Key = $"Safety_Zone_{i}", Value = "true", Group = "Safety Settings", Type = "Boolean", Description = $"Enable monitoring for Safety Zone {i}." }); } File.WriteAllText(_settingsPath, JsonConvert.SerializeObject(defaultSettings, Formatting.Indented)); Console.WriteLine("[INFO] Default settings.json created"); } // --- Methods called from React (App.tsx) --- public void MoveAxis(string axis, double value) { // In real app, call DLL here: Motion.Move(axis, value) Console.WriteLine($"[C#] Moving {axis} to {value}"); // For simulation, update host state directly _host.SetTargetPosition(axis, value); } public void SetIO(int id, bool isInput, bool state) { Console.WriteLine($"[C#] Set IO {id} to {state}"); _host.SetOutput(id, state); } public void SystemControl(string command) { Console.WriteLine($"[C#] CMD: {command}"); _host.HandleCommand(command); } public string SelectRecipe(string recipeId) { Console.WriteLine($"[C#] Selecting Recipe: {recipeId}"); try { string recipePath = Path.Combine(_recipeFolder, $"{recipeId}.json"); if (!File.Exists(recipePath)) { var response = new { success = false, message = "Recipe not found" }; return JsonConvert.SerializeObject(response); } // Load recipe data from file string json = File.ReadAllText(recipePath); var recipeData = JsonConvert.DeserializeObject(json); // Set as current recipe _host.SetCurrentRecipe(recipeId); Console.WriteLine($"[INFO] Recipe {recipeId} selected successfully"); var response2 = new { success = true, message = "Recipe selected successfully", recipeId = recipeId, recipe = recipeData }; return JsonConvert.SerializeObject(response2); } catch (Exception ex) { Console.WriteLine($"[ERROR] Failed to select recipe: {ex.Message}"); var response = new { success = false, message = ex.Message }; return JsonConvert.SerializeObject(response); } } public string GetConfig() { try { if (!File.Exists(_settingsPath)) { InitializeDefaultSettings(); } string json = File.ReadAllText(_settingsPath); Console.WriteLine($"[INFO] Loaded settings from {_settingsPath}"); return json; } catch (Exception ex) { Console.WriteLine($"[ERROR] Failed to load settings: {ex.Message}"); return "[]"; } } public void SaveConfig(string configJson) { try { Console.WriteLine($"[Backend] SAVE CONFIG REQUEST RECEIVED"); // Validate JSON format var settings = JsonConvert.DeserializeObject(configJson); // Save to file with formatting File.WriteAllText(_settingsPath, JsonConvert.SerializeObject(settings, Formatting.Indented)); Console.WriteLine($"[INFO] Settings saved successfully to {_settingsPath}"); } catch (Exception ex) { Console.WriteLine($"[ERROR] Failed to save settings: {ex.Message}"); } } public string GetIOList() { var ioList = new System.Collections.Generic.List(); Console.WriteLine("GetIOList"); // Outputs (0-31) for (int i = 0; i < 32; i++) { string name = $"DOUT_{i:D2}"; if (i == 0) name = "Tower Lamp Red"; if (i == 1) name = "Tower Lamp Yel"; if (i == 2) name = "Tower Lamp Grn"; ioList.Add(new { id = i, name = name, type = "output", state = false }); } // Inputs (0-31) for (int i = 0; i < 32; i++) { string name = $"DIN_{i:D2}"; bool initialState = false; if (i == 0) name = "Front Door Sensor"; if (i == 1) name = "Right Door Sensor"; if (i == 2) name = "Left Door Sensor"; if (i == 3) name = "Back Door Sensor"; if (i == 4) { name = "Main Air Pressure"; initialState = true; } if (i == 5) { name = "Vacuum Generator"; initialState = true; } if (i == 6) { name = "Emergency Stop Loop"; initialState = true; } ioList.Add(new { id = i, name = name, type = "input", state = initialState }); } return Newtonsoft.Json.JsonConvert.SerializeObject(ioList); } public string GetRecipeList() { try { var recipes = new List(); if (!Directory.Exists(_recipeFolder)) { Directory.CreateDirectory(_recipeFolder); return JsonConvert.SerializeObject(recipes); } var jsonFiles = Directory.GetFiles(_recipeFolder, "*.json"); foreach (var filePath in jsonFiles) { try { string fileName = Path.GetFileNameWithoutExtension(filePath); string json = File.ReadAllText(filePath); var recipeData = JsonConvert.DeserializeObject(json); var lastModified = File.GetLastWriteTime(filePath).ToString("yyyy-MM-dd"); recipes.Add(new { id = fileName, name = recipeData?.name ?? fileName, lastModified = lastModified }); } catch (Exception ex) { Console.WriteLine($"[ERROR] Failed to read recipe {filePath}: {ex.Message}"); } } Console.WriteLine($"[INFO] Loaded {recipes.Count} recipes from {_recipeFolder}"); return JsonConvert.SerializeObject(recipes); } catch (Exception ex) { Console.WriteLine($"[ERROR] Failed to get recipe list: {ex.Message}"); return "[]"; } } public string GetRecipe(string recipeId) { try { string recipePath = Path.Combine(_recipeFolder, $"{recipeId}.json"); if (!File.Exists(recipePath)) { var response = new { success = false, message = "Recipe not found" }; return JsonConvert.SerializeObject(response); } string json = File.ReadAllText(recipePath); Console.WriteLine($"[INFO] Loaded recipe {recipeId}"); return json; } catch (Exception ex) { Console.WriteLine($"[ERROR] Failed to get recipe {recipeId}: {ex.Message}"); var response = new { success = false, message = ex.Message }; return JsonConvert.SerializeObject(response); } } public string SaveRecipe(string recipeId, string recipeData) { try { string recipePath = Path.Combine(_recipeFolder, $"{recipeId}.json"); // Validate JSON format var recipe = JsonConvert.DeserializeObject(recipeData); // Save to file with formatting File.WriteAllText(recipePath, JsonConvert.SerializeObject(recipe, Formatting.Indented)); Console.WriteLine($"[INFO] Recipe {recipeId} saved successfully to {recipePath}"); var response = new { success = true, message = "Recipe saved successfully", recipeId = recipeId }; return JsonConvert.SerializeObject(response); } catch (Exception ex) { Console.WriteLine($"[ERROR] Failed to save recipe {recipeId}: {ex.Message}"); var response = new { success = false, message = ex.Message }; return JsonConvert.SerializeObject(response); } } public string CopyRecipe(string recipeId, string newName) { Console.WriteLine($"[C#] Copying Recipe: {recipeId} as {newName}"); try { string sourcePath = Path.Combine(_recipeFolder, $"{recipeId}.json"); if (!File.Exists(sourcePath)) { var response = new { success = false, message = "Source recipe not found" }; return JsonConvert.SerializeObject(response); } // Generate new ID string newId = Guid.NewGuid().ToString().Substring(0, 8); string destPath = Path.Combine(_recipeFolder, $"{newId}.json"); // Read source recipe string json = File.ReadAllText(sourcePath); var recipeData = JsonConvert.DeserializeObject(json); // Update name in recipe data recipeData.name = newName; // Save to new file File.WriteAllText(destPath, JsonConvert.SerializeObject(recipeData, Formatting.Indented)); string timestamp = DateTime.Now.ToString("yyyy-MM-dd"); Console.WriteLine($"[INFO] Recipe copied from {recipeId} to {newId}"); var response2 = new { success = true, message = "Recipe copied successfully", newRecipe = new { id = newId, name = newName, lastModified = timestamp } }; return JsonConvert.SerializeObject(response2); } catch (Exception ex) { Console.WriteLine($"[ERROR] Failed to copy recipe: {ex.Message}"); var response = new { success = false, message = ex.Message }; return JsonConvert.SerializeObject(response); } } public string DeleteRecipe(string recipeId) { Console.WriteLine($"[C#] Deleting Recipe: {recipeId}"); try { // Check if recipe is in use if (recipeId == _host.GetCurrentRecipe()) { var response1 = new { success = false, message = "Cannot delete currently selected recipe" }; return JsonConvert.SerializeObject(response1); } string recipePath = Path.Combine(_recipeFolder, $"{recipeId}.json"); if (!File.Exists(recipePath)) { var response = new { success = false, message = "Recipe not found" }; return JsonConvert.SerializeObject(response); } // Delete the file File.Delete(recipePath); Console.WriteLine($"[INFO] Recipe {recipeId} deleted successfully"); var response2 = new { success = true, message = "Recipe deleted successfully", recipeId = recipeId }; return JsonConvert.SerializeObject(response2); } catch (Exception ex) { Console.WriteLine($"[ERROR] Failed to delete recipe: {ex.Message}"); var response = new { success = false, message = ex.Message }; return JsonConvert.SerializeObject(response); } } } }