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() { try { var result = new { inputs = GetDigitalInputs(), outputs = GetDigitalOutputs(), interlocks = GetInterlocks() }; return JsonConvert.SerializeObject(result); } catch (Exception ex) { Console.WriteLine($"[ERROR] Failed to get IO list: {ex.Message}"); return JsonConvert.SerializeObject(new { inputs = new List(), outputs = new List(), interlocks = new List() }); } } /// /// 마이그레이션완료 /// /// private List GetDigitalInputs() { var inputs = new List(); try { var diNames = DIO.Pin.GetDIName; var diPinNames = DIO.Pin.GetDIPinName; int diCount = PUB.dio.GetDICount; for (int i = 0; i < diCount; i++) { bool state = PUB.dio.GetDIValue(i); string name = i < diNames.Length ? diNames[i] : $"DIN_{i:D2}"; string pinName = i < diPinNames.Length ? diPinNames[i] : $"X{i:D2}"; inputs.Add(new { id = i, name = name, pinName = pinName, type = "input", state = state }); } } catch (Exception ex) { Console.WriteLine($"[ERROR] Failed to get digital inputs: {ex.Message}"); } return inputs; } /// /// 마이그레이션완료 /// /// private List GetDigitalOutputs() { var outputs = new List(); try { var doNames = DIO.Pin.GetDOName; var doPinNames = DIO.Pin.GetDOPinName; int doCount = PUB.dio.GetDOCount; for (int i = 0; i < doCount; i++) { bool state = PUB.dio.GetDOValue(i); string name = i < doNames.Length ? doNames[i] : $"DOUT_{i:D2}"; string pinName = i < doPinNames.Length ? doPinNames[i] : $"Y{i:D2}"; outputs.Add(new { id = i, name = name, pinName = pinName, type = "output", state = state }); } } catch (Exception ex) { Console.WriteLine($"[ERROR] Failed to get digital outputs: {ex.Message}"); } return outputs; } /// /// 마이그레이션완료 /// /// private List GetInterlocks() { var interlocks = new List(); try { for (int i = 0; i < PUB.iLock.Length; i++) { var axisName = ((AR.eAxis)i).ToString(); var nonAxis = false; // Check if this is a non-axis interlock (i >= 7) if (i >= 7) { axisName = PUB.iLock[i].Tag.ToString(); nonAxis = true; } // Get enum names based on axis index string[] ilockNames; if (i == 7) ilockNames = Enum.GetNames(typeof(eILockPRL)); else if (i == 8) ilockNames = Enum.GetNames(typeof(eILockPRR)); else if (i == 9) ilockNames = Enum.GetNames(typeof(eILockVS0)); else if (i == 10) ilockNames = Enum.GetNames(typeof(eILockVS1)); else if (i == 11) ilockNames = Enum.GetNames(typeof(eILockVS2)); else if (i == 12 || i == 13) ilockNames = Enum.GetNames(typeof(eILockCV)); else ilockNames = Enum.GetNames(typeof(eILock)); // Get interlock values (up to 64 bits) var lockValues = new List(); for (int j = 0; j < ilockNames.Length && j < 64; j++) { bool state = PUB.iLock[i].get(j); lockValues.Add(new { id = j, name = ilockNames[j], state = state }); } interlocks.Add(new { axisIndex = i, axisName = axisName, nonAxis = nonAxis, locks = lockValues, hexValue = PUB.iLock[i].Value().HexString() }); } } catch (Exception ex) { Console.WriteLine($"[ERROR] Failed to get interlocks: {ex.Message}"); } return interlocks; } /// /// 마이그레이션완료 /// /// public string GetRecipeList() { //PUB.mdm.dataSet.OPModel에 등록된 데이터가 모델 목록이다 try { var recipes = new List(); if (PUB.mdm == null || PUB.mdm.dataSet == null || PUB.mdm.dataSet.OPModel == null) { Console.WriteLine($"[WARN] OPModel is not initialized"); return JsonConvert.SerializeObject(recipes); } // OPModel 테이블에서 Title이 비어있지 않은 항목만 가져오기 foreach (var row in PUB.mdm.dataSet.OPModel) { var modelRow = row as DataSet1.OPModelRow; if (modelRow == null) continue; // Title이 비어있으면 스킵 if (string.IsNullOrEmpty(modelRow.Title)) continue; recipes.Add(new { id = modelRow.idx.ToString(), name = modelRow.Title, lastModified = DateTime.Now.ToString("yyyy-MM-dd") }); } Console.WriteLine($"[INFO] Loaded {recipes.Count} recipes from OPModel"); return JsonConvert.SerializeObject(recipes); } catch (Exception ex) { Console.WriteLine($"[ERROR] Failed to get recipe list: {ex.Message}"); return "[]"; } } /// /// 마이그레이션완료 /// /// /// public string GetRecipe(string recipeTitle) { try { var dr = PUB.mdm.GetDataV(recipeTitle); if (dr == null) { var response = new { success = false, message = "Recipe not found" }; return JsonConvert.SerializeObject(response); } // DataRow의 모든 컬럼을 Dictionary로 변환 var recipeData = new Dictionary(); foreach (System.Data.DataColumn col in dr.Table.Columns) { string colName = col.ColumnName; object value = dr[colName]; // DBNull을 null로 변환 if (value == DBNull.Value) value = null; recipeData[colName] = value; } Console.WriteLine($"[INFO] Loaded recipe {recipeTitle}"); return JsonConvert.SerializeObject(recipeData); } catch (Exception ex) { Console.WriteLine($"[ERROR] Failed to get recipe {recipeTitle}: {ex.Message}"); var response = new { success = false, message = ex.Message }; return JsonConvert.SerializeObject(response); } } /// /// 마이그레이션완료 /// /// /// /// public string SaveRecipe(string recipeTitle, string recipeData) { try { var dr = PUB.mdm.GetDataV(recipeTitle); if (dr == null) { var response = new { success = false, message = "Recipe not found" }; return JsonConvert.SerializeObject(response); } // JSON 데이터를 Dictionary로 변환 var recipeDict = JsonConvert.DeserializeObject>(recipeData); // DataRow의 각 컬럼 업데이트 foreach (var kvp in recipeDict) { string colName = kvp.Key; // 컬럼이 테이블에 존재하는지 확인 if (!dr.Table.Columns.Contains(colName)) continue; // idx나 Midx 같은 자동 생성 컬럼은 스킵 if (colName == "idx" || colName == "Midx") continue; try { var col = dr.Table.Columns[colName]; object value = kvp.Value; // null 처리 if (value == null) { dr[colName] = DBNull.Value; } else { // 타입 변환 if (col.DataType == typeof(bool)) dr[colName] = Convert.ToBoolean(value); else if (col.DataType == typeof(int)) dr[colName] = Convert.ToInt32(value); else if (col.DataType == typeof(double)) dr[colName] = Convert.ToDouble(value); else if (col.DataType == typeof(float)) dr[colName] = Convert.ToSingle(value); else dr[colName] = value.ToString(); } } catch (Exception ex) { Console.WriteLine($"[WARN] Failed to set column {colName}: {ex.Message}"); } } // 변경사항 저장 PUB.mdm.dataSet.OPModel.AcceptChanges(); PUB.mdm.SaveModelV(); Console.WriteLine($"[INFO] Recipe {recipeTitle} saved successfully to OPModel"); var response2 = new { success = true, message = "Recipe saved successfully", recipeId = recipeTitle }; return JsonConvert.SerializeObject(response2); } catch (Exception ex) { Console.WriteLine($"[ERROR] Failed to save recipe {recipeTitle}: {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); } } } }