using System; using System.Runtime.InteropServices; using System.Windows.Forms; using Newtonsoft.Json; using System.IO; using System.Linq; using System.Collections.Generic; using AR; namespace Project.WebUI { // Important: Allows JavaScript to see this class [ClassInterface(ClassInterfaceType.AutoDual)] [ComVisible(true)] public class MachineBridge { // Reference to the main form to update logic private Project.Dialog.fWebView _host; // Data folder paths private readonly string _dataFolder; private readonly string _settingsPath; private readonly string _recipeFolder; public MachineBridge(Project.Dialog.fWebView host) { _host = host; // Initialize data folder paths - use existing Data folder _dataFolder = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Data"); _settingsPath = Path.Combine(_dataFolder, "Setting.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); } catch (Exception ex) { Console.WriteLine($"[ERROR] Failed to create data folders: {ex.Message}"); } } // --- Methods called from React --- public void MoveAxis(string axis, double value) { Console.WriteLine($"[C#] Moving {axis} to {value}"); _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); } string json = File.ReadAllText(recipePath); var recipeData = JsonConvert.DeserializeObject(json); _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 { // Use SETTING.Data (CommonSetting) from the project if (AR.SETTING.Data == null) { Console.WriteLine($"[WARN] SETTING.Data is not initialized"); return "[]"; } var settingsArray = new List(); var properties = AR.SETTING.Data.GetType().GetProperties(); foreach (var prop in properties) { // Skip non-browsable properties var browsable = prop.GetCustomAttributes(typeof(System.ComponentModel.BrowsableAttribute), false) .FirstOrDefault() as System.ComponentModel.BrowsableAttribute; if (browsable != null && !browsable.Browsable) continue; // Skip filename property if (prop.Name == "filename") continue; // Get property info string key = prop.Name; object value = prop.GetValue(AR.SETTING.Data, null); string type = "String"; string group = "General"; string description = key.Replace("_", " "); // Get Category attribute var categoryAttr = prop.GetCustomAttributes(typeof(System.ComponentModel.CategoryAttribute), false) .FirstOrDefault() as System.ComponentModel.CategoryAttribute; if (categoryAttr != null) group = categoryAttr.Category; // Get DisplayName attribute var displayNameAttr = prop.GetCustomAttributes(typeof(System.ComponentModel.DisplayNameAttribute), false) .FirstOrDefault() as System.ComponentModel.DisplayNameAttribute; if (displayNameAttr != null) description = displayNameAttr.DisplayName; // Get Description attribute var descAttr = prop.GetCustomAttributes(typeof(System.ComponentModel.DescriptionAttribute), false) .FirstOrDefault() as System.ComponentModel.DescriptionAttribute; if (descAttr != null) description = descAttr.Description; // Determine type based on property type if (prop.PropertyType == typeof(bool)) { type = "Boolean"; value = value?.ToString().ToLower() ?? "false"; } else if (prop.PropertyType == typeof(int) || prop.PropertyType == typeof(long) || prop.PropertyType == typeof(double) || prop.PropertyType == typeof(float)) { type = "Number"; value = value?.ToString() ?? "0"; } else { value = value?.ToString() ?? ""; } settingsArray.Add(new { Key = key, Value = value, Group = group, Type = type, Description = description }); } Console.WriteLine($"[INFO] Loaded {settingsArray.Count} settings from SETTING.Data"); return JsonConvert.SerializeObject(settingsArray); } 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"); // Parse array format from React var settingsArray = JsonConvert.DeserializeObject>>(configJson); // Update SETTING.Data properties var properties = AR.SETTING.Data.GetType().GetProperties(); foreach (var item in settingsArray) { string key = item["Key"].ToString(); string value = item["Value"].ToString(); string type = item["Type"].ToString(); var prop = properties.FirstOrDefault(p => p.Name == key); if (prop == null || !prop.CanWrite) continue; // Convert value based on type try { if (type == "Boolean") { prop.SetValue(AR.SETTING.Data, bool.Parse(value), null); } else if (type == "Number") { if (prop.PropertyType == typeof(int)) prop.SetValue(AR.SETTING.Data, int.Parse(value), null); else if (prop.PropertyType == typeof(long)) prop.SetValue(AR.SETTING.Data, long.Parse(value), null); else if (prop.PropertyType == typeof(double)) prop.SetValue(AR.SETTING.Data, double.Parse(value), null); else if (prop.PropertyType == typeof(float)) prop.SetValue(AR.SETTING.Data, float.Parse(value), null); } else { prop.SetValue(AR.SETTING.Data, value, null); } } catch (Exception ex) { Console.WriteLine($"[WARN] Failed to set property {key}: {ex.Message}"); } } // Save to file AR.SETTING.Data.Save(); Console.WriteLine($"[INFO] Settings saved successfully"); } catch (Exception ex) { Console.WriteLine($"[ERROR] Failed to save settings: {ex.Message}"); } } public string GetIOList() { var ioList = new List(); // 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 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"); var recipe = JsonConvert.DeserializeObject(recipeData); 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); } string newId = Guid.NewGuid().ToString().Substring(0, 8); string destPath = Path.Combine(_recipeFolder, $"{newId}.json"); string json = File.ReadAllText(sourcePath); var recipeData = JsonConvert.DeserializeObject(json); recipeData.name = newName; 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 { 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); } 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); } } } }