245 lines
8.0 KiB
C#
245 lines
8.0 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Drawing;
|
|
using System.IO;
|
|
using System.Text;
|
|
using System.Windows.Forms;
|
|
|
|
namespace AGVSimulator.Forms
|
|
{
|
|
|
|
/// <summary>
|
|
/// 경로 예측 테스트 진행 상황 로그 표시 폼
|
|
/// </summary>
|
|
public partial class ProgressLogForm : Form
|
|
{
|
|
private List<PathTestLogItem> _logItems;
|
|
|
|
/// <summary>
|
|
/// 취소 요청 여부
|
|
/// </summary>
|
|
public bool CancelRequested { get; private set; }
|
|
|
|
public ProgressLogForm()
|
|
{
|
|
InitializeComponent();
|
|
CancelRequested = false;
|
|
_logItems = new List<PathTestLogItem>();
|
|
}
|
|
|
|
/// <summary>
|
|
/// 로그 추가 (PathTestLogItem)
|
|
/// </summary>
|
|
public void AddLogItem(PathTestLogItem item)
|
|
{
|
|
if (InvokeRequired)
|
|
{
|
|
Invoke(new Action<PathTestLogItem>(AddLogItem), item);
|
|
return;
|
|
}
|
|
|
|
_logItems.Add(item);
|
|
|
|
var listItem = new ListViewItem(item.PreviousPosition ?? "-");
|
|
listItem.SubItems.Add(item.MotorDirection ?? "-");
|
|
listItem.SubItems.Add(item.CurrentPosition ?? "-");
|
|
listItem.SubItems.Add(item.TargetPosition ?? "-");
|
|
listItem.SubItems.Add(item.DockingPosition ?? "-");
|
|
listItem.SubItems.Add(item.Success ? "O" : "X");
|
|
listItem.SubItems.Add(item.Message ?? "-");
|
|
listItem.SubItems.Add(item.DetailedPath ?? "-");
|
|
listItem.SubItems.Add(item.Timestamp.ToString("HH:mm:ss"));
|
|
|
|
// 성공 여부에 따라 색상 설정
|
|
if (!item.Success)
|
|
{
|
|
listItem.BackColor = Color.LightPink;
|
|
}
|
|
|
|
var dockpos = item.DockingPosition ?? string.Empty;
|
|
var targerpos = item.TargetPosition ?? string.Empty;
|
|
if (dockpos.Equals("충전기") && targerpos.StartsWith("0015"))
|
|
listItem.ForeColor = Color.DarkViolet;
|
|
|
|
_logListView.Items.Add(listItem);
|
|
_logListView.EnsureVisible(_logListView.Items.Count - 1);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 간단한 텍스트 로그 추가 (상태 메시지용)
|
|
/// </summary>
|
|
public void AppendLog(string message)
|
|
{
|
|
var item = new PathTestLogItem
|
|
{
|
|
Message = message,
|
|
Success = true
|
|
};
|
|
AddLogItem(item);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 상태 메시지 업데이트
|
|
/// </summary>
|
|
public void UpdateStatus(string status)
|
|
{
|
|
if (InvokeRequired)
|
|
{
|
|
Invoke(new Action<string>(UpdateStatus), status);
|
|
return;
|
|
}
|
|
|
|
_statusLabel.Text = status;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 프로그레스바 업데이트
|
|
/// </summary>
|
|
public void UpdateProgress(int value, int maximum)
|
|
{
|
|
if (InvokeRequired)
|
|
{
|
|
Invoke(new Action<int, int>(UpdateProgress), value, maximum);
|
|
return;
|
|
}
|
|
|
|
_progressBar.Maximum = maximum;
|
|
_progressBar.Value = Math.Min(value, maximum);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 작업 완료 시 호출
|
|
/// </summary>
|
|
public void SetCompleted()
|
|
{
|
|
if (InvokeRequired)
|
|
{
|
|
Invoke(new Action(SetCompleted));
|
|
return;
|
|
}
|
|
|
|
_cancelButton.Enabled = false;
|
|
_closeButton.Enabled = true;
|
|
UpdateStatus("작업 완료");
|
|
}
|
|
|
|
/// <summary>
|
|
/// 작업 취소 시 호출
|
|
/// </summary>
|
|
public void SetCancelled()
|
|
{
|
|
if (InvokeRequired)
|
|
{
|
|
Invoke(new Action(SetCancelled));
|
|
return;
|
|
}
|
|
|
|
_cancelButton.Enabled = false;
|
|
_closeButton.Enabled = true;
|
|
UpdateStatus("작업 취소됨");
|
|
}
|
|
|
|
private void OnCancel_Click(object sender, EventArgs e)
|
|
{
|
|
var result = MessageBox.Show(
|
|
"진행 중인 작업을 취소하시겠습니까?",
|
|
"취소 확인",
|
|
MessageBoxButtons.YesNo,
|
|
MessageBoxIcon.Question);
|
|
|
|
if (result == DialogResult.Yes)
|
|
{
|
|
CancelRequested = true;
|
|
_cancelButton.Enabled = false;
|
|
UpdateStatus("취소 요청됨...");
|
|
AppendLog("사용자가 취소를 요청했습니다.");
|
|
}
|
|
}
|
|
|
|
private void OnSaveCSV_Click(object sender, EventArgs e)
|
|
{
|
|
if (_logItems.Count == 0)
|
|
{
|
|
MessageBox.Show("저장할 데이터가 없습니다.", "알림", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
|
return;
|
|
}
|
|
|
|
using (var saveDialog = new SaveFileDialog())
|
|
{
|
|
saveDialog.Filter = "CSV 파일 (*.csv)|*.csv|모든 파일 (*.*)|*.*";
|
|
saveDialog.DefaultExt = "csv";
|
|
saveDialog.FileName = $"경로예측테스트_{DateTime.Now:yyyyMMdd_HHmmss}.csv";
|
|
|
|
if (saveDialog.ShowDialog() == DialogResult.OK)
|
|
{
|
|
try
|
|
{
|
|
SaveToCSV(saveDialog.FileName);
|
|
MessageBox.Show($"CSV 파일이 저장되었습니다.\n{saveDialog.FileName}",
|
|
"저장 완료", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
|
|
|
var prc = new System.Diagnostics.Process();
|
|
prc.StartInfo = new System.Diagnostics.ProcessStartInfo("explorer", saveDialog.FileName);
|
|
prc.Start();
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
MessageBox.Show($"CSV 저장 중 오류가 발생했습니다:\n{ex.Message}",
|
|
"오류", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// CSV 파일로 저장
|
|
/// </summary>
|
|
private void SaveToCSV(string filePath)
|
|
{
|
|
using (var writer = new StreamWriter(filePath, false, Encoding.UTF8))
|
|
{
|
|
// 헤더 작성
|
|
writer.WriteLine("이전위치,모터방향,현재위치,대상위치,도킹위치,성공,메세지,상세경로,시간");
|
|
|
|
// 데이터 작성
|
|
foreach (var item in _logItems)
|
|
{
|
|
var line = $"{EscapeCSV(item.PreviousPosition)}," +
|
|
$"{EscapeCSV(item.MotorDirection)}," +
|
|
$"{EscapeCSV(item.CurrentPosition)}," +
|
|
$"{EscapeCSV(item.TargetPosition)}," +
|
|
$"{EscapeCSV(item.DockingPosition)}," +
|
|
$"{(item.Success ? "O" : "X")}," +
|
|
$"{EscapeCSV(item.Message)}," +
|
|
$"{EscapeCSV(item.DetailedPath)}," +
|
|
$"{item.Timestamp:yyyy-MM-dd HH:mm:ss}";
|
|
|
|
writer.WriteLine(line);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// CSV 셀 데이터 이스케이프 처리
|
|
/// </summary>
|
|
private string EscapeCSV(string value)
|
|
{
|
|
if (string.IsNullOrEmpty(value))
|
|
return "";
|
|
|
|
// 쉼표, 큰따옴표, 줄바꿈이 있으면 큰따옴표로 감싸고 내부 큰따옴표는 두 개로
|
|
if (value.Contains(",") || value.Contains("\"") || value.Contains("\n") || value.Contains("\r"))
|
|
{
|
|
return "\"" + value.Replace("\"", "\"\"") + "\"";
|
|
}
|
|
|
|
return value;
|
|
}
|
|
|
|
private void OnClose_Click(object sender, EventArgs e)
|
|
{
|
|
this.Close();
|
|
}
|
|
}
|
|
}
|