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>
This commit is contained in:
@@ -71,6 +71,121 @@ namespace Project.WebUI
|
||||
_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>
|
||||
@@ -90,11 +205,19 @@ namespace Project.WebUI
|
||||
}
|
||||
|
||||
// Select model using PUB.SelectModelV
|
||||
bool result = AR.PUB.SelectModelV(recipeTitle, true);
|
||||
bool result = PUB.SelectModelV(recipeTitle, true);
|
||||
|
||||
if (result)
|
||||
{
|
||||
Console.WriteLine($"[INFO] Recipe {recipeTitle} selected successfully");
|
||||
// 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);
|
||||
@@ -144,14 +267,14 @@ namespace Project.WebUI
|
||||
try
|
||||
{
|
||||
// Use SETTING.Data (CommonSetting) from the project
|
||||
if (AR.SETTING.Data == null)
|
||||
if (SETTING.Data == null)
|
||||
{
|
||||
Console.WriteLine($"[WARN] SETTING.Data is not initialized");
|
||||
return "[]";
|
||||
}
|
||||
|
||||
var settingsArray = new List<object>();
|
||||
var properties = AR.SETTING.Data.GetType().GetProperties();
|
||||
var properties = SETTING.Data.GetType().GetProperties();
|
||||
|
||||
foreach (var prop in properties)
|
||||
{
|
||||
@@ -167,7 +290,7 @@ namespace Project.WebUI
|
||||
|
||||
// Get property info
|
||||
string key = prop.Name;
|
||||
object value = prop.GetValue(AR.SETTING.Data, null);
|
||||
object value = prop.GetValue(SETTING.Data, null);
|
||||
string type = "String";
|
||||
string group = "General";
|
||||
string description = key.Replace("_", " ");
|
||||
@@ -241,7 +364,7 @@ namespace Project.WebUI
|
||||
var settingsArray = JsonConvert.DeserializeObject<List<Dictionary<string, object>>>(configJson);
|
||||
|
||||
// Update SETTING.Data properties
|
||||
var properties = AR.SETTING.Data.GetType().GetProperties();
|
||||
var properties = SETTING.Data.GetType().GetProperties();
|
||||
|
||||
foreach (var item in settingsArray)
|
||||
{
|
||||
@@ -258,22 +381,22 @@ namespace Project.WebUI
|
||||
{
|
||||
if (type == "Boolean")
|
||||
{
|
||||
prop.SetValue(AR.SETTING.Data, bool.Parse(value), null);
|
||||
prop.SetValue(SETTING.Data, bool.Parse(value), null);
|
||||
}
|
||||
else if (type == "Number")
|
||||
{
|
||||
if (prop.PropertyType == typeof(int))
|
||||
prop.SetValue(AR.SETTING.Data, int.Parse(value), null);
|
||||
prop.SetValue(SETTING.Data, int.Parse(value), null);
|
||||
else if (prop.PropertyType == typeof(long))
|
||||
prop.SetValue(AR.SETTING.Data, long.Parse(value), null);
|
||||
prop.SetValue(SETTING.Data, long.Parse(value), null);
|
||||
else if (prop.PropertyType == typeof(double))
|
||||
prop.SetValue(AR.SETTING.Data, double.Parse(value), null);
|
||||
prop.SetValue(SETTING.Data, double.Parse(value), null);
|
||||
else if (prop.PropertyType == typeof(float))
|
||||
prop.SetValue(AR.SETTING.Data, float.Parse(value), null);
|
||||
prop.SetValue(SETTING.Data, float.Parse(value), null);
|
||||
}
|
||||
else
|
||||
{
|
||||
prop.SetValue(AR.SETTING.Data, value, null);
|
||||
prop.SetValue(SETTING.Data, value, null);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
@@ -283,7 +406,7 @@ namespace Project.WebUI
|
||||
}
|
||||
|
||||
// Save to file
|
||||
AR.SETTING.Data.Save();
|
||||
SETTING.Data.Save();
|
||||
|
||||
Console.WriteLine($"[INFO] Settings saved successfully");
|
||||
}
|
||||
@@ -705,5 +828,126 @@ namespace Project.WebUI
|
||||
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>());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -212,6 +212,27 @@ namespace Project.WebUI
|
||||
var response = new { type = "RECIPE_SAVED", data = Newtonsoft.Json.JsonConvert.DeserializeObject(resultJson) };
|
||||
await Send(socket, Newtonsoft.Json.JsonConvert.SerializeObject(response));
|
||||
}
|
||||
else if (type == "INITIALIZE_DEVICE")
|
||||
{
|
||||
var bridge = new MachineBridge(_mainForm);
|
||||
string resultJson = bridge.InitializeDevice();
|
||||
var response = new { type = "DEVICE_INITIALIZED", data = Newtonsoft.Json.JsonConvert.DeserializeObject(resultJson) };
|
||||
await Send(socket, Newtonsoft.Json.JsonConvert.SerializeObject(response));
|
||||
}
|
||||
else if (type == "GET_INITIALIZE_STATUS")
|
||||
{
|
||||
var bridge = new MachineBridge(_mainForm);
|
||||
string statusJson = bridge.GetInitializeStatus();
|
||||
var response = new { type = "INITIALIZE_STATUS_DATA", data = Newtonsoft.Json.JsonConvert.DeserializeObject(statusJson) };
|
||||
await Send(socket, Newtonsoft.Json.JsonConvert.SerializeObject(response));
|
||||
}
|
||||
else if (type == "GET_PROCESSED_DATA")
|
||||
{
|
||||
var bridge = new MachineBridge(_mainForm);
|
||||
string dataJson = bridge.GetProcessedData();
|
||||
var response = new { type = "PROCESSED_DATA", data = Newtonsoft.Json.JsonConvert.DeserializeObject(dataJson) };
|
||||
await Send(socket, Newtonsoft.Json.JsonConvert.SerializeObject(response));
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user