Files
WebUITest-RealProjecT/Handler/Project/WebUI/MachineBridge.cs
arDTDev 6219c4c60e feat: Implement vision menu, processed data panel, and UI improvements
- Add VisionMenu component with Camera (QRCode) and Barcode (Keyence) submenus
- Remove old CameraPanel component and replace with dropdown menu structure
- Add ProcessedDataPanel to display processed data in bottom dock (5 rows visible)
- Create SystemStatusPanel component with horizontal button layout (START/STOP/RESET)
- Create EventLogPanel component for better code organization
- Add device initialization feature with 7-axis progress tracking
- Add GetProcessedData and GetInitializeStatus backend methods
- Update Header menu layout to vertical (icon on top, text below) for more space
- Update HomePage layout with bottom-docked ProcessedDataPanel
- Refactor HomePage to use new modular components

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-25 22:18:55 +09:00

954 lines
37 KiB
C#
Raw Blame History

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 InitializeDevice()
{
Console.WriteLine($"[C#] Initialize Device Request");
try
{
// Check if motion is initialized
if (PUB.flag.get(eVarBool.FG_INIT_MOTIO) == false)
{
var response = new { success = false, message = "Motion not initialized\nPlease try again later" };
return JsonConvert.SerializeObject(response);
}
var errors = new List<string>();
// Check if system is running
if (PUB.sm.isRunning == true)
errors.Add("AUTO-RUN MODE - Cannot be used during automatic execution. Please stop and try again");
// Check AIR supply
if (DIO.GetIOOutput(eDOName.SOL_AIR) == false)
errors.Add("Press the AIR button on the front to supply AIR");
// Check printer pickers
if (DIO.GetIOInput(eDIName.L_PICK_BW) == false)
errors.Add("Left printer picker is not in reverse position");
if (DIO.GetIOInput(eDIName.R_PICK_BW) == false)
errors.Add("Right printer picker is not in reverse position");
// Check picker cylinders
if (SETTING.Data.Enable_PickerCylinder)
{
if (DIO.GetIOInput(eDIName.L_CYLUP) == false)
errors.Add("Left printer picker cylinder is not in UP position");
if (DIO.GetIOInput(eDIName.R_CYLUP) == false)
errors.Add("Right printer picker cylinder is not in UP position");
}
// Check emergency stop
if (DIO.IsEmergencyOn() == true)
errors.Add("Cannot move when in emergency stop state");
// Check if running
if (PUB.sm.Step == eSMStep.RUN)
errors.Add("Cannot initialize while in operation");
if (errors.Count > 0)
{
var response = new { success = false, message = string.Join("\n", errors) };
return JsonConvert.SerializeObject(response);
}
// Start initialization
PUB.log.Add("User Request: Initialize Device", false);
PUB.sm.SetNewStep(eSMStep.HOME_FULL);
var response2 = new { success = true, message = "Device initialization started" };
return JsonConvert.SerializeObject(response2);
}
catch (Exception ex)
{
Console.WriteLine($"[ERROR] Failed to initialize device: {ex.Message}");
var response = new { success = false, message = ex.Message };
return JsonConvert.SerializeObject(response);
}
}
public string GetInitializeStatus()
{
try
{
var status = new
{
isInitializing = PUB.sm.Step == eSMStep.HOME_FULL,
currentStep = PUB.sm.Step.ToString(),
axes = new List<object>
{
new { name = "PZ_PICK", axisIndex = (int)eAxis.PZ_PICK, isHomeSet = PUB.mot.IsHomeSet((short)eAxis.PZ_PICK), progress = GetAxisProgress(eAxis.PZ_PICK) },
new { name = "PL_UPDN", axisIndex = (int)eAxis.PL_UPDN, isHomeSet = PUB.mot.IsHomeSet((short)eAxis.PL_UPDN), progress = GetAxisProgress(eAxis.PL_UPDN) },
new { name = "PR_UPDN", axisIndex = (int)eAxis.PR_UPDN, isHomeSet = PUB.mot.IsHomeSet((short)eAxis.PR_UPDN), progress = GetAxisProgress(eAxis.PR_UPDN) },
new { name = "PL_MOVE", axisIndex = (int)eAxis.PL_MOVE, isHomeSet = PUB.mot.IsHomeSet((short)eAxis.PL_MOVE), progress = GetAxisProgress(eAxis.PL_MOVE) },
new { name = "PR_MOVE", axisIndex = (int)eAxis.PR_MOVE, isHomeSet = PUB.mot.IsHomeSet((short)eAxis.PR_MOVE), progress = GetAxisProgress(eAxis.PR_MOVE) },
new { name = "Z_THETA", axisIndex = (int)eAxis.Z_THETA, isHomeSet = PUB.mot.IsHomeSet((short)eAxis.Z_THETA), progress = GetAxisProgress(eAxis.Z_THETA) },
new { name = "PX_PICK", axisIndex = (int)eAxis.PX_PICK, isHomeSet = PUB.mot.IsHomeSet((short)eAxis.PX_PICK), progress = GetAxisProgress(eAxis.PX_PICK) }
}
};
return JsonConvert.SerializeObject(status);
}
catch (Exception ex)
{
Console.WriteLine($"[ERROR] Failed to get initialize status: {ex.Message}");
return JsonConvert.SerializeObject(new { isInitializing = false, currentStep = "ERROR", axes = new List<object>() });
}
}
private int GetAxisProgress(eAxis axis)
{
// Return 100 if home is set, otherwise check motion status
if (PUB.mot.IsHomeSet((short)axis))
return 100;
// Check if axis is in motion
if (PUB.mot.IsMotion((short)axis))
return 50; // In progress
// Check if home search started
if (PUB.mot.IsOrg((short)axis) || PUB.mot.IsLimitN((short)axis))
return 75; // Near completion
return 0; // Not started
}
/// <summary>
/// 마이그레이션완료
/// </summary>
/// <param name="recipeTitle"></param>
/// <returns></returns>
public string SelectRecipe(string recipeTitle)
{
Console.WriteLine($"[C#] Selecting Recipe: {recipeTitle}");
try
{
// Check if system is running
if (PUB.sm.Step == eSMStep.RUN || PUB.sm.Step == eSMStep.WAITSTART || PUB.sm.Step == eSMStep.PAUSE)
{
var response = new { success = false, message = "Cannot change model while system is currently running (waiting)" };
return JsonConvert.SerializeObject(response);
}
// Select model using PUB.SelectModelV
bool result = PUB.SelectModelV(recipeTitle, true);
if (result)
{
// Select motion model based on conveyor usage
var motionmode = VAR.BOOL[eVarBool.Use_Conveyor] ? "Conveyor" : "Default";
bool changeMot = PUB.SelectModelM(motionmode);
if (changeMot == false)
{
PUB.log.AddE($"No Motion model ({motionmode})");
}
Console.WriteLine($"[INFO] Recipe {recipeTitle} selected successfully with motion mode: {motionmode}");
// Get the selected model data
var dr = PUB.mdm.GetDataV(recipeTitle);
if (dr == null)
{
var response = new { success = false, message = "Recipe data not found" };
return JsonConvert.SerializeObject(response);
}
// Convert DataRow to Dictionary
var recipeData = new Dictionary<string, object>();
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;
}
var response2 = new { success = true, message = "Recipe selected successfully", recipeId = recipeTitle, recipe = recipeData };
return JsonConvert.SerializeObject(response2);
}
else
{
var response = new { success = false, message = "Failed to select recipe" };
return JsonConvert.SerializeObject(response);
}
}
catch (Exception ex)
{
Console.WriteLine($"[ERROR] Failed to select recipe: {ex.Message}");
var response = new { success = false, message = ex.Message };
return JsonConvert.SerializeObject(response);
}
}
/// <summary>
/// <20><><EFBFBD>̱׷<CCB1><D7B7>̼<EFBFBD> <20><><EFBFBD><EFBFBD>
/// </summary>
/// <returns></returns>
public string GetConfig()
{
try
{
// Use SETTING.Data (CommonSetting) from the project
if (SETTING.Data == null)
{
Console.WriteLine($"[WARN] SETTING.Data is not initialized");
return "[]";
}
var settingsArray = new List<object>();
var properties = 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(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 "[]";
}
}
/// <summary>
/// <20><><EFBFBD>̱׷<CCB1><D7B7>̼Ǽ<CCBC><C7BC><EFBFBD>
/// </summary>
/// <param name="configJson"></param>
public void SaveConfig(string configJson)
{
try
{
Console.WriteLine($"[Backend] SAVE CONFIG REQUEST RECEIVED");
// Parse array format from React
var settingsArray = JsonConvert.DeserializeObject<List<Dictionary<string, object>>>(configJson);
// Update SETTING.Data properties
var properties = 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(SETTING.Data, bool.Parse(value), null);
}
else if (type == "Number")
{
if (prop.PropertyType == typeof(int))
prop.SetValue(SETTING.Data, int.Parse(value), null);
else if (prop.PropertyType == typeof(long))
prop.SetValue(SETTING.Data, long.Parse(value), null);
else if (prop.PropertyType == typeof(double))
prop.SetValue(SETTING.Data, double.Parse(value), null);
else if (prop.PropertyType == typeof(float))
prop.SetValue(SETTING.Data, float.Parse(value), null);
}
else
{
prop.SetValue(SETTING.Data, value, null);
}
}
catch (Exception ex)
{
Console.WriteLine($"[WARN] Failed to set property {key}: {ex.Message}");
}
}
// Save to file
SETTING.Data.Save();
Console.WriteLine($"[INFO] Settings saved successfully");
}
catch (Exception ex)
{
Console.WriteLine($"[ERROR] Failed to save settings: {ex.Message}");
}
}
/// <summary>
/// 마이그레이션완료
/// </summary>
/// <returns></returns>
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<object>(), outputs = new List<object>(), interlocks = new List<object>() });
}
}
/// <summary>
/// 마이그레이션완료
/// </summary>
/// <returns></returns>
private List<object> GetDigitalInputs()
{
var inputs = new List<object>();
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;
}
/// <summary>
/// 마이그레이션완료
/// </summary>
/// <returns></returns>
private List<object> GetDigitalOutputs()
{
var outputs = new List<object>();
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;
}
/// <summary>
/// 마이그레이션완료
/// </summary>
/// <returns></returns>
private List<object> GetInterlocks()
{
var interlocks = new List<object>();
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<object>();
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;
}
/// <summary>
/// 마이그레이션완료
/// </summary>
/// <returns></returns>
public string GetRecipeList()
{
//PUB.mdm.dataSet.OPModel에 등록된 데이터가 모델 목록이다
try
{
var recipes = new List<object>();
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 "[]";
}
}
/// <summary>
/// 마이그레이션완료
/// </summary>
/// <param name="recipeTitle"></param>
/// <returns></returns>
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<string, object>();
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);
}
}
/// <summary>
/// 마이그레이션완료
/// </summary>
/// <param name="recipeTitle"></param>
/// <param name="recipeData"></param>
/// <returns></returns>
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<Dictionary<string, object>>(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<dynamic>(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);
}
}
public string GetProcessedData()
{
try
{
// TODO: Connect to actual data source (FMain.dataSet1.K4EE_Component_Reel_Result)
// For now, return sample data structure
var processedData = new List<object>();
// Sample data for testing - replace with actual data from FMain or database
var sampleData = new[] {
new {
target = "1",
JTYPE = "MODEL_A",
STIME = DateTime.Now.AddMinutes(-30).ToString("HH:mm:ss"),
BATCH = DateTime.Now.AddMinutes(-30).ToString("HH:mm:ss"),
SID = "SID123456",
RID = "RID789012",
VNAME = "VENDOR_A",
LOC = "L",
QTY = 1250,
qtymax = 2500,
MFGDATE = "2025-11-20",
VLOT = "LOT001",
PARTNO = "PN12345",
MCN = "CPN001",
REMARK = "",
PRNATTACH = true,
PRNVALID = true
},
new {
target = "2",
JTYPE = "MODEL_B",
STIME = DateTime.Now.AddMinutes(-25).ToString("HH:mm:ss"),
BATCH = DateTime.Now.AddMinutes(-25).ToString("HH:mm:ss"),
SID = "SID234567",
RID = "RID890123",
VNAME = "VENDOR_B",
LOC = "R",
QTY = 3500,
qtymax = 5000,
MFGDATE = "2025-11-21",
VLOT = "LOT002",
PARTNO = "PN23456",
MCN = "CPN002",
REMARK = "",
PRNATTACH = true,
PRNVALID = false
},
new {
target = "3",
JTYPE = "MODEL_A",
STIME = DateTime.Now.AddMinutes(-20).ToString("HH:mm:ss"),
BATCH = DateTime.Now.AddMinutes(-20).ToString("HH:mm:ss"),
SID = "SID345678",
RID = "RID901234",
VNAME = "VENDOR_A",
LOC = "L",
QTY = 800,
qtymax = 2500,
MFGDATE = "2025-11-22",
VLOT = "LOT003",
PARTNO = "PN34567",
MCN = "CPN003",
REMARK = "(BYPASS)",
PRNATTACH = false,
PRNVALID = true
},
new {
target = "4",
JTYPE = "MODEL_C",
STIME = DateTime.Now.AddMinutes(-15).ToString("HH:mm:ss"),
BATCH = DateTime.Now.AddMinutes(-15).ToString("HH:mm:ss"),
SID = "SID456789",
RID = "RID012345",
VNAME = "VENDOR_C",
LOC = "R",
QTY = 4200,
qtymax = 6000,
MFGDATE = "2025-11-23",
VLOT = "LOT004",
PARTNO = "PN45678",
MCN = "CPN004",
REMARK = "",
PRNATTACH = true,
PRNVALID = true
},
new {
target = "5",
JTYPE = "MODEL_B",
STIME = DateTime.Now.AddMinutes(-10).ToString("HH:mm:ss"),
BATCH = DateTime.Now.AddMinutes(-10).ToString("HH:mm:ss"),
SID = "SID567890",
RID = "RID123456",
VNAME = "VENDOR_B",
LOC = "L",
QTY = 1800,
qtymax = 5000,
MFGDATE = "2025-11-24",
VLOT = "LOT005",
PARTNO = "PN56789",
MCN = "CPN005",
REMARK = "",
PRNATTACH = true,
PRNVALID = true
}
};
foreach (var item in sampleData)
{
processedData.Add(item);
}
return JsonConvert.SerializeObject(processedData);
}
catch (Exception ex)
{
Console.WriteLine($"[ERROR] Failed to get processed data: {ex.Message}");
return JsonConvert.SerializeObject(new List<object>());
}
}
}
}