| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553 |
- using System;
- using System.Collections.Generic;
- using System.IO;
- using System.Linq;
- using System.Text;
- using System.Threading;
- using System.Threading.Tasks;
- using GSG.NET.Concurrent;
- using GSG.NET.LINQ;
- using GSG.NET.Logging;
- using GSG.NET.Quartz;
- using GSG.NET.TCP;
- using GSG.NET.Utils;
- using GSG.NET.Extensions;
- namespace OHVConnector
- {
- public partial class Manager
- {
- const byte STX = 0x02;
- const byte ETX = 0x03;
- const long INIT_CTRL_SYSBYTE = 0x40000000;
- long sysbyte = 0;
- long ctrl_sysbyte = INIT_CTRL_SYSBYTE;
- static Logger logger = Logger.GetLogger();
- TsQueue<QueueObject> qQ = new TsQueue<QueueObject>();
- TsQueue<OCSMessage> qqW = new TsQueue<OCSMessage>();
- TimerTemplate<byte, OCSMessage> quzT3 = new TimerTemplate<byte, OCSMessage>();
- TsMap<long, SyncObject> ddReq = new TsMap<long, SyncObject>(); //sync 통신을 위함.
- TcpConnector h = new TcpConnector();
- Thread _TQ;//pumping queue thread
- Thread _TW;//write
- Thread _TR;//read
- Thread _TLINK;//Linktest
- bool ModeActive { get; set; }
- bool? inited;
- public Config Config { get; set; }
- #region Properties
- public bool Connected
- {
- get { return h.Connected; }
- }
- /// <summary>
- /// 연결 시도 가능여부 체크
- /// <para>Connect 호출 or 연결중: true</para>
- /// <para>최초 or Disconnect 호출: false</para>
- /// </summary>
- public bool Connecting
- {
- get { return inited.HasValue ? inited.Value : false; }
- }
- #endregion
- #region Constactor
- public Manager()
- {
- ModeActive = true;
- Config = new Config();
- quzT3.OnTimeout += _OnTimeout;
- h.OnTcpStateChanged += _OnLog;
- }
- #endregion
- #region Connection Method
- public void Connect(bool active = false)
- {
- ModeActive = active;
- if (!inited.HasValue)//최초 한번 실행.
- {
- _TQ = ThreadUtils.Invoke(_ThPullQueue);
- inited = false;
- }
- Assert.IsFalse(inited.Value, "Already connecting");
- inited = true;
- _TW = ThreadUtils.Invoke(_ThWriteTcp);
- _TR = ThreadUtils.Invoke(_ThReadTcp);
- _TLINK = ThreadUtils.Invoke(_ThLinkQuz);
- _OnLog("OHV CONNECT REQ " + Config);
- }
- public void Disconnect()
- {
- //if (inited.HasValue && inited.Value)
- if (Connecting)
- {
- _OnLog("HSMS DISCONNECT REQ " + Config);
- inited = false;
- quzT3.StopAll();
- ThreadUtils.Kill(_TLINK);
- ThreadUtils.Kill(_TW);
- h.StopListen();//20170720 연결 시도중(passive listening)에 disconnect blocking patch.
- ThreadUtils.Kill(_TR);
- //h.StopListen(); 20170720
- h.CloseSocket();//Kill을 사용할 경우 뒤에 존재해야 한다.
- }
- }
- void _OnDicontd(Exception e)
- {
- _OnLog("OHV DISCONNECTED");
- sysbyte = 0;
- ctrl_sysbyte = INIT_CTRL_SYSBYTE;
- quzT3.StopAll();
- ddReq.Clear();
- qQ.Enqueue(new QoNotComm { Arg0 = e });
- }
- void _OnContd()
- {
- _OnLog("OHV CONNECTED");
- qQ.Enqueue(new QoComm());
- }
- void TcpConnect()
- {
- h.Connect(new TcpComm
- {
- Active = ModeActive,
- RetryCnt = 1, //T5를 처리해야 함.
- Ip = Config.IpAddress,
- PortNo = Config.Port,
- T5 = Config.T5,
- T6 = Config.T6,//Config.TcpRecdTimeout,
- });
- if (!h.Connected)
- {
- if (ModeActive)
- _OnLog("T5 TIMEOUT " + Config.ID);
- return;
- }
- _OnContd();
- ChgTcpTimeout(true);
- //if (ModeActive)
- //SendCtrlMsg(1);//무조건 HSMS Active
- }
- #endregion
- private void _OnLog(string obj)
- {
- qQ.Enqueue(new QoLog { Arg0 = obj });
- }
- private void _OnTimeout(byte id, OCSMessage msg)
- {
- if (null == msg)
- {
- logger.W("T3 [{0}] attachment is null", id);
- return;
- }
- qQ.Enqueue(new QoTimeout { Arg0 = msg });
- //_OnLog("T3 TIMEOUT {0}".format(msg.LogHeader));
- }
- #region Thread Method
- readonly object lockLink = new object();
- void _ThLinkQuz()
- {
- for (; ; )
- {
- try
- {
- bool waked;
- if (Config.LinkOn)
- waked = LockUtils.Wait(Config.TLink, lockLink);
- else
- waked = LockUtils.Wait(lockLink);
- if (waked)
- continue;//notify: 패킷을 수신할때마다 reset함.
- if (Connected)//연결여부와 상관없이 thread가 기동되므로 연결시에만.
- {
- //SendCtrlMsg(5);
- }
- }
- catch (ThreadAbortException)
- {
- break;
- }
- catch (Exception e)
- {
- logger.E(e);
- }
- }
- }
- void _ThWriteTcp()
- {
- logger.I("Write {0}", ThreadUtils.GetCurrThreadID());
- for (; ; )
- {
- try
- {
- var v = qqW.Dequeue();
- this.TcpWriteMsg(v);
- //v.IsRecd = false;
- //if (v.AfterMillis > 0)
- // LockUtils.Wait(v.AfterMillis);
- //if (v.CtrlMsg)
- // TcpWriteCtrlMsg(v);
- //else
- // TcpWriteNormalMsg(v);
- }
- catch (ThreadAbortException)
- {
- break;
- }
- catch (Exception e)
- {
- logger.E(e);
- }
- }
- }
- void _ThReadTcp()
- {
- for (; ; )
- {
- try
- {
- if (!h.Connected)
- {
- TcpConnect();
- continue;
- }
- ReadSocket();
- }
- catch (ObjectDisposedException e)
- {
- TcpError(e);
- }
- catch (IOException e)
- {
- TcpError(e);
- }
- catch (ThreadAbortException)
- {
- _OnLog("DISCONNECT REQUEST APPLIED " + Config);
- TcpError(new IOException("DISCONNECT REQUEST"));
- break;
- }
- catch (Exception e)
- {
- logger.E(e);
- }
- }
- }
- void _ThPullQueue()
- {
- for (; ; )
- {
- try
- {
- var qo = this.qQ.Dequeue();
- if (qo is QoRecdUnk)
- {
- DelegateUtils.Invoke(OnRecdUnk, qo.Arg0, qo.Arg1);
- //if (AutoS9Fy)
- //{
- // var v = qo.Arg0 as SFMessage;
- // Send(v.S9Fy);
- //}
- }
- else if (qo is QoComm)
- DelegateUtils.Invoke(OnContd, Config.ID);
- else if (qo is QoNotComm)
- DelegateUtils.Invoke(OnDiscontd, Config.ID, qo.Arg0);
- else if (qo is QoLog)
- DelegateUtils.Invoke(OnLog, Config.ID, qo.Arg0);
- else if (qo is QoRecd)
- DelegateUtils.Invoke(OnRecd, qo.Arg0);
- else if (qo is QoTimeout)
- {
- DelegateUtils.Invoke(OnT3Timeout, qo.Arg0);
- //if (AutoS9Fy)
- //{
- // var v = qo.Arg0 as SFMessage;
- // Send(MessageSupport.MakeS9FX(9, v));
- //}
- }
- else if (qo is QoSent)
- DelegateUtils.Invoke(OnSent, qo.Arg0);
- else
- Assert.Fail("Unk Object {0}", qo);
- }
- catch (ThreadAbortException)
- {
- break;
- }
- catch (Exception e)
- {
- logger.E(e);
- }
- }
- }
- #endregion
- #region Read Method
- void ReadSocket()
- {
- h.ReadByte(); //STX
- string revID = string.Empty;
- string sendID = string.Empty;
- if (ModeActive)
- {
- revID = h.ReadAscii(2);
- sendID = h.ReadAscii(5);
- }
- else
- {
- revID = h.ReadAscii(5);
- sendID = h.ReadAscii(2);
- }
- if (!this.Config.ID.Equals(revID))
- OnLog(this.Config.ID, $"RevID Not Equals");
- var ocsMeg = new OCSMessage();
- ocsMeg.RevID = revID;
- ocsMeg.SendID = sendID;
- ocsMeg.Kind = h.ReadAscii(1).ToEnum<eKind>(eKind.Unknown);
- ocsMeg.Tag = h.ReadAscii(4);
- ocsMeg.SubCode = h.ReadAscii(3);
- //CheckSum 을 해야 하나??
- ocsMeg.CheckSum = h.ReadByte();
- h.ReadUntil(ETX);
- //Todo: 응답으로 온건지 그냥 보낸건지 분류가 필요. = CheckSum 을 저장 했다가 이용하자.
- //if (!len.FwBtw(10, MAX_SIZE))
- // throw new IOException("HSMS ABNORMAL LENGTH:" + len);
- //var head = h.ReadBytes(10);
- //var body = h.ReadBytes(len - 10);
- ChgTcpTimeout(true);//무언가 받으면
- LockUtils.NotifyAll(lockLink);//Linktest thread 변환의 notify
- //var v = new OCSMessage { Header = head, Body = body, IsRecd = true };
- //v.Decoding();
- _OnRecd(ocsMeg);
- }
- void _OnRecd(OCSMessage recd)
- {
- //Alive Check Reply
- if (recd.Kind == eKind.A && !ModeActive)
- {
- var reply = new OCSMessage()
- {
- Id = this.Config.ID,
- RevID = recd.SendID,
- SendID = this.Config.ID,
- Kind = eKind.A,
- Tag = recd.Tag,
- SubCode = recd.SubCode,
- };
- Reply(reply);
- return;
- }
- //Send 한 Message 의 Reply 로 판단.
- if (this.quzT3.HasId(recd.CheckSum))
- {
- //Send 목록에서 삭제한다.
- this.quzT3.Stop(recd.CheckSum);
- this._OnLog($"[Received] - Reply - {recd.LogFormat()}");
- return;
- }
- this.qQ.Enqueue(new QoRecd { Arg0 = recd });
- }
- void TcpError(Exception e)
- {
- _OnLog(TcpUtils.GetTcpErrMsg(h.IPClient, e));
- h.CloseSocket();
- _OnDicontd(e);
- LockUtils.Wait(1000);//잠시대기.
- }
- #endregion
- #region Write Method
- void TcpWriteMsg(OCSMessage msg)
- {
- qQ.Enqueue(new QoSent { Arg0 = msg });
- this.h.WriteFlush(msg.ToMemoryBuffer().ToBytes);
- }
- void TcpWriteNormalMsg(OCSMessage nm)
- {
- //nm.Encoding();
- //if (nm.IsPrimary && nm.IsWbit)
- // quzT3.StartOnce(Config.T3 * ConstUtils.ONE_SECOND, nm.Systembyte, nm);
- //int len = nm.Header.Length + nm.Body.Length + 10;
- //var mb = new MemoryBuffer(len);
- //mb.AppendBeInt(nm.Length);
- //mb.Append(nm.Header);
- //mb.Append(nm.Body);
- //qQ.Enqueue(new QoSent { Arg0 = nm });
- //h.WriteFlush(mb.ToBytes);
- }
- void TcpWriteCtrlMsg(OCSMessage ctrl)
- {
- //var mb = new MemoryBuffer(16);
- //mb.AppendBeInt(10);
- //mb.Append(ctrl.Header);
- //bool skip = ctrl.CtrlLinkTest && Config.HideLogLink;
- //if (!skip)
- // _OnLog(ctrl.LogFormat());
- //h.WriteFlush(mb.ToBytes);
- }
- void SendCtrlMsg(int stype)
- {
- //Send(new OCSMessage { SType = stype });
- }
- public void Send(OCSMessage msg, int after)
- {
- if (after > 0)
- TimerUtils.Once(after, Send, msg);
- else
- Send(msg);
- }
- public void Send(OCSMessage msg)
- {
- //msg.Id = Config.ID;
- if (!Connected)
- {
- _OnLog("Send fail not connected" + msg.LogFormat());
- return;
- }
- msg.RevID = Config.HostID;
- msg.SendID = Config.ID;
- if (this.quzT3.HasId(msg.GetCheckSum()))
- {
- _OnLog("quzT3 Has ID" + msg.LogFormat());
- return;
- }
- this.quzT3.StartOnce(Config.T3 * ConstUtils.ONE_SECOND, msg.GetCheckSum(), msg);
- //if (msg.CtrlMsg)
- //{
- // if (msg.CtrlSelectReq || msg.CtrlLinkReq)
- // {
- // msg.Systembyte = Interlocked.Increment(ref ctrl_sysbyte);
- // ChgTcpTimeout(false);//select, linktest req
- // }
- //}
- //else
- //{
- // msg.DeviceId = msg.SessID.HasValue ? msg.SessID.Value : Config.DeviceID;
- // if (msg.IsPrimary && msg.NeedSetSysbyte)
- // msg.Systembyte = Interlocked.Increment(ref sysbyte);
- //}
- qqW.Enqueue(msg);
- }
- /// <summary>
- /// 응답을 보낼 때 사용.
- /// </summary>
- /// <param name="msg"></param>
- public void Reply(OCSMessage msg)
- {
- if (!Connected)
- {
- _OnLog("Reply fail not connected" + msg.LogFormat());
- return;
- }
- qqW.Enqueue(msg);
- }
- #endregion
- #region HelpMothed
- void ChgTcpTimeout(bool infinite)
- {
- if (h.Connected)
- {
- if (infinite)
- {
- if (h.Socket.ReceiveTimeout != Timeout.Infinite)
- h.ChangeRecvTimeout(Timeout.Infinite);
- }
- else
- {
- if (Config.LinkOn)
- h.ChangeRecvTimeout(Config.T6 * ConstUtils.ONE_SECOND);
- }
- }
- }
- #endregion
- }
- }
|