Merge branch 'master' of https://git.tindevil.com/Amkor/ENIG
This commit is contained in:
@@ -119,6 +119,9 @@ namespace AGVNavigationCore.Controls
|
||||
var worldPoint = ScreenToWorld(e.Location);
|
||||
var hitNode = GetItemAt(worldPoint);
|
||||
|
||||
// 가동 모드에서는 더블클릭 편집 방지
|
||||
if (_canvasMode == CanvasMode.Run) return;
|
||||
|
||||
if (hitNode == null) return;
|
||||
|
||||
if (hitNode.Type == NodeType.Normal)
|
||||
|
||||
@@ -39,7 +39,8 @@ namespace AGVNavigationCore.Controls
|
||||
{
|
||||
Edit, // 편집 가능 (맵 에디터)
|
||||
Sync, // 동기화 모드 (장비 설정 동기화)
|
||||
Emulator // 에뮬레이터 모드
|
||||
Emulator, // 에뮬레이터 모드
|
||||
Run // 가동 모드 (User Request)
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -207,7 +207,7 @@ namespace Project
|
||||
//연결은 되었으나 통신이 지난지 10초가 지났다면 자동종료한다
|
||||
var tsRecv = VAR.TIME.RUN(recvtime);
|
||||
var tsConn = VAR.TIME.RUN(conntry);
|
||||
if (tsRecv.TotalSeconds > 10 && tsConn.TotalSeconds > 5)
|
||||
if (tsRecv.TotalSeconds > 30 && tsConn.TotalSeconds > 5)
|
||||
{
|
||||
this.BeginInvoke(new Action(() =>
|
||||
{
|
||||
|
||||
@@ -43,11 +43,19 @@ namespace Project.ViewForm
|
||||
|
||||
private void OnNodeSelected(object sender, NodeBase node, MouseEventArgs e)
|
||||
{
|
||||
if (e.Button != MouseButtons.Right) return;
|
||||
if (node == null) return;
|
||||
var mapnode = node as MapNode;
|
||||
if (mapnode == null) return;
|
||||
|
||||
// [Run Mode] Left Click: AGV Operation
|
||||
if (PUB._mapCanvas.Mode == AGVNavigationCore.Controls.UnifiedAGVCanvas.CanvasMode.Run && e.Button == MouseButtons.Left)
|
||||
{
|
||||
HandleRunModeClick(mapnode);
|
||||
return;
|
||||
}
|
||||
|
||||
if (e.Button != MouseButtons.Right) return;
|
||||
|
||||
// 도킹 가능한 노드인지 또는 작업 노드인지 확인
|
||||
if (mapnode.isDockingNode == false) return;
|
||||
|
||||
@@ -158,6 +166,9 @@ namespace Project.ViewForm
|
||||
//}
|
||||
|
||||
this.timer1.Start();
|
||||
|
||||
// Set Run Mode
|
||||
PUB._mapCanvas.Mode = AGVNavigationCore.Controls.UnifiedAGVCanvas.CanvasMode.Run;
|
||||
}
|
||||
private void AGV_DataReceive(object sender, arDev.Narumi.DataEventArgs e)
|
||||
{
|
||||
@@ -194,6 +205,9 @@ namespace Project.ViewForm
|
||||
PUB.sm.StepChanged -= Sm_StepChanged;
|
||||
this.ctlAuto1.ButtonClick -= CtlAuto1_ButtonClick;
|
||||
PUB.AGV.DataReceive -= AGV_DataReceive;
|
||||
|
||||
// Reset Mode to Edit
|
||||
PUB._mapCanvas.Mode = AGVNavigationCore.Controls.UnifiedAGVCanvas.CanvasMode.Edit;
|
||||
}
|
||||
|
||||
bool tmrun = false;
|
||||
@@ -201,7 +215,15 @@ namespace Project.ViewForm
|
||||
private void fAuto_VisibleChanged(object sender, EventArgs e)
|
||||
{
|
||||
this.timer1.Enabled = this.Visible;
|
||||
if (timer1.Enabled) timer1.Start();
|
||||
if (timer1.Enabled)
|
||||
{
|
||||
timer1.Start();
|
||||
// 화면이 보일 때 Run 모드로 강제 설정
|
||||
if (PUB._mapCanvas.Mode != AGVNavigationCore.Controls.UnifiedAGVCanvas.CanvasMode.Run)
|
||||
{
|
||||
PUB._mapCanvas.Mode = AGVNavigationCore.Controls.UnifiedAGVCanvas.CanvasMode.Run;
|
||||
}
|
||||
}
|
||||
else timer1.Stop();
|
||||
}
|
||||
|
||||
@@ -236,5 +258,49 @@ namespace Project.ViewForm
|
||||
|
||||
//tmrun = false;
|
||||
}
|
||||
|
||||
private void HandleRunModeClick(MapNode targetNode)
|
||||
{
|
||||
if (targetNode == null) return;
|
||||
|
||||
ENIGProtocol.AGVCommandHE targetCmd = ENIGProtocol.AGVCommandHE.Goto;
|
||||
string confirmMsg = "";
|
||||
|
||||
if (targetNode.StationType == StationType.Charger)
|
||||
{
|
||||
if (MessageBox.Show($"[{targetNode.Id}] 충전기로 이동하여 충전을 진행하시겠습니까?", "작업 확인", MessageBoxButtons.YesNo) == DialogResult.Yes)
|
||||
{
|
||||
targetCmd = ENIGProtocol.AGVCommandHE.Charger;
|
||||
ExecuteManualCommand(targetNode, targetCmd);
|
||||
}
|
||||
return;
|
||||
}
|
||||
else if (targetNode.isDockingNode)
|
||||
{
|
||||
// Loader, Unloader, Buffer, Cleaner - Pick/Drop Selection
|
||||
ContextMenuStrip menu = new ContextMenuStrip();
|
||||
|
||||
var pickOn = new ToolStripMenuItem("Pick On (Move & Pick)");
|
||||
pickOn.Click += (s, args) => ExecuteManualCommand(targetNode, ENIGProtocol.AGVCommandHE.PickOn);
|
||||
menu.Items.Add(pickOn);
|
||||
|
||||
var pickOff = new ToolStripMenuItem("Pick Off (Move & Drop)");
|
||||
pickOff.Click += (s, args) => ExecuteManualCommand(targetNode, ENIGProtocol.AGVCommandHE.PickOff);
|
||||
menu.Items.Add(pickOff);
|
||||
|
||||
menu.Show(Cursor.Position);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Normal Node
|
||||
if (MessageBox.Show($"[{targetNode.Id}] 노드로 이동하시겠습니까?", "이동 확인", MessageBoxButtons.YesNo) == DialogResult.Yes)
|
||||
{
|
||||
targetCmd = ENIGProtocol.AGVCommandHE.Goto;
|
||||
ExecuteManualCommand(targetNode, targetCmd);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -229,7 +229,7 @@ namespace arDev
|
||||
private void RevSTS(Dataframe frame)
|
||||
{
|
||||
LastSTS = frame.DataString;
|
||||
string rcvdNow = frame.DataString;
|
||||
string rcvdNow = frame.DataString.Replace("\0","");
|
||||
byte[] bRcvData = frame.Buffer;
|
||||
var encoding = System.Text.Encoding.Default;
|
||||
try
|
||||
@@ -267,8 +267,12 @@ namespace arDev
|
||||
nDataTemp = Convert.ToByte(rcvdNow.Substring(idx, 2), 16);
|
||||
signal1.SetValue(nDataTemp); idx += 2;
|
||||
|
||||
nDataTemp = Convert.ToByte(rcvdNow.Substring(idx, 2), 16);
|
||||
signal2.SetValue(nDataTemp);
|
||||
if(idx <= rcvdNow.Length-2)
|
||||
{
|
||||
nDataTemp = Convert.ToByte(rcvdNow.Substring(idx, 2), 16);
|
||||
signal2.SetValue(nDataTemp);
|
||||
}
|
||||
|
||||
|
||||
DataReceive?.Invoke(this, new DataEventArgs(DataType.STS));
|
||||
|
||||
|
||||
@@ -51,6 +51,12 @@ namespace arDev
|
||||
public string WriteErrorMessage = string.Empty;
|
||||
public int WaitTimeout { get; set; } = 1000;
|
||||
public int MinRecvLength { get; set; } = 1;
|
||||
|
||||
// Polling Thread related
|
||||
protected Thread _recvThread;
|
||||
protected volatile bool _isReading = false;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 포트이름
|
||||
/// </summary>
|
||||
@@ -103,7 +109,7 @@ namespace arDev
|
||||
_device = new System.IO.Ports.SerialPort();
|
||||
this.BaudRate = 9600;
|
||||
ScanInterval = 10;
|
||||
_device.DataReceived += barcode_DataReceived;
|
||||
// _device.DataReceived += barcode_DataReceived; // Removed event handler
|
||||
_device.ErrorReceived += this.barcode_ErrorReceived;
|
||||
_device.WriteTimeout = 3000;
|
||||
_device.ReadTimeout = 3000;
|
||||
@@ -147,9 +153,23 @@ namespace arDev
|
||||
//
|
||||
}
|
||||
|
||||
_device.DataReceived -= barcode_DataReceived;
|
||||
// Stop reading thread
|
||||
_isReading = false;
|
||||
|
||||
// _device.DataReceived -= barcode_DataReceived; // Removed event handler
|
||||
_device.ErrorReceived -= this.barcode_ErrorReceived;
|
||||
|
||||
if (_recvThread != null && _recvThread.IsAlive)
|
||||
{
|
||||
_recvThread.Join(500);
|
||||
}
|
||||
|
||||
if (_device != null)
|
||||
{
|
||||
if (_device.IsOpen) _device.Close();
|
||||
_device.Dispose();
|
||||
}
|
||||
|
||||
// Free any unmanaged objects here.
|
||||
//
|
||||
disposed = true;
|
||||
@@ -159,8 +179,24 @@ namespace arDev
|
||||
{
|
||||
try
|
||||
{
|
||||
_device.Open();
|
||||
return IsOpen;
|
||||
if (_device.IsOpen == false)
|
||||
{
|
||||
_device.Open();
|
||||
}
|
||||
|
||||
if (_device.IsOpen)
|
||||
{
|
||||
// Start polling thread
|
||||
if (_isReading == false)
|
||||
{
|
||||
_isReading = true;
|
||||
_recvThread = new Thread(ReadPort);
|
||||
_recvThread.IsBackground = true;
|
||||
_recvThread.Start();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -193,14 +229,31 @@ namespace arDev
|
||||
|
||||
public virtual bool Close()
|
||||
{
|
||||
if (_device != null && _device.IsOpen)
|
||||
try
|
||||
{
|
||||
_device.DiscardInBuffer();
|
||||
_device.DiscardOutBuffer();
|
||||
_device.Close(); //dispose에서는 포트를 직접 클리어하지 않게 해뒀다.
|
||||
return true;
|
||||
_isReading = false; // Stop thread loop
|
||||
|
||||
if (_recvThread != null && _recvThread.IsAlive)
|
||||
{
|
||||
if (!_recvThread.Join(500)) // Wait for thread to finish
|
||||
{
|
||||
// _recvThread.Abort(); // Avoid Abort if possible
|
||||
}
|
||||
}
|
||||
|
||||
if (_device != null && _device.IsOpen)
|
||||
{
|
||||
_device.DiscardInBuffer();
|
||||
_device.DiscardOutBuffer();
|
||||
_device.Close(); //dispose에서는 포트를 직접 클리어하지 않게 해뒀다.
|
||||
return true;
|
||||
}
|
||||
else return false;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else return false;
|
||||
}
|
||||
protected Boolean RaiseRecvData()
|
||||
{
|
||||
@@ -226,6 +279,9 @@ namespace arDev
|
||||
|
||||
try
|
||||
{
|
||||
// UI update might need Invoke if this event handler updates UI directly,
|
||||
// but usually the subscriber handles Invoke.
|
||||
// Since we are running on a background thread now, subscribers must be aware.
|
||||
Message?.Invoke(this, new MessageEventArgs(Data, true)); //recvmessage
|
||||
if (ProcessRecvData(Data) == false)
|
||||
{
|
||||
@@ -262,6 +318,9 @@ namespace arDev
|
||||
}
|
||||
|
||||
byte[] buffer = new byte[] { };
|
||||
|
||||
// Replaced with ReadPort Loop
|
||||
/*
|
||||
void barcode_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
|
||||
{
|
||||
|
||||
@@ -308,6 +367,62 @@ namespace arDev
|
||||
}
|
||||
|
||||
}
|
||||
*/
|
||||
|
||||
void ReadPort()
|
||||
{
|
||||
while (_isReading)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (_device == null || !_device.IsOpen)
|
||||
{
|
||||
Thread.Sleep(100);
|
||||
continue;
|
||||
}
|
||||
|
||||
int readCount = _device.BytesToRead;
|
||||
if (readCount > 0)
|
||||
{
|
||||
byte[] buffer = new byte[readCount];
|
||||
_device.Read(buffer, 0, buffer.Length);
|
||||
|
||||
byte[] remainBuffer;
|
||||
Repeat:
|
||||
if (CustomParser(buffer, out remainBuffer))
|
||||
{
|
||||
//분석완료이므로 받은 데이터를 버퍼에 기록한다
|
||||
if (LastReceiveBuffer == null || (LastReceiveBuffer.Length != tempBuffer.Count))
|
||||
Array.Resize(ref LastReceiveBuffer, tempBuffer.Count);
|
||||
Array.Copy(tempBuffer.ToArray(), LastReceiveBuffer, tempBuffer.Count);
|
||||
tempBuffer.Clear();
|
||||
|
||||
//수신메세지발생
|
||||
RaiseRecvData();
|
||||
if (remainBuffer != null && remainBuffer.Length > 0)
|
||||
{
|
||||
//버퍼를 변경해서 다시 전송을 해준다.
|
||||
buffer = new byte[remainBuffer.Length]; // Reallocate buffer for remaining data
|
||||
Array.Copy(remainBuffer, buffer, remainBuffer.Length);
|
||||
goto Repeat; //남은 버퍼가 있다면 진행을 해준다.
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Thread.Sleep(20); // Data 없음, 대기
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// Thread 상에서 Exception 발생 시 로그 남기고 계속 진행 여부 결정
|
||||
// 여기서는 에러 메시지 발생시키고 Sleep
|
||||
ErrorMessage = ex.Message;
|
||||
this.Message?.Invoke(this, new MessageEventArgs(ex.Message, true));
|
||||
Thread.Sleep(1000);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
Submodule Cs_HMI/SubProject/CommUtil updated: b070b711f0...ed05439991
Binary file not shown.
Binary file not shown.
Reference in New Issue
Block a user