This commit is contained in:
chi
2025-04-24 11:51:55 +09:00
parent 0a93a54a6f
commit f71b963851
62 changed files with 1748 additions and 4105 deletions

View File

@@ -6,395 +6,173 @@ using System.ComponentModel;
using System.Threading;
using COMM;
using System.Runtime.Remoting.Messaging;
using ENIG;
using System.Security.Cryptography;
using AR;
using System.Web.Compilation;
namespace Project.Device
{
public class Xbee : arDev.arRS232
public class Xbee : System.IO.Ports.SerialPort
{
public Xbee()
{
}
public string buffer = string.Empty;
public System.Text.StringBuilder newbuffer = new StringBuilder();
protected override bool CustomParser(byte[] buf, out byte[] remainBuffer)
public string errorMessage = string.Empty;
public DateTime LastStatusSendTime = DateTime.Now;
private EEProtocol proto;
public Xbee()
{
//일반 kit 명령은 0x02 kit번호, kit값 0x03
//상태 명령은 0x04 메세지데이터 0x05
this.DataReceived += Xbee_DataReceived;
proto = new EEProtocol();
proto.OnDataReceived += Proto_OnDataReceived;
proto.OnMessage += Proto_OnMessage;
}
~Xbee()
{
this.DataReceived -= Xbee_DataReceived;
proto.OnDataReceived -= Proto_OnDataReceived;
proto.OnMessage -= Proto_OnMessage;
}
Console.WriteLine("custom parse len=" + buf.Length.ToString());
List<byte> sparebuffer = new List<byte>();
Boolean bComplete = false;
for (int i = 0; i < buf.Length; i++)
/// <summary>
/// AGV상태를 Xbee 로 전송한다
/// </summary>
public void SendStatus()
{
/*
Mode[1] : 0=manual, 1=auto
RunSt[1] : 0=stop, 1=run, 2=error
Diection[1] : 0=straight, 1=left, 2=right, 3=markstop
Inposition[1] : 0=off, 1=on : 목적위치에 도달완료 시 설정 이동 이동시 OFF됨
ChargeSt[1] : 0=off, 1=on
CartSt[1] : 0=off, 1=on, 2=unknown
LiftSt[1] : 0=down , 1=up, 2=unknown
LastTag[6] : "000000"
*/
try
{
var incomByte = buf[i];
if (bComplete == true) sparebuffer.Add(incomByte);
byte[] data = new byte[13]; // 총 13바이트 데이터
// Mode
data[0] = (byte)(VAR.BOOL[eVarBool.FLAG_AUTORUN] ? 1 : 0);
// RunSt
if (PUB.AGV.error.Emergency)
data[1] = 2; // error
else if (PUB.AGV.system1.agv_run)
data[1] = 1; // run
else
{
if (findSTX == false)
{
if (incomByte != 0x02)
{
//버리는데이터
}
else
{
findSTX = true;
tempBuffer.Clear();
tempBuffer.Add(incomByte);
}
}
else
{
tempBuffer.Add(incomByte);
data[1] = 0; // stop
if (incomByte == 0x03)
{
//데이터가 맞게 수신됨
LastReceiveBuffer = tempBuffer.ToArray();
bComplete = true;
findSTX = false;
}
else if (tempBuffer.Count > 30)
{
tempBuffer.Clear();
findSTX = false;
}
}
}
}
remainBuffer = sparebuffer.ToArray();
return bComplete;
}
public override bool ProcessRecvData(byte[] data)
{
var kitno = (char)(data[1]);
var kitval = (char)(data[2]);
NewMsgEvent(kitno, kitval);
return true;
}
public void NewMsgEvent(char kitNo, char kitVal)
{
//대상위치 확인
ePosition kitPos = ePosition.NONE;
if (kitNo == '9') kitPos = ePosition.QC;
else if (kitNo == '1') kitPos = ePosition.F1;
else if (kitNo == '2') kitPos = ePosition.F2;
else if (kitNo == '3') kitPos = ePosition.F3;
else if (kitNo == '4') kitPos = ePosition.F4;
else
{
if (kitNo == '0') return;
PUB.log.Add($"비허가 키트번호 무시 번호={kitNo},값={kitVal}");
return;
}
//if (kitNo= 'F')
//{
// PUB.Speak("알 수 없는 키트번호 입니다");
// PUB.logsys.Add($"키트번호별위치확인실패 값={kitNo}");
// return;
//}
//(ePosition)int.Parse(kitNo.ToString());
PUB.logcal.Add(string.Format("Xbee:newMsgEvent:{0},{1}=>{2}", kitNo, kitVal, kitPos));
if (PUB.CheckManualChargeMode() == false)
{
PUB.log.Add($"수동 충전 중이라 콜 기능을 취소 합니다");
return;
}
bool FixButton = kitPos == ePosition.NONE;
if (kitPos == ePosition.NONE)
{
if (kitVal == '1') kitPos = ePosition.QC;
else if (kitVal == '2') kitPos = ePosition.CHARGE;
else if (kitVal == '4') kitPos = ePosition.QC;
//else if (kitVal == '3') kitPos = ePosition.QA;
// Direction
if (PUB.AGV.system1.stop_by_front_detect)
data[2] = 3; // markstop
else if (VAR.BOOL[eVarBool.FLAG_LEFT_RUN])
data[2] = 1; // left
else if (VAR.BOOL[eVarBool.FLAG_RIGHT_RUN])
data[2] = 2; // right
else
{
PUB.log.Add($"AGV버튼 이벤트 실행 값:{kitVal} 취소");
return;
}
kitVal = '1'; //버튼은 무조건 ON상태로 한다
}
data[2] = 0; // straight
//자동상태가 아니라면 오류처리
if (VAR.BOOL[eVarBool.FLAG_AUTORUN] == false || PUB.sm.Step != StateMachine.eSMStep.RUN)
{
PUB.log.AddE("'자동실행' 상태가 아니므로 콜/취소 작업을 할 수 없습니다 콜:" + kitPos.ToString() + ",값=" + kitVal.ToString());
PUB.Speak(Lang.);
return;
}
// Inposition
data[3] = (byte)(PUB.AGV.system1.agv_stop ? 1 : 0);
//충전기를 제외하고 '1'의 값은 호출을 원하는값이다.('0'=취소)
if (kitVal == '1')
{
//qA혹은 Pack 에서 해당 버튼을 눌렀을때의 처리방법
//if (PUB.Result.CurrentPos == ePosition.QA && kitPos == ePosition.QA)
//{
// PUB.logsys.AddE("QA위치에서 QA버튼을 눌렀음 명령 취소함");
// PUB.Speak("현재 위치는 QA 입니다.");
// return;
//}
// ChargeSt
data[4] = (byte)((VAR.BOOL[eVarBool.FLAG_CHARGEONA] || VAR.BOOL[eVarBool.FLAG_CHARGEONM]) ? 1 : 0);
//상/하차 작업중이 아니라면 콜을 받는다
if (PUB.sm.RunStep != StateMachine.ERunStep.GOUP && PUB.sm.RunStep != StateMachine.ERunStep.GODOWN)
{
//call을 받으려면 대상위 (2=QC,3,4,5,6,7)
//콜을 요청하는 곳 (2=QC,3=FVI-1,4,5,6,7==FVI-5)
//1번은 충전기
//콜버튼으로 대상위치를 다시 결정한다.
//대상번호는 콜번호에서 +2을 하면 된다.
//대상번호 PK=1,QA2=2,QA1=3,QC=4
//QC의경우 콜Kit번호는 2이므로, 이동대상은 +2인 4가 되어야 함
//콜번호와 RFID대상번호는 2의 오차가 발생함, 현재 설치된 RFID를 모두 변경할 수 없어서 이렇게 운용함
RaiseMessage(MessageType.Normal, string.Format("CALL확정 Kit={0},대상위치={1}", kitNo, kitPos));
PUB.AGV.data.CallNo = int.Parse(kitNo.ToString());
PUB.AGV.data.CallString = kitNo.ToString();
PUB.Result.CommandKit = kitNo;
PUB.Result.TargetPos = kitPos;
SendToKit(kitNo, '1', (PUB.IsCanCALL() ? '1' : '0')); //콜확정을 클라이언트에 알림
PUB.Speak(kitPos.ToString() + Lang.);
//상태머신을 변경한다 (충전 OFF상태로 간다, 충전상태가 아니면 바로 이동하게됨)
//Pub.sm.ResetRunStep();
//Pub.sm.setNewStep(StateMachine.eSMStep.RUN);
//qa, pack 은 하차 전용이다
PUB.sm.ClearRunStep();
PUB.sm.SetNewRunStep(StateMachine.ERunStep.GOUP);
PUB.sm.SetNewStep(StateMachine.eSMStep.RUN);
}
else if (PUB.sm.RunStep == StateMachine.ERunStep.GODOWN && VAR.BOOL[eVarBool.WAIT_COVER_DOWN] == false && VAR.BOOL[eVarBool.WAIT_COVER_UP] == false)
{
//하차중에 받을 수 있게하자 (단 FVi1 이하일때에는 무시하자) 230710 - 김수곤
if (PUB.Result.TargetPos == ePosition.QC && PUB.Result.CurrentPos < ePosition.F1)
{
PUB.log.Add($"하차중이지만 FVI #1 아래에 있으니 콜을 취소 합니다");
SendToKit(kitNo, '0', '0');
RaiseMessage(MessageType.Normal, $"QC 추가 콜이지만 위치가 #1번 이하이므로 취소 됩니다. Kit={kitNo},현재={PUB.Result.CurrentPos},대상={PUB.Result.TargetPos}");
}
else
{
RaiseMessage(MessageType.Normal, string.Format("CALL확정(추가) Kit={0},대상위치={1}", kitNo, kitPos));
PUB.AGV.data.CallNo = int.Parse(kitNo.ToString());
PUB.AGV.data.CallString = kitNo.ToString();
PUB.Result.CommandKit = kitNo;
PUB.Result.TargetPos = kitPos;
SendToKit(kitNo, '1', (PUB.IsCanCALL() ? '1' : '0')); //콜확정을 클라이언트에 알림
PUB.Speak(kitPos.ToString() + Lang.);
//상태머신을 변경한다 (충전 OFF상태로 간다, 충전상태가 아니면 바로 이동하게됨)
//Pub.sm.ResetRunStep();
//Pub.sm.setNewStep(StateMachine.eSMStep.RUN);
//qa, pack 은 하차 전용이다
PUB.sm.ClearRunStep();
PUB.sm.SetNewRunStep(StateMachine.ERunStep.GOUP);
PUB.sm.SetNewStep(StateMachine.eSMStep.RUN);
}
}
//else if (PUB.sm.RunStep == StateMachine.ERunStep.GODOWN && PUB.Result.CurrentPos == ePosition.QA)
//{
// RaiseMessage(MessageType.Normal, string.Format("CALL확정 Kit={0},대상위치={1}", kitNo, kitPos));
// PUB.Result.CommandKit = kitNo;
// PUB.Result.TargetPos = kitPos;
// SendToKit(kitNo, '1'); //콜확정을 클라이언트에 알림
// PUB.Speak(kitPos.ToString() + "위치로 이동 합니다");
// //상태머신을 변경한다 (충전 OFF상태로 간다, 충전상태가 아니면 바로 이동하게됨)
// // Pub.sm.ResetRunStep();
// // Pub.sm.setNewStep(StateMachine.eSMStep.RUN);
// //qa => pack 은 하차 전용이다
// //QA에서 PACK이동시에는 바로 N극 멈춤 기능을 사용해도도된다.(자재가 있으며, 커버가 내려가 있으므로 느리게 동작해야함)
// PUB.sm.ClearRunStep();
// PUB.sm.SetNewRunStep(StateMachine.ERunStep.GODOWN);
// PUB.sm.SetNewStep(StateMachine.ESMStep.RUN);
// PUB.AGV.AGVMoveStop("xbee_newmsgevent", arDev.Narumi.eStopOpt.MarkStop);// (arDev.Narumi.eAgvCmd.MoveStop,.SetNextStop_Mark(true, "xbee godn,qa,pack");
// PUB.PLC.ZMot(arDev.FakePLC.ZMotDirection.Up);
//}
else if (PUB.Result.TargetPos == kitPos) ////현재 지정된 클라이언트가 또 눌렀다
{
PUB.Result.TargetPos = kitPos;
PUB.AGV.data.CallNo = int.Parse(kitNo.ToString());
PUB.AGV.data.CallString = kitNo.ToString();
//상차 대기 중이라면 커버를 자동 올려준다
//이것은 QC만 가능하다
//QC가 QA혹은 PACK으로 보낼때 사용함
if (kitPos == ePosition.QC &&
VAR.BOOL[eVarBool.FLAG_LIMITLOW] == true)
{
RaiseMessage(MessageType.Normal, "QC위치에서 자동으로 커버를 올립니다");
PUB.PLC.ZMot(arDev.FakePLC.ZMotDirection.Up);
}
else
{
//커버대기상태라면 커버를 자동으로 처리해준다.
if (PUB.sm.Step == StateMachine.eSMStep.RUN && (PUB.sm.RunStep == StateMachine.ERunStep.GOUP || PUB.sm.RunStep == StateMachine.ERunStep.GODOWN))
{
if (VAR.BOOL[eVarBool.WAIT_COVER_DOWN] == true)
{
var ld = PUB.PLC.GetValueI(arDev.FakePLC.DIName.PINI_LIMIT_LD);
var rd = PUB.PLC.GetValueI(arDev.FakePLC.DIName.PINI_LIMIT_RD);
if (ld == false || rd == false)
{
PUB.log.Add("사용자 콜버튼 추가 동작 - Z내림");
PUB.PLC.ZMot(arDev.FakePLC.ZMotDirection.Down);
PUB.Speak(Lang.);
}
}
else if (VAR.BOOL[eVarBool.WAIT_COVER_UP] == true)
{
var lu = PUB.PLC.GetValueI(arDev.FakePLC.DIName.PINI_LIMIT_LU);
var ru = PUB.PLC.GetValueI(arDev.FakePLC.DIName.PINI_LIMIT_RU);
if (lu == false || ru == false)
{
PUB.PLC.ZMot(arDev.FakePLC.ZMotDirection.Up);
PUB.log.Add("사용자 콜버튼 추가 동작 - Z올림");
PUB.Speak(Lang.);
}
}
else RaiseMessage(MessageType.Normal, string.Format("이미 CALL이 확정되었습니다 No={0},Pos={1}", kitNo, kitPos));
}
else RaiseMessage(MessageType.Normal, string.Format("이미 CALL이 확정되었습니다 No={0},Pos={1}", kitNo, kitPos));
}
SendToKit(kitNo, '1', (PUB.IsCanCALL() ? '1' : '0')); //콜확정을 클라이언트에 알림
}
// CartSt
if(PUB.AGV.signal.cart_detect1 && PUB.AGV.signal.cart_detect2)
data[5] = 1; // 센서두개가 모두 감지되는 경우
else if(PUB.AGV.signal.cart_detect1==false && PUB.AGV.signal.cart_detect2==false)
data[5] = 0; // 센서두개가 모두 감지되지 않는 경우
else
{
//다른대상이 이미 선택된 상태이므로 CANCEL 한다.
SendToKit(kitNo, '0', (PUB.IsCanCALL() ? '1' : '0'));
RaiseMessage(MessageType.Normal, string.Format("다른 CALL이 확정된 상태입니다. 요청KIT={2},확정KIT={0},위치={1}", PUB.Result.TargetPos - 2, PUB.Result.TargetPos, kitNo));
}
}
else if (kitVal == '0') //캔슬버튼을 눌렀다
{
//목표번호가 캔슬버튼을 눌렀다
if (PUB.Result.TargetPos == kitPos)
{
//상차가 완료되면 TargetPos 가 변경되어 ,더이상 해당 버튼으로는 취소하지 못한다
PUB.AGV.data.CallNo = 0;
PUB.AGV.data.CallString = "--";
data[5] = 2; // 센서하나만 감지되는 경우
SendToKit(kitNo, '0', (PUB.IsCanCALL() ? '1' : '0'));
RaiseMessage(MessageType.Normal, string.Format("콜 취소 Kit={0}", kitNo));
if (PUB.sm.RunStep != StateMachine.ERunStep.GOHOME)
{
PUB.Result.CommandKit = kitNo;
PUB.Result.TargetPos = ePosition.QC;
PUB.sm.ClearRunStep();
PUB.sm.SetNewRunStep(StateMachine.ERunStep.GOHOME);
PUB.sm.SetNewStep(StateMachine.eSMStep.RUN);
RaiseMessage(MessageType.Normal, string.Format("콜 취소로 인해 홈으로 이동합니다 Kit={0}", kitNo));
}
}
// LiftSt
if(PUB.AGV.signal.lift_up)
data[6] = 1; // 위로 올라가는 경우
else if(PUB.AGV.signal.lift_down)
data[6] = 0; // 아래로 내려가는 경우
else
{
//다른놈이 취소 버튼을 눌렀다. ㅋㅋ
//아무~ 의미 없다.
SendToKit(kitNo, '0', (PUB.IsCanCALL() ? '1' : '0'));
RaiseMessage(MessageType.Normal, string.Format("해당 콜취소는 유효하지 않습니다 요청KIT={0},확정KIT={1}", kitNo, PUB.Result.TargetPos - 2));
}
data[6] = 2; // unknown (기본값)
// LastTag
string lastTag = PUB.AGV.data.TagNo.ToString("000000") ?? "000000";
byte[] tagBytes = Encoding.ASCII.GetBytes(lastTag.PadRight(6, '0'));
Array.Copy(tagBytes, 0, data, 7, 6);
// 데이터 전송
var packet = proto.CreatePacket(PUB.setting.XBE_ID, 9, data);
Send(packet);
LastStatusSendTime = DateTime.Now;
}
else if (kitVal == '2') //BACKWORK시에만 이 값이 전송되게 한다
catch (Exception ex)
{
//QA 이동 명령
//발동조건 1. QC 위치에서 GOUP(대기 혹은 상차완료)상태일때 (일반적인경우)
//발동조건 2. QC 위치에서 IDLE 상태일때 (GOUP상태로 하지 않고 바로 싫어버린경우)
//발동조건 3. 충전중일때
//if (kitPos != ePosition.QC)
//{
// RaiseMessage(MessageType.Error, $"back 버튼은 QC버튼만 사용 가능 합니다");
// return;
//}
//if (kitPos == ePosition.QC && PUB.Result.CurrentPos != ePosition.QC)
//{
// RaiseMessage(MessageType.Error, string.Format("QC의 QA버튼은 홈위치에 있어야 사용이 가능 합니다"));
// return;
//}
////명령송신은 QC로 고정한다
//PUB.Result.CommandKit = '2';
//PUB.Result.TargetPos = ePosition.QA;
//PUB.sm.ClearRunStep();
//PUB.sm.SetNewRunStep(StateMachine.ERunStep.GODOWN);
//PUB.sm.SetNewStep(StateMachine.ESMStep.RUN);
//RaiseMessage(MessageType.Normal, string.Format("후진하차 모드 대상위치={0}", ePosition.QA));
//PUB.logsys.AddI("QC의 QA리모콘 버튼 작업 입니다");
errorMessage = ex.Message;
PUB.logxbee.AddE(errorMessage);
}
}
public void SendToKit(char kitNo, char Value, char enbcall)
/// <summary>
/// 지그비장치에 데이터를 전송합니다
/// </summary>
/// <param name="data"></param>
/// <returns></returns>
public bool Send(byte[] data)
{
List<byte> buffer = new List<byte>
try
{
0x02,
(byte)kitNo,
(byte)Value,
(byte)enbcall,
0x03
};
WriteData(buffer.ToArray());
//RaiseMessage(MessageType.Normal, $"지그비전송 {kitNo}:{Value}:{enbcall}");
}
public string GetStatusString()
{
return DateTime.Now.ToString();
}
public void SendStatus(String StatusString)
{
////IO
char kitno = '0';
char kitval = '1';
if (PUB.Result.TargetPos == ePosition.F1) kitno = '1';
else if (PUB.Result.TargetPos == ePosition.F2) kitno = '2';
else if (PUB.Result.TargetPos == ePosition.F3) kitno = '3';
else if (PUB.Result.TargetPos == ePosition.F4) kitno = '4';
else if (PUB.Result.TargetPos == ePosition.QC) kitno = '9';
else
this.Write(data, 0, data.Length);
return true;
}catch (Exception ex)
{
//충전기 충전하는 것으로한다
if (VAR.BOOL[eVarBool.FLAG_CHARGEONA] == false && VAR.BOOL[eVarBool.FLAG_CHARGEONM] == false)
kitval = '0';
errorMessage = ex.Message;
return false;
}
}
//콜가능여부도 전송한다
var enbcall = PUB.IsCanCALL() ? '1' : '0';
SendToKit(kitno, kitval, enbcall);
public new bool Open()
{
try{
this.Open();
return true;
}catch (Exception ex)
{
errorMessage = ex.Message;
PUB.logxbee.AddE(errorMessage);
return false;
}
}
private void Proto_OnDataReceived(object sender, EEProtocol.DataEventArgs e)
{
var hexstrRaw = e.ReceivedPacket.RawData.HexString();
var hexstr = e.ReceivedPacket.Data.HexString();
var cmd = e.ReceivedPacket.Command.ToString("X2");
var id = e.ReceivedPacket.ID.ToString("X2");
PUB.logxbee.Add("RX", $"{hexstrRaw}\nID:{id},CMD:{cmd},DATA:{hexstr}");
//TODO : 기능 처리필요 (XBee 메세지 데이터처리)
//PUB.CheckManualChargeMode() : 수동충전확인
//VAR.BOOL[eVarBool.FLAG_AUTORUN] : 자동실행
//PUB.Speak("현재 위치는 QA 입니다.") : 음성출력
}
private void Proto_OnMessage(object sender, EEProtocol.MessageEventArgs e)
{
if (e.IsError) PUB.log.AddE(e.Message);
else PUB.log.Add(e.Message);
}
private void Xbee_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
var dev = sender as System.IO.Ports.SerialPort;
var buffer = new byte[dev.BytesToRead];
dev.Read(buffer, 0, buffer.Length);
proto.ProcessReceivedData(buffer);
}
}
}