Files
WebUITest-RealProjecT/Handler/Project/WebUI/MachineBridge.cs
arDTDev 86fe466b55 feat: Add VisionData panel, HW error display, and reel handler 3D model
- Add hardware error banner with priority system (motion > i/o > emergency)
- Add DIO status to HW status display with backend integration
- Remove status text from HW status, keep only LED indicators
- Add VisionDataPanel showing real-time recognized data for L/C/R ports
- Add GetVisionData API in MachineBridge with batch field support
- Add BroadcastVisionData function (250ms interval)
- Replace 3D model with detailed reel handler equipment
- Use OrthographicCamera with front view for distortion-free display
- Fix ProcessedDataPanel layout to avoid right sidebar overlap
- Show log viewer filename in error message when file not found

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-27 23:22:56 +09:00

2404 lines
95 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;
using System.Threading.Tasks;
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>());
}
}
// ===== VISION CONTROL METHODS =====
public string CameraConnect()
{
try
{
if (PUB.wsL != null && PUB.wsL.Connected)
{
var response = new { success = false, message = "Camera is already connected" };
return JsonConvert.SerializeObject(response);
}
if (string.IsNullOrEmpty(SETTING.Data.CameraLFile))
{
var response = new { success = false, message = "Camera program filename not specified" };
return JsonConvert.SerializeObject(response);
}
var fi = new System.IO.FileInfo(SETTING.Data.CameraLFile);
if (!fi.Exists)
{
var response = new { success = false, message = $"Camera program file does not exist\n{fi.FullName}" };
return JsonConvert.SerializeObject(response);
}
PUB.log.Add("User Request: Connect Camera (QRCode)", false);
// Camera connection logic would be implemented here
// For now, return success
var response2 = new { success = true, message = "Camera connection initiated" };
return JsonConvert.SerializeObject(response2);
}
catch (Exception ex)
{
Console.WriteLine($"[ERROR] Failed to connect camera: {ex.Message}");
var response = new { success = false, message = ex.Message };
return JsonConvert.SerializeObject(response);
}
}
public string CameraDisconnect()
{
try
{
PUB.log.Add("User Request: Disconnect Camera (QRCode)", false);
// Camera disconnection logic
var response = new { success = true, message = "Camera disconnected" };
return JsonConvert.SerializeObject(response);
}
catch (Exception ex)
{
Console.WriteLine($"[ERROR] Failed to disconnect camera: {ex.Message}");
var response = new { success = false, message = ex.Message };
return JsonConvert.SerializeObject(response);
}
}
public string CameraGetImage()
{
try
{
PUB.log.Add("User Request: Get Camera Image", false);
// Get image logic
var response = new { success = true, message = "Image captured" };
return JsonConvert.SerializeObject(response);
}
catch (Exception ex)
{
Console.WriteLine($"[ERROR] Failed to get camera image: {ex.Message}");
var response = new { success = false, message = ex.Message };
return JsonConvert.SerializeObject(response);
}
}
public string CameraLiveView()
{
try
{
PUB.log.Add("User Request: Camera Live View", false);
// Live view logic
var response = new { success = true, message = "Live view started" };
return JsonConvert.SerializeObject(response);
}
catch (Exception ex)
{
Console.WriteLine($"[ERROR] Failed to start live view: {ex.Message}");
var response = new { success = false, message = ex.Message };
return JsonConvert.SerializeObject(response);
}
}
public string CameraReadTest()
{
try
{
PUB.log.Add("User Request: Camera Read Test", false);
// Read test logic
var response = new { success = true, message = "Read test completed" };
return JsonConvert.SerializeObject(response);
}
catch (Exception ex)
{
Console.WriteLine($"[ERROR] Failed to run read test: {ex.Message}");
var response = new { success = false, message = ex.Message };
return JsonConvert.SerializeObject(response);
}
}
public string KeyenceTriggerOn()
{
try
{
PUB.log.Add("User Request: Keyence Trigger ON", false);
PUB.keyenceF.Trigger(true);
PUB.keyenceR.Trigger(true);
var response = new { success = true, message = "Keyence trigger ON" };
return JsonConvert.SerializeObject(response);
}
catch (Exception ex)
{
Console.WriteLine($"[ERROR] Failed to trigger keyence on: {ex.Message}");
var response = new { success = false, message = ex.Message };
return JsonConvert.SerializeObject(response);
}
}
public string KeyenceTriggerOff()
{
try
{
PUB.log.Add("User Request: Keyence Trigger OFF", false);
PUB.keyenceF.Trigger(false);
PUB.keyenceR.Trigger(false);
var response = new { success = true, message = "Keyence trigger OFF" };
return JsonConvert.SerializeObject(response);
}
catch (Exception ex)
{
Console.WriteLine($"[ERROR] Failed to trigger keyence off: {ex.Message}");
var response = new { success = false, message = ex.Message };
return JsonConvert.SerializeObject(response);
}
}
public string KeyenceGetImage()
{
try
{
PUB.log.Add("User Request: Get Keyence Image", false);
var curimageF = PUB.keyenceF.GetImage();
var curimageR = PUB.keyenceR.GetImage();
var response = new { success = true, message = "Keyence images captured" };
return JsonConvert.SerializeObject(response);
}
catch (Exception ex)
{
Console.WriteLine($"[ERROR] Failed to get keyence image: {ex.Message}");
var response = new { success = false, message = ex.Message };
return JsonConvert.SerializeObject(response);
}
}
public string KeyenceSaveImage()
{
try
{
PUB.log.Add("User Request: Save Keyence Image", false);
var fn = System.IO.Path.Combine(AR.UTIL.CurrentPath, "Images", "keyence.bmp");
var dir = System.IO.Path.GetDirectoryName(fn);
if (!System.IO.Directory.Exists(dir))
{
System.IO.Directory.CreateDirectory(dir);
}
// SaveImage logic would go here
var response = new { success = true, message = $"Image saved to {fn}" };
return JsonConvert.SerializeObject(response);
}
catch (Exception ex)
{
Console.WriteLine($"[ERROR] Failed to save keyence image: {ex.Message}");
var response = new { success = false, message = ex.Message };
return JsonConvert.SerializeObject(response);
}
}
public string ToggleLight()
{
try
{
if (PUB.flag.get(AR.eVarBool.FG_INIT_MOTIO) == false)
{
var response = new { success = false, message = "Motion not initialized. Please try again later" };
return JsonConvert.SerializeObject(response);
}
var cur = DIO.GetIOOutput(AR.eDOName.ROOMLIGHT);
var success = DIO.SetRoomLight(!cur, true);
if (success == false)
{
var response = new { success = false, message = "Failed to control room light" };
return JsonConvert.SerializeObject(response);
}
PUB.log.Add($"User Request: Room Light {(!cur ? "ON" : "OFF")}", false);
var response2 = new { success = true, message = $"Light turned {(!cur ? "ON" : "OFF")}" };
return JsonConvert.SerializeObject(response2);
}
catch (Exception ex)
{
Console.WriteLine($"[ERROR] Failed to toggle light: {ex.Message}");
var response = new { success = false, message = ex.Message };
return JsonConvert.SerializeObject(response);
}
}
public string ExecuteManualPrint(string sid, string venderLot, string qty, string mfg, string rid, string spy, string partNo, string printer, int count)
{
try
{
// Parse quantity
if (!int.TryParse(qty, out int vqty))
{
var response = new { success = false, message = "Please enter quantity as a number" };
return JsonConvert.SerializeObject(response);
}
// Select printer
var selectedPrinter = printer.ToLower() == "left" ? PUB.PrinterL : PUB.PrinterR;
// Create ZPL
string zpl = selectedPrinter.makeZPL_210908(new Class.Reel
{
SID = sid,
venderLot = venderLot,
venderName = spy,
qty = vqty,
id = rid,
mfg = mfg,
PartNo = partNo,
}, SETTING.Data.DrawOutbox, out string qrdata);
// Print
for (int i = 0; i < count; i++)
{
var prn = selectedPrinter.Print(zpl);
if (prn.result == false)
{
PUB.log.AddE(prn.errmessage);
var response = new { success = false, message = $"Cannot proceed further due to printing failure: {prn.errmessage}" };
return JsonConvert.SerializeObject(response);
}
else
{
if (i == 0) // Log only first print
PUB.log.Add($"User print completed: {string.Join("|", new string[] { sid, venderLot, spy, qty, rid, mfg, partNo })}", false);
}
System.Threading.Thread.Sleep(100);
}
var response2 = new { success = true, message = $"Successfully printed {count} label(s)" };
return JsonConvert.SerializeObject(response2);
}
catch (Exception ex)
{
Console.WriteLine($"[ERROR] Failed to execute manual print: {ex.Message}");
var response = new { success = false, message = ex.Message };
return JsonConvert.SerializeObject(response);
}
}
public string CancelJob()
{
try
{
if (PUB.flag.get(AR.eVarBool.FG_INIT_MOTIO) == false)
{
var response = new { success = false, message = "Motion not initialized. Please try again later" };
return JsonConvert.SerializeObject(response);
}
var msg = new System.Text.StringBuilder();
if (PUB.mot.HasHomeSetOff)
{
msg.AppendLine("Device initialization is not complete. Execute device initialization");
}
if (PUB.sm.isRunning == true)
{
msg.AppendLine("AUTO-RUN MODE. Cannot be used during automatic execution. Please stop and try again");
}
if (DIO.IsEmergencyOn() == true)
{
msg.AppendLine("Release emergency stop");
}
if (DIO.isSaftyDoorF() == false)
{
msg.AppendLine("Front door is open");
}
if (DIO.isSaftyDoorR() == false)
{
msg.AppendLine("Rear door is open");
}
if (msg.Length > 0)
{
PUB.log.AddE(msg.ToString());
var response = new { success = false, message = msg.ToString() };
return JsonConvert.SerializeObject(response);
}
PUB.log.Add("User Click : tray out & clear position", false);
PUB.flag.set(eVarBool.FG_USERSTEP, false, "Run_MotionPositionReset");
PUB.log.AddAT("Starting discharge and home movement");
PUB.AddSystemLog(System.Windows.Forms.Application.ProductVersion, "MAIN", "Cancel Work");
PUB.sm.SetNewStep(eSMStep.HOME_QUICK);
var response2 = new { success = true, message = "Job cancelled. Executing motion position reset" };
return JsonConvert.SerializeObject(response2);
}
catch (Exception ex)
{
Console.WriteLine($"[ERROR] Failed to cancel job: {ex.Message}");
var response = new { success = false, message = ex.Message };
return JsonConvert.SerializeObject(response);
}
}
public string OpenManage()
{
try
{
if (PUB.sm.isRunning)
{
var response = new { success = false, message = "Cannot use during operation" };
return JsonConvert.SerializeObject(response);
}
if (PUB.flag.get(AR.eVarBool.FG_MOVE_PICKER))
{
var response = new { success = false, message = "The window is already open" };
return JsonConvert.SerializeObject(response);
}
// Set flag to indicate picker move dialog is open
PUB.flag.set(eVarBool.FG_MOVE_PICKER, true, "PICKERMOVE_WEB");
PUB.log.Add("User Request: Manage (Web UI)", false);
var response2 = new { success = true, message = "Manage dialog opened" };
return JsonConvert.SerializeObject(response2);
}
catch (Exception ex)
{
Console.WriteLine($"[ERROR] Failed to open manage: {ex.Message}");
var response = new { success = false, message = ex.Message };
return JsonConvert.SerializeObject(response);
}
}
public string CloseManage()
{
try
{
// Clear the flag
PUB.flag.set(eVarBool.FG_MOVE_PICKER, false, "PICKERMOVE_WEB");
// Check if auto-init is needed: home not set AND picker is in center position
bool shouldAutoInit = PUB.mot.HasHomeSetOff == true && DIO.GetIOInput(eDIName.PICKER_SAFE);
PUB.log.Add($"User Request: Close Manage (Web UI), shouldAutoInit={shouldAutoInit}", false);
var response = new { shouldAutoInit = shouldAutoInit };
return JsonConvert.SerializeObject(response);
}
catch (Exception ex)
{
Console.WriteLine($"[ERROR] Failed to close manage: {ex.Message}");
var response = new { shouldAutoInit = false };
return JsonConvert.SerializeObject(response);
}
}
public string OpenManual()
{
try
{
string file = System.IO.Path.Combine(AR.UTIL.CurrentPath, "Manual", "Manual.pdf");
if (System.IO.File.Exists(file) == false)
{
var response = new { success = false, message = "User manual file does not exist\nFile name: " + file };
return JsonConvert.SerializeObject(response);
}
AR.UTIL.RunExplorer(file);
PUB.log.Add("User Request: Open Manual", false);
var response2 = new { success = true, message = "Manual opened" };
return JsonConvert.SerializeObject(response2);
}
catch (Exception ex)
{
Console.WriteLine($"[ERROR] Failed to open manual: {ex.Message}");
var response = new { success = false, message = ex.Message };
return JsonConvert.SerializeObject(response);
}
}
public string OpenLogViewer()
{
try
{
var exename = AR.UTIL.MakePath("LogView.exe");
if (System.IO.File.Exists(exename) == false)
{
var response = new { success = false, message = $"Log viewer file not found\n{exename}\nPlease contact support" };
return JsonConvert.SerializeObject(response);
}
AR.UTIL.RunProcess(exename);
PUB.log.Add("User Request: Open Log Viewer", false);
var response2 = new { success = true, message = "Log viewer opened" };
return JsonConvert.SerializeObject(response2);
}
catch (Exception ex)
{
Console.WriteLine($"[ERROR] Failed to open log viewer: {ex.Message}");
var response = new { success = false, message = ex.Message };
return JsonConvert.SerializeObject(response);
}
}
public string OpenProgramFolder()
{
try
{
AR.UTIL.RunExplorer(AR.UTIL.CurrentPath);
PUB.log.Add("User Request: Open Program Folder", false);
var response = new { success = true, message = "Program folder opened" };
return JsonConvert.SerializeObject(response);
}
catch (Exception ex)
{
Console.WriteLine($"[ERROR] Failed to open program folder: {ex.Message}");
var response = new { success = false, message = ex.Message };
return JsonConvert.SerializeObject(response);
}
}
public string OpenLogFolder()
{
try
{
PUB.LogFlush();
var fi = new System.IO.FileInfo(PUB.log.FileName);
AR.UTIL.RunExplorer(fi.Directory.FullName);
PUB.log.Add("User Request: Open Log Folder", false);
var response = new { success = true, message = "Log folder opened" };
return JsonConvert.SerializeObject(response);
}
catch (Exception ex)
{
Console.WriteLine($"[ERROR] Failed to open log folder: {ex.Message}");
var response = new { success = false, message = ex.Message };
return JsonConvert.SerializeObject(response);
}
}
public string OpenScreenshotFolder()
{
try
{
string savefile = System.IO.Path.Combine(AR.UTIL.CurrentPath, "ScreenShot", DateTime.Now.ToString("yyyyMMddHHmmss") + ".png");
var grpath = new System.IO.FileInfo(savefile);
AR.UTIL.RunExplorer(grpath.Directory.FullName);
PUB.log.Add("User Request: Open Screenshot Folder", false);
var response = new { success = true, message = "Screenshot folder opened" };
return JsonConvert.SerializeObject(response);
}
catch (Exception ex)
{
Console.WriteLine($"[ERROR] Failed to open screenshot folder: {ex.Message}");
var response = new { success = false, message = ex.Message };
return JsonConvert.SerializeObject(response);
}
}
public string OpenSavedDataFolder()
{
try
{
var basepath = SETTING.Data.GetDataPath();
var path = System.IO.Path.Combine(
basepath, "History",
DateTime.Now.Year.ToString("0000"),
DateTime.Now.Month.ToString("00"),
DateTime.Now.Day.ToString("00"));
if (System.IO.Directory.Exists(path))
AR.UTIL.RunExplorer(path);
else
AR.UTIL.RunExplorer(System.IO.Path.Combine(SETTING.Data.GetDataPath(), "History"));
PUB.log.Add("User Request: Open Saved Data Folder", false);
var response = new { success = true, message = "Saved data folder opened" };
return JsonConvert.SerializeObject(response);
}
catch (Exception ex)
{
Console.WriteLine($"[ERROR] Failed to open saved data folder: {ex.Message}");
var response = new { success = false, message = ex.Message };
return JsonConvert.SerializeObject(response);
}
}
// ===== PICKER MOVE METHODS =====
private static bool _manPosL = false;
private static bool _manPosR = false;
public string GetPickerStatus()
{
try
{
// X축 피커 이동 가능 여부 체크
bool xEnabled = false;
if (PUB.mot.IsHomeSet((int)eAxis.PZ_PICK) == false)
{
if (PUB.mot.IsOrg((int)eAxis.PZ_PICK) || PUB.mot.IsLimitN((int)eAxis.PZ_PICK))
xEnabled = true;
}
else
{
var PosZ = MOT.GetPZPos(ePZLoc.READY);
var OffZ = MOT.getPositionOffset(PosZ);
xEnabled = OffZ < 1;
}
var doorsafef = DIO.isSaftyDoorF();
var managementEnabled = PUB.mot.HasHomeSetOff == false && doorsafef == true;
var status = new
{
xEnabled = xEnabled,
zEnabled = xEnabled, // Z jog uses same condition
pickerSafe = DIO.GetIOInput(eDIName.PICKER_SAFE),
managementEnabled = managementEnabled,
manPosL = _manPosL,
manPosR = _manPosR
};
return JsonConvert.SerializeObject(status);
}
catch (Exception ex)
{
Console.WriteLine($"[ERROR] Failed to get picker status: {ex.Message}");
return JsonConvert.SerializeObject(new { xEnabled = false, zEnabled = false, pickerSafe = false, managementEnabled = false, manPosL = false, manPosR = false });
}
}
private bool CheckPickerSafety(out string errorMessage)
{
errorMessage = null;
if (DIO.isSaftyDoorF() == false)
{
errorMessage = "Front door is open";
return false;
}
if (PUB.mot.HasHomeSetOff)
{
errorMessage = "Motion home operation is not completed";
return false;
}
return true;
}
private bool CheckZAxisReady(out string errorMessage)
{
errorMessage = null;
var z = MOT.GetPZPos(ePZLoc.READY);
var zpos = MOT.getPositionOffset(z);
if (zpos >= 0.5)
{
errorMessage = "Raise the Z axis and try again";
return false;
}
return true;
}
public string PickerMoveLeft()
{
try
{
if (!CheckPickerSafety(out string safetyError))
return JsonConvert.SerializeObject(new { success = false, message = safetyError });
if (!CheckZAxisReady(out string zError))
return JsonConvert.SerializeObject(new { success = false, message = zError });
var m1 = MOT.GetLMPos(eLMLoc.READY);
if (MOT.getPositionMatch(m1) == false)
return JsonConvert.SerializeObject(new { success = false, message = "Printer attachment is not in ready position.\nCannot move as collision may occur" });
var p1 = MOT.GetPXPos(ePXLoc.PICKOFFL);
MOT.Move(eAxis.PX_PICK, p1.Position, 250, p1.Acc, false, false, false);
return JsonConvert.SerializeObject(new { success = true, message = "Moving to left position" });
}
catch (Exception ex)
{
return JsonConvert.SerializeObject(new { success = false, message = ex.Message });
}
}
public string PickerMoveLeftWait()
{
try
{
if (!CheckPickerSafety(out string safetyError))
return JsonConvert.SerializeObject(new { success = false, message = safetyError });
if (!CheckZAxisReady(out string zError))
return JsonConvert.SerializeObject(new { success = false, message = zError });
var m1 = MOT.GetLMPos(eLMLoc.READY);
if (MOT.getPositionMatch(m1) == false)
return JsonConvert.SerializeObject(new { success = false, message = "Printer attachment is not in ready position.\nCannot move as collision may occur" });
var p1 = MOT.GetPXPos(ePXLoc.READYL);
MOT.Move(eAxis.PX_PICK, p1.Position, 250, p1.Acc, false, false, false);
return JsonConvert.SerializeObject(new { success = true, message = "Moving to left wait position" });
}
catch (Exception ex)
{
return JsonConvert.SerializeObject(new { success = false, message = ex.Message });
}
}
public string PickerMoveCenter()
{
try
{
if (!CheckPickerSafety(out string safetyError))
return JsonConvert.SerializeObject(new { success = false, message = safetyError });
if (!CheckZAxisReady(out string zError))
return JsonConvert.SerializeObject(new { success = false, message = zError });
var p1 = MOT.GetPXPos(ePXLoc.PICKON);
MOT.Move(eAxis.PX_PICK, p1.Position, 250, p1.Acc, false, false, false);
return JsonConvert.SerializeObject(new { success = true, message = "Moving to center position" });
}
catch (Exception ex)
{
return JsonConvert.SerializeObject(new { success = false, message = ex.Message });
}
}
public string PickerMoveRightWait()
{
try
{
if (!CheckPickerSafety(out string safetyError))
return JsonConvert.SerializeObject(new { success = false, message = safetyError });
if (!CheckZAxisReady(out string zError))
return JsonConvert.SerializeObject(new { success = false, message = zError });
var m1 = MOT.GetRMPos(eRMLoc.READY);
if (MOT.getPositionMatch(m1) == false)
return JsonConvert.SerializeObject(new { success = false, message = "Printer attachment is not in ready position.\nCannot move as collision may occur" });
var p1 = MOT.GetPXPos(ePXLoc.READYR);
MOT.Move(eAxis.PX_PICK, p1.Position, 250, p1.Acc, false, false, false);
return JsonConvert.SerializeObject(new { success = true, message = "Moving to right wait position" });
}
catch (Exception ex)
{
return JsonConvert.SerializeObject(new { success = false, message = ex.Message });
}
}
public string PickerMoveRight()
{
try
{
if (!CheckPickerSafety(out string safetyError))
return JsonConvert.SerializeObject(new { success = false, message = safetyError });
if (!CheckZAxisReady(out string zError))
return JsonConvert.SerializeObject(new { success = false, message = zError });
var m1 = MOT.GetRMPos(eRMLoc.READY);
if (MOT.getPositionMatch(m1) == false)
return JsonConvert.SerializeObject(new { success = false, message = "Printer attachment is not in ready position.\nCannot move as collision may occur" });
var p1 = MOT.GetPXPos(ePXLoc.PICKOFFR);
MOT.Move(eAxis.PX_PICK, p1.Position, 250, p1.Acc, false, false, false);
return JsonConvert.SerializeObject(new { success = true, message = "Moving to right position" });
}
catch (Exception ex)
{
return JsonConvert.SerializeObject(new { success = false, message = ex.Message });
}
}
public string PickerJogStart(string direction)
{
try
{
switch (direction)
{
case "up":
PUB.mot.JOG((int)eAxis.PZ_PICK, arDev.MOT.MOTION_DIRECTION.Negative, AR.SETTING.Data.JOG_Speed, AR.SETTING.Data.JOG_Acc);
break;
case "down":
PUB.mot.JOG((int)eAxis.PZ_PICK, arDev.MOT.MOTION_DIRECTION.Positive, AR.SETTING.Data.JOG_Speed, AR.SETTING.Data.JOG_Acc);
break;
case "left":
PUB.mot.JOG((int)eAxis.PX_PICK, arDev.MOT.MOTION_DIRECTION.Negative, AR.SETTING.Data.JOG_Speed, AR.SETTING.Data.JOG_Acc);
break;
case "right":
PUB.mot.JOG((int)eAxis.PX_PICK, arDev.MOT.MOTION_DIRECTION.Positive, AR.SETTING.Data.JOG_Speed, AR.SETTING.Data.JOG_Acc);
break;
}
return JsonConvert.SerializeObject(new { success = true, message = $"Jog {direction} started" });
}
catch (Exception ex)
{
return JsonConvert.SerializeObject(new { success = false, message = ex.Message });
}
}
public string PickerJogStop()
{
try
{
PUB.mot.MoveStop("pmove_web", (short)eAxis.PX_PICK);
PUB.mot.MoveStop("pmove_web", (short)eAxis.PZ_PICK);
return JsonConvert.SerializeObject(new { success = true, message = "Jog stopped" });
}
catch (Exception ex)
{
return JsonConvert.SerializeObject(new { success = false, message = ex.Message });
}
}
public string PickerStop()
{
try
{
PUB.mot.MoveStop("pmove_web", (int)eAxis.PX_PICK);
return JsonConvert.SerializeObject(new { success = true, message = "Picker stopped" });
}
catch (Exception ex)
{
return JsonConvert.SerializeObject(new { success = false, message = ex.Message });
}
}
public string CancelVisionValidation(string side)
{
try
{
if (side == "left")
{
PUB.flag.set(eVarBool.FG_PRC_VISIONL, false, "CANCEL_WEB");
PUB.flag.set(eVarBool.FG_PORTL_ITEMON, false, "CANCEL_WEB");
PUB.log.Add($"LEFT-QR verification cancelled JGUID={PUB.Result.ItemDataL.guid}");
}
else
{
PUB.flag.set(eVarBool.FG_PRC_VISIONR, false, "CANCEL_WEB");
PUB.flag.set(eVarBool.FG_PORTR_ITEMON, false, "CANCEL_WEB");
PUB.log.Add($"RIGHT-QR verification cancelled JGUID={PUB.Result.ItemDataR.guid}");
}
return JsonConvert.SerializeObject(new { success = true, message = $"{side.ToUpper()}-QR verification cancelled" });
}
catch (Exception ex)
{
return JsonConvert.SerializeObject(new { success = false, message = ex.Message });
}
}
public string PickerManagePosition(string side)
{
try
{
if (PUB.sm.Step != eSMStep.IDLE)
return JsonConvert.SerializeObject(new { success = false, message = "Available only in standby state" });
var Xpos = DIO.GetIOInput(eDIName.PICKER_SAFE);
if (Xpos == false)
return JsonConvert.SerializeObject(new { success = false, message = "Available only when picker is in center position" });
int vidx = side == "left" ? 0 : 2;
Task.Run(() =>
{
DateTime dt;
if (vidx == 0)
{
while (DIO.GetIOInput(eDIName.L_CYLUP) == false)
{
var dorlt = DIO.checkDigitalO(eDOName.L_CYLDN, new TimeSpan(1), false);
if (dorlt == eNormalResult.False)
System.Threading.Thread.Sleep(100);
else if (dorlt == eNormalResult.Error)
{
PUB.log.AddE("l_cylup check error");
return;
}
}
var zPos = MOT.GetLZPos(eLZLoc.READY).Clone();
zPos.Speed = 100;
MOT.Move(zPos);
dt = DateTime.Now;
while (MOT.getPositionMatch(zPos) == false)
{
if ((DateTime.Now - dt).TotalSeconds > 30) break;
System.Threading.Thread.Sleep(10);
}
var mPos = MOT.GetLMPos(eLMLoc.PRINTL07).Clone();
mPos.Speed = 100;
MOT.Move(mPos);
dt = DateTime.Now;
while (MOT.getPositionMatch(mPos) == false)
{
if ((DateTime.Now - dt).TotalSeconds > 30) break;
System.Threading.Thread.Sleep(10);
}
var zPos2 = MOT.GetLZPos(eLZLoc.PICKOFF);
var tPos = (zPos2.Position / 2f);
MOT.Move(eAxis.PL_UPDN, tPos, 100, zPos.Acc);
_manPosL = true;
}
else
{
while (DIO.GetIOInput(eDIName.R_CYLUP) == false)
{
var dorlt = DIO.checkDigitalO(eDOName.R_CYLDN, new TimeSpan(1), false);
if (dorlt == eNormalResult.False)
System.Threading.Thread.Sleep(100);
else if (dorlt == eNormalResult.Error)
{
PUB.log.AddE("r_cylup check error");
return;
}
}
var zPos = MOT.GetRZPos(eRZLoc.READY).Clone();
zPos.Speed = 100;
MOT.Move(zPos);
dt = DateTime.Now;
while (MOT.getPositionMatch(zPos) == false)
{
if ((DateTime.Now - dt).TotalSeconds > 30) break;
System.Threading.Thread.Sleep(10);
}
var mPos = MOT.GetRMPos(eRMLoc.PRINTL07).Clone();
mPos.Speed = 100;
MOT.Move(mPos);
dt = DateTime.Now;
while (MOT.getPositionMatch(mPos) == false)
{
if ((DateTime.Now - dt).TotalSeconds > 30) break;
System.Threading.Thread.Sleep(10);
}
var zPos2 = MOT.GetRZPos(eRZLoc.PICKOFF);
var tPos = (zPos2.Position / 2f);
MOT.Move(eAxis.PR_UPDN, tPos, 100, zPos.Acc);
_manPosR = true;
}
});
return JsonConvert.SerializeObject(new { success = true, message = $"Moving to {side} management position" });
}
catch (Exception ex)
{
return JsonConvert.SerializeObject(new { success = false, message = ex.Message });
}
}
public string PickerManageReturn()
{
try
{
var Xpos = DIO.GetIOInput(eDIName.PICKER_SAFE);
if (Xpos == false)
return JsonConvert.SerializeObject(new { success = false, message = "Available only when picker is in center position" });
// Recover both positions
Task.Run(() => PosRecover(0));
Task.Run(() => PosRecover(2));
return JsonConvert.SerializeObject(new { success = true, message = "Returning to ready position" });
}
catch (Exception ex)
{
return JsonConvert.SerializeObject(new { success = false, message = ex.Message });
}
}
private void PosRecover(int vidx)
{
DateTime dt;
if (vidx == 0)
{
while (DIO.GetIOInput(eDIName.L_CYLUP) == false)
{
var dorlt = DIO.checkDigitalO(eDOName.L_CYLDN, new TimeSpan(1), false);
if (dorlt == eNormalResult.False)
System.Threading.Thread.Sleep(100);
else if (dorlt == eNormalResult.Error)
{
PUB.log.AddE("l_cylup check error");
return;
}
}
var zPos = MOT.GetLZPos(eLZLoc.READY).Clone();
zPos.Speed = 100;
MOT.Move(zPos);
dt = DateTime.Now;
while (MOT.getPositionMatch(zPos) == false)
{
if ((DateTime.Now - dt).TotalSeconds > 30) break;
System.Threading.Thread.Sleep(10);
}
var mPos = MOT.GetLMPos(eLMLoc.READY).Clone();
mPos.Speed = 100;
MOT.Move(mPos);
dt = DateTime.Now;
while (MOT.getPositionMatch(mPos) == false)
{
if ((DateTime.Now - dt).TotalSeconds > 30) break;
System.Threading.Thread.Sleep(10);
}
_manPosL = false;
}
else
{
while (DIO.GetIOInput(eDIName.R_CYLUP) == false)
{
var dorlt = DIO.checkDigitalO(eDOName.R_CYLDN, new TimeSpan(1), false);
if (dorlt == eNormalResult.False)
System.Threading.Thread.Sleep(100);
else if (dorlt == eNormalResult.Error)
{
PUB.log.AddE("R_cylup check error");
return;
}
}
var zPos = MOT.GetRZPos(eRZLoc.READY).Clone();
zPos.Speed = 100;
MOT.Move(zPos);
dt = DateTime.Now;
while (MOT.getPositionMatch(zPos) == false)
{
if ((DateTime.Now - dt).TotalSeconds > 30) break;
System.Threading.Thread.Sleep(10);
}
var mPos = MOT.GetRMPos(eRMLoc.READY).Clone();
mPos.Speed = 100;
MOT.Move(mPos);
dt = DateTime.Now;
while (MOT.getPositionMatch(mPos) == false)
{
if ((DateTime.Now - dt).TotalSeconds > 30) break;
System.Threading.Thread.Sleep(10);
}
_manPosR = false;
}
}
public string PickerZHome()
{
try
{
MOT.Home("Management_Web", eAxis.PZ_PICK, false);
return JsonConvert.SerializeObject(new { success = true, message = "Z-axis home search started" });
}
catch (Exception ex)
{
return JsonConvert.SerializeObject(new { success = false, message = ex.Message });
}
}
public string PickerZZero()
{
try
{
if (PUB.mot.IsHomeSet((int)eAxis.PZ_PICK) == false)
return JsonConvert.SerializeObject(new { success = false, message = "Z home operation is not completed. Please perform HOME first" });
MOT.Move(eAxis.PZ_PICK, 0, 500, 1000, false, false, false);
return JsonConvert.SerializeObject(new { success = true, message = "Moving Z-axis to zero" });
}
catch (Exception ex)
{
return JsonConvert.SerializeObject(new { success = false, message = ex.Message });
}
}
public string PickerTestPrint(string side)
{
try
{
if (side == "left")
{
PUB.PrinterL.TestPrint(AR.SETTING.Data.DrawOutbox, "", "");
PUB.log.Add("Temporary print L:" + PUB.PrinterL.LastPrintZPL);
}
else
{
PUB.PrinterR.TestPrint(AR.SETTING.Data.DrawOutbox, "", "");
PUB.log.Add("Temporary print R:" + PUB.PrinterR.LastPrintZPL);
}
return JsonConvert.SerializeObject(new { success = true, message = $"{side.ToUpper()} test print completed" });
}
catch (Exception ex)
{
return JsonConvert.SerializeObject(new { success = false, message = ex.Message });
}
}
public string CanCloseManage()
{
try
{
if (_manPosL || _manPosR)
{
return JsonConvert.SerializeObject(new { canClose = false, message = "Printer motion is in management position.\nReturn to position and try again" });
}
return JsonConvert.SerializeObject(new { canClose = true, message = "" });
}
catch (Exception ex)
{
return JsonConvert.SerializeObject(new { canClose = false, message = ex.Message });
}
}
// ===== INTERLOCK METHODS =====
/// <summary>
/// 인터락 토글 (DIOMonitor.cs의 gvILXF_ItemClick과 동일)
/// </summary>
public string ToggleInterlock(int axisIndex, int lockIndex)
{
try
{
if (axisIndex < 0 || axisIndex >= PUB.iLock.Length)
{
return JsonConvert.SerializeObject(new { success = false, message = "Invalid axis index" });
}
var curValue = PUB.iLock[axisIndex].get(lockIndex);
PUB.iLock[axisIndex].set(lockIndex, !curValue, "IOMONITOR_WEB");
PUB.log.Add($"[Web] Interlock toggle: axis={axisIndex}, lock={lockIndex}, newVal={!curValue}");
return JsonConvert.SerializeObject(new { success = true, newState = !curValue });
}
catch (Exception ex)
{
Console.WriteLine($"[ERROR] Failed to toggle interlock: {ex.Message}");
return JsonConvert.SerializeObject(new { success = false, message = ex.Message });
}
}
// ===== HISTORY DATA =====
/// <summary>
/// 작업 이력 데이터 조회 (fHistory.cs의 refreshList와 동일)
/// </summary>
public string GetHistoryData(string startDate, string endDate, string search)
{
try
{
var results = new List<object>();
using (var ta = new DataSet1TableAdapters.K4EE_Component_Reel_ResultTableAdapter())
{
var ds = new DataSet1();
// 검색어 처리
var searchPattern = string.IsNullOrEmpty(search) ? "%" : "%" + search + "%";
// DB에서 조회
ta.FillBySearch(ds.K4EE_Component_Reel_Result, AR.SETTING.Data.McName, startDate, endDate, searchPattern);
foreach (DataSet1.K4EE_Component_Reel_ResultRow row in ds.K4EE_Component_Reel_Result.Rows)
{
results.Add(new
{
idx = row.idx,
jtype = row.IsJTYPENull() ? "" : row.JTYPE,
rid = row.IsRIDNull() ? "" : row.RID,
sid = row.IsSIDNull() ? "" : row.SID,
qty = row.IsQTYNull() ? 0 : row.QTY,
vname = row.IsVNAMENull() ? "" : row.VNAME,
vlot = row.IsVLOTNull() ? "" : row.VLOT,
loc = row.IsLOCNull() ? "" : row.LOC,
qr = row.IsQRNull() ? "" : row.QR,
stime = row.STIME.ToString("yyyy-MM-dd HH:mm:ss"),
etime = row.IsETIMENull() ? "" : row.ETIME.ToString("yyyy-MM-dd HH:mm:ss"),
prnattach = !row.IsPRNATTACHNull() && row.PRNATTACH,
prnvalid = !row.IsPRNVALIDNull() && row.PRNVALID,
rid0 = row.IsRID0Null() ? "" : row.RID0,
sid0 = row.IsSID0Null() ? "" : row.SID0,
qtymax = row.IsqtymaxNull() ? 0 : row.qtymax
});
}
}
return JsonConvert.SerializeObject(new {
success = true,
data = results,
mcName = AR.SETTING.Data.McName
});
}
catch (Exception ex)
{
Console.WriteLine($"[ERROR] Failed to get history data: {ex.Message}");
return JsonConvert.SerializeObject(new { success = false, message = ex.Message, data = new List<object>() });
}
}
// ===== HARDWARE STATUS METHODS =====
/// <summary>
/// H/W 상태 조회 (_Interval_250ms.cs의 HWState 업데이트 로직과 동일)
/// </summary>
public string GetHWStatus()
{
try
{
var hwList = new List<object>();
// 1. KeyenceF (BCD Front) - 바코드 리더
if (PUB.keyenceF != null)
{
hwList.Add(new
{
name = "BCD-F",
title = PUB.keyenceF.IsConnect ? (PUB.keyenceF.IsTriggerOn ? "TRIG" : "ON") : "OFF",
status = PUB.keyenceF.IsConnect ? (PUB.keyenceF.IsTriggerOn ? 2 : 1) : 3
});
}
else
{
hwList.Add(new { name = "BCD-F", title = "SET", status = 0 });
}
// 2. KeyenceR (BCD Rear) - 바코드 리더
if (PUB.keyenceR != null)
{
hwList.Add(new
{
name = "BCD-R",
title = PUB.keyenceR.IsConnect ? (PUB.keyenceR.IsTriggerOn ? "TRIG" : "ON") : "OFF",
status = PUB.keyenceR.IsConnect ? (PUB.keyenceR.IsTriggerOn ? 2 : 1) : 3
});
}
else
{
hwList.Add(new { name = "BCD-R", title = "SET", status = 0 });
}
// 3. Vision WebSocket Left
if (PUB.wsL != null)
{
hwList.Add(new
{
name = "VIS-L",
title = PUB.wsL.Connected ? "ON" : "OFF",
status = PUB.wsL.Connected ? 1 : 3
});
}
else
{
hwList.Add(new { name = "VIS-L", title = "SET", status = 0 });
}
// 4. Vision WebSocket Right
if (PUB.wsR != null)
{
hwList.Add(new
{
name = "VIS-R",
title = PUB.wsR.Connected ? "ON" : "OFF",
status = PUB.wsR.Connected ? 1 : 3
});
}
else
{
hwList.Add(new { name = "VIS-R", title = "SET", status = 0 });
}
// 5. BarcodeFix (Fixed Barcode Reader)
hwList.Add(new
{
name = "FIX",
title = PUB.BarcodeFix.IsOpen() ? AR.SETTING.Data.Barcode_Port : "OFF",
status = PUB.BarcodeFix.IsOpen() ? 1 : 3
});
// 6. PrinterL
if (PUB.PrinterL != null)
{
hwList.Add(new
{
name = "PRT-L",
title = PUB.PrinterL.IsOpen ? AR.SETTING.Data.PrintL_Port : "OFF",
status = PUB.PrinterL.IsOpen ? 1 : 3
});
}
else
{
hwList.Add(new { name = "PRT-L", title = "SET", status = 0 });
}
// 7. PrinterR
if (PUB.PrinterR != null)
{
hwList.Add(new
{
name = "PRT-R",
title = PUB.PrinterR.IsOpen ? AR.SETTING.Data.PrintR_Port : "OFF",
status = PUB.PrinterR.IsOpen ? 1 : 3
});
}
else
{
hwList.Add(new { name = "PRT-R", title = "SET", status = 0 });
}
// 8. PLC
if (PUB.plc != null)
{
hwList.Add(new
{
name = "PLC",
title = PUB.plc.Init ? AR.SETTING.Data.swplc_name : "OFF",
status = PUB.plc.Init ? 1 : 3
});
}
else
{
hwList.Add(new { name = "PLC", title = "SET", status = 0 });
}
// 9. DIO (Digital I/O)
hwList.Add(new
{
name = "DIO",
title = PUB.dio?.IsInit == true ? "ON" : "OFF",
status = PUB.dio?.IsInit == true ? 1 : 3
});
// 10. Motion
hwList.Add(new
{
name = "MOT",
title = PUB.mot?.IsInit == true ? "ON" : "OFF",
status = PUB.mot?.IsInit == true ? 1 : 3
});
return JsonConvert.SerializeObject(hwList);
}
catch (Exception ex)
{
Console.WriteLine($"[ERROR] Failed to get HW status: {ex.Message}");
return JsonConvert.SerializeObject(new List<object>());
}
}
/// <summary>
/// 실시간 인식 데이터 조회 (listView21과 동일한 데이터)
/// Left/Center/Right 포트의 VisionData 정보
/// </summary>
public string GetVisionData()
{
try
{
var visdataL = PUB.Result.ItemDataL?.VisionData;
var visdataC = PUB.Result.ItemDataC?.VisionData;
var visdataR = PUB.Result.ItemDataR?.VisionData;
var result = new
{
left = new
{
cartSize = DIO.getCartSize(0).ToString(),
enabled = PUB.flag.get(eVarBool.FG_ENABLE_LEFT),
rid = visdataL?.RID ?? "",
sid = visdataL?.SID ?? "",
qty = visdataL?.QTY ?? "",
vname = visdataL?.VNAME ?? "",
vlot = visdataL?.VLOT ?? "",
mfgdate = visdataL?.MFGDATE ?? "",
partno = visdataL?.PARTNO ?? "",
reelSize = visdataL?.ReelSize.ToString() ?? "--",
batch = visdataL?.BATCH ?? "",
rid2 = visdataL?.RID2 ?? "",
sid2 = visdataL?.SID2 ?? "",
qty2 = visdataL?.QTY2 ?? "",
vname2 = visdataL?.VNAME2 ?? "",
vlot2 = visdataL?.VLOT2 ?? "",
mfgdate2 = visdataL?.MFGDATE2 ?? "",
partno2 = visdataL?.PARTNO2 ?? ""
},
center = new
{
cartSize = DIO.getCartSize(1).ToString(),
confirm = visdataC?.Confirm ?? false,
rid = visdataC?.RID ?? "",
rid_trust = visdataC?.RID_Trust ?? false,
rid_new = visdataC?.RIDNew ?? false,
sid = visdataC?.SID ?? "",
sid0 = visdataC?.SID0 ?? "",
sid_trust = visdataC?.SID_Trust ?? false,
qty = visdataC?.QTY ?? "",
qty_trust = visdataC?.QTY_Trust ?? false,
qty_rq = visdataC?.QTYRQ ?? false,
vname = visdataC?.VNAME ?? "",
vname_trust = visdataC?.VNAME_Trust ?? false,
vlot = visdataC?.VLOT ?? "",
vlot_trust = visdataC?.VLOT_Trust ?? false,
mfgdate = visdataC?.MFGDATE ?? "",
mfgdate_trust = visdataC?.MFGDATE_Trust ?? false,
partno = visdataC?.PARTNO ?? "",
partno_trust = visdataC?.PARTNO_Trust ?? false,
reelSize = visdataC?.ReelSize.ToString() ?? "--",
batch = visdataC?.BATCH ?? "",
qtymax = visdataC?.QTYMAX ?? "",
barcodeCount = visdataC?.barcodelist?.Count ?? 0,
regexCount = PUB.Result.BCDPattern?.Count ?? 0
},
right = new
{
cartSize = DIO.getCartSize(2).ToString(),
enabled = PUB.flag.get(eVarBool.FG_ENABLE_RIGHT),
rid = visdataR?.RID ?? "",
sid = visdataR?.SID ?? "",
qty = visdataR?.QTY ?? "",
vname = visdataR?.VNAME ?? "",
vlot = visdataR?.VLOT ?? "",
mfgdate = visdataR?.MFGDATE ?? "",
partno = visdataR?.PARTNO ?? "",
reelSize = visdataR?.ReelSize.ToString() ?? "--",
batch = visdataR?.BATCH ?? "",
rid2 = visdataR?.RID2 ?? "",
sid2 = visdataR?.SID2 ?? "",
qty2 = visdataR?.QTY2 ?? "",
vname2 = visdataR?.VNAME2 ?? "",
vlot2 = visdataR?.VLOT2 ?? "",
mfgdate2 = visdataR?.MFGDATE2 ?? "",
partno2 = visdataR?.PARTNO2 ?? ""
}
};
return JsonConvert.SerializeObject(result);
}
catch (Exception ex)
{
Console.WriteLine($"[ERROR] Failed to get vision data: {ex.Message}");
return JsonConvert.SerializeObject(new { left = new { }, center = new { }, right = new { } });
}
}
/// <summary>
/// 인터락 목록 조회 (실시간 값 포함)
/// </summary>
public string GetInterlockList()
{
try
{
var interlocks = new List<object>();
for (int i = 0; i < PUB.iLock.Length; i++)
{
var axisName = ((AR.eAxis)i).ToString();
var nonAxis = false;
if (i >= 7)
{
axisName = PUB.iLock[i].Tag.ToString();
nonAxis = true;
}
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));
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()
});
}
return JsonConvert.SerializeObject(interlocks);
}
catch (Exception ex)
{
Console.WriteLine($"[ERROR] Failed to get interlock list: {ex.Message}");
return JsonConvert.SerializeObject(new List<object>());
}
}
}
}