Manager.cs 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604
  1. using System;
  2. using System.Collections.Generic;
  3. using System.IO;
  4. using System.Linq;
  5. using System.Text;
  6. using System.Threading;
  7. using System.Threading.Tasks;
  8. using GSG.NET.Concurrent;
  9. using GSG.NET.LINQ;
  10. using GSG.NET.Logging;
  11. using GSG.NET.Quartz;
  12. using GSG.NET.TCP;
  13. using GSG.NET.Utils;
  14. using GSG.NET.Extensions;
  15. namespace OHVConnector
  16. {
  17. public partial class Manager
  18. {
  19. const byte STX = 0x02;
  20. const byte ETX = 0x03;
  21. const long INIT_CTRL_SYSBYTE = 0x40000000;
  22. long sysbyte = 0;
  23. long ctrl_sysbyte = INIT_CTRL_SYSBYTE;
  24. static Logger logger = Logger.GetLogger();
  25. TsQueue<QueueObject> qQ = new TsQueue<QueueObject>();
  26. TsQueue<OCSMessage> qqW = new TsQueue<OCSMessage>();
  27. byte[] crcTable = ChksumUtils.Crc8GenTable( 0xD5 );
  28. TimerTemplate<byte, OCSMessage> quzT3 = new TimerTemplate<byte, OCSMessage>();
  29. TsMap<long, SyncObject> ddReq = new TsMap<long, SyncObject>(); //sync 통신을 위함.
  30. TcpConnector h = new TcpConnector();
  31. Thread _TQ; //pumping queue thread
  32. Thread _TW; //write
  33. Thread _TR; //read
  34. Thread _TLINK; //Link test
  35. bool ModeActive { get; set; }
  36. bool? inited;
  37. public Config Config { get; set; }
  38. #region Properties
  39. public bool Connected
  40. {
  41. get { return h.Connected; }
  42. }
  43. /// <summary>
  44. /// 연결 시도 가능여부 체크
  45. /// <para>Connect 호출 or 연결중: true</para>
  46. /// <para>최초 or Disconnect 호출: false</para>
  47. /// </summary>
  48. public bool Connecting
  49. {
  50. get { return inited.HasValue ? inited.Value : false; }
  51. }
  52. #endregion
  53. #region Constactor
  54. public Manager()
  55. {
  56. ModeActive = true;
  57. Config = new Config();
  58. quzT3.OnTimeout += _OnTimeout;
  59. h.OnTcpStateChanged += _OnLog;
  60. }
  61. #endregion
  62. #region Connection Method
  63. public void Connect( bool active = false )
  64. {
  65. ModeActive = active;
  66. if ( !inited.HasValue )//최초 한번 실행.
  67. {
  68. _TQ = ThreadUtils.Invoke( _ThPullQueue );
  69. inited = false;
  70. }
  71. Assert.IsFalse( inited.Value, "Already connecting" );
  72. inited = true;
  73. _TW = ThreadUtils.Invoke( _ThWriteTcp );
  74. _TR = ThreadUtils.Invoke( _ThReadTcp );
  75. _TLINK = ThreadUtils.Invoke( _ThLinkQuz );
  76. _OnLog( "OHV CONNECT REQ " + Config );
  77. }
  78. public void Disconnect()
  79. {
  80. //if (inited.HasValue && inited.Value)
  81. _OnLog( "OCS DISCONNECT REQ " + Config );
  82. inited = false;
  83. quzT3.StopAll();
  84. ThreadUtils.Kill( _TLINK );
  85. ThreadUtils.Kill( _TW );
  86. h.StopListen();
  87. ThreadUtils.Kill( _TR );
  88. h.CloseSocket();//Kill 을 사용할 경우 뒤에 존재해야 한다.
  89. }
  90. void _OnDicontd( Exception e )
  91. {
  92. _OnLog( "OHV DISCONNECTED" );
  93. sysbyte = 0;
  94. ctrl_sysbyte = INIT_CTRL_SYSBYTE;
  95. //quzT3.StopAll(); //2020.08.261. Kang. 재연결 시 다시 Send 를 위해.
  96. ddReq.Clear();
  97. qQ.Enqueue( new QoNotComm { Arg0 = e } );
  98. }
  99. void _OnContd()
  100. {
  101. _OnLog( "OHV CONNECTED" );
  102. qQ.Enqueue( new QoComm() );
  103. }
  104. void TcpConnect()
  105. {
  106. h.Connect( new TcpComm
  107. {
  108. Active = ModeActive,
  109. RetryCnt = 1, //T5를 처리해야 함.
  110. Ip = Config.IpAddress,
  111. PortNo = Config.Port,
  112. T5 = Config.T5,
  113. T6 = Config.T6,//Config.TcpRecdTimeout,
  114. } );
  115. if ( !h.Connected )
  116. {
  117. if ( ModeActive )
  118. _OnLog( "T5 TIMEOUT " + Config.ID );
  119. return;
  120. }
  121. _OnContd();
  122. ChgTcpTimeout( true );
  123. //if (ModeActive)
  124. //SendCtrlMsg(1);//무조건 HSMS Active
  125. }
  126. #endregion
  127. private void _OnLog( string obj )
  128. {
  129. qQ.Enqueue( new QoLog { Arg0 = obj } );
  130. }
  131. private void _OnTimeout( byte crc, OCSMessage msg )
  132. {
  133. if ( null == msg )
  134. {
  135. logger.W( "T3 [{0}] attachment is null", crc );
  136. return;
  137. }
  138. qQ.Enqueue( new QoTimeout { Arg0 = msg } );
  139. _OnLog( $"T3 TIMEOUT {msg.LogFormat()}" );
  140. }
  141. #region Thread Method
  142. readonly object lockLink = new object();
  143. void _ThLinkQuz()
  144. {
  145. for (; ; )
  146. {
  147. try
  148. {
  149. bool waked;
  150. if ( Config.LinkOn )
  151. waked = LockUtils.Wait( Config.TLink, lockLink );
  152. else
  153. waked = LockUtils.Wait( lockLink );
  154. if ( waked )
  155. continue;//notify: 패킷을 수신할때마다 reset 함.
  156. if ( Connected )//연결여부와 상관없이 thread 가 기동되므로 연결시에만.
  157. {
  158. //SendCtrlMsg(5);
  159. }
  160. }
  161. catch ( ThreadAbortException )
  162. {
  163. break;
  164. }
  165. catch ( Exception e )
  166. {
  167. logger.E( e );
  168. }
  169. }
  170. }
  171. void _ThWriteTcp()
  172. {
  173. logger.I( "Write {0}", ThreadUtils.GetCurrThreadID() );
  174. for (; ; )
  175. {
  176. try
  177. {
  178. var v = qqW.Dequeue();
  179. this.TcpWriteMsg( v );
  180. //v.IsRecd = false;
  181. //if (v.AfterMillis > 0)
  182. // LockUtils.Wait(v.AfterMillis);
  183. //if (v.CtrlMsg)
  184. // TcpWriteCtrlMsg(v);
  185. //else
  186. // TcpWriteNormalMsg(v);
  187. }
  188. catch ( ThreadAbortException )
  189. {
  190. break;
  191. }
  192. catch ( Exception e )
  193. {
  194. logger.E( e );
  195. }
  196. }
  197. }
  198. void _ThReadTcp()
  199. {
  200. for (; ; )
  201. {
  202. try
  203. {
  204. if ( !h.Connected )
  205. {
  206. TcpConnect();
  207. continue;
  208. }
  209. ReadSocket();
  210. }
  211. catch ( ObjectDisposedException e )
  212. {
  213. TcpError( e );
  214. }
  215. catch ( IOException e )
  216. {
  217. TcpError( e );
  218. }
  219. catch ( ThreadAbortException )
  220. {
  221. _OnLog( "DISCONNECT REQUEST APPLIED " + Config );
  222. TcpError( new IOException( "DISCONNECT REQUEST" ) );
  223. break;
  224. }
  225. catch ( Exception e )
  226. {
  227. TcpError( e );
  228. logger.E( e );
  229. }
  230. }
  231. }
  232. void _ThPullQueue()
  233. {
  234. for (; ; )
  235. {
  236. try
  237. {
  238. var qo = this.qQ.Dequeue();
  239. if ( qo is QoRecdUnk )
  240. {
  241. DelegateUtils.Invoke( OnRecdUnk, qo.Arg0, qo.Arg1 );
  242. //if (AutoS9Fy)
  243. //{
  244. // var v = qo.Arg0 as SFMessage;
  245. // Send(v.S9Fy);
  246. //}
  247. }
  248. else if ( qo is QoComm )
  249. DelegateUtils.Invoke( OnContd, Config.ID );
  250. else if ( qo is QoNotComm )
  251. DelegateUtils.Invoke( OnDiscontd, Config.ID, qo.Arg0 );
  252. else if ( qo is QoLog )
  253. DelegateUtils.Invoke( OnLog, Config.ID, qo.Arg0 );
  254. else if ( qo is QoRecd )
  255. DelegateUtils.Invoke( OnRecd, qo.Arg0 );
  256. else if ( qo is QoTimeout )
  257. {
  258. DelegateUtils.Invoke( OnT3Timeout, qo.Arg0 );
  259. //if (AutoS9Fy)
  260. //{
  261. // var v = qo.Arg0 as SFMessage;
  262. // Send(MessageSupport.MakeS9FX(9, v));
  263. //}
  264. }
  265. else if ( qo is QoSent )
  266. DelegateUtils.Invoke( OnSent, qo.Arg0 );
  267. else
  268. Assert.Fail( "Unk Object {0}", qo );
  269. }
  270. catch ( ThreadAbortException )
  271. {
  272. break;
  273. }
  274. catch ( Exception e )
  275. {
  276. logger.E( e );
  277. }
  278. }
  279. }
  280. #endregion
  281. #region Read Method
  282. void ReadSocket()
  283. {
  284. byte stx = 0x0;
  285. do
  286. {
  287. stx = h.ReadByte(); //STX
  288. //logger.D( $"[OCS] - {stx}" );
  289. }
  290. while ( stx != STX );
  291. string revID = string.Empty;
  292. string sendID = string.Empty;
  293. if ( ModeActive )
  294. {
  295. revID = h.ReadAscii( 2 );
  296. sendID = h.ReadAscii( 5 );
  297. }
  298. else
  299. {
  300. revID = h.ReadAscii( 5 );
  301. sendID = h.ReadAscii( 2 );
  302. }
  303. if ( !this.Config.ID.Equals( revID ) )
  304. OnLog( this.Config.ID, $"RevID Not Equals" );
  305. var ocsMeg = new OCSMessage();
  306. ocsMeg.RevID = revID;
  307. ocsMeg.SendID = sendID;
  308. ocsMeg.Kind = h.ReadAscii( 1 ).ToEnum<eKind>( eKind.Unknown );
  309. if ( ocsMeg.Kind == eKind.M ) //M Command 가변으로 들어 온다.
  310. {
  311. ocsMeg.Tag = h.ReadAscii( 4 );
  312. ocsMeg.SubCode = h.ReadAscii( 3 );
  313. var viaCount = Convert.ToInt16( h.ReadAscii( 4 ) ); //4byte
  314. for ( int i = 0; i < viaCount; i++ )
  315. {
  316. ocsMeg.ViaRouteList.Add( h.ReadAscii( 4 ) );
  317. }
  318. ocsMeg.CheckSum = h.ReadAscii( 1 );
  319. h.ReadUntil( ETX );
  320. }
  321. else
  322. {
  323. ocsMeg.Tag = h.ReadAscii( 4 );
  324. ocsMeg.SubCode = h.ReadAscii( 3 );
  325. if ( ocsMeg.Kind == eKind.B )
  326. ocsMeg.BatterySOH = h.ReadAscii( 3 );
  327. //CheckSum 을 해야 하나??
  328. ocsMeg.CheckSum = h.ReadAscii( 1 );
  329. h.ReadUntil( ETX );
  330. }
  331. //Todo: 응답으로 온건지 그냥 보낸건지 분류가 필요. = CheckSum 을 저장 했다가 이용하자.
  332. //if (!len.FwBtw(10, MAX_SIZE))
  333. // throw new IOException("HSMS ABNORMAL LENGTH:" + len);
  334. //var head = h.ReadBytes(10);
  335. //var body = h.ReadBytes(len - 10);
  336. ChgTcpTimeout( true );//무언가 받으면
  337. LockUtils.NotifyAll( lockLink );//Linktest thread 변환의 notify
  338. //var v = new OCSMessage { Header = head, Body = body, IsRecd = true };
  339. //v.Decoding();
  340. _OnRecd( ocsMeg );
  341. }
  342. void _OnRecd( OCSMessage recd )
  343. {
  344. //Alive Check Reply
  345. if ( recd.Kind == eKind.A && !ModeActive ) //자동으로 응답을 보낸다. OCS 가 Active 상태
  346. {
  347. var reply = new OCSMessage()
  348. {
  349. Id = this.Config.ID,
  350. RevID = recd.SendID,
  351. SendID = this.Config.ID,
  352. Kind = eKind.A,
  353. Tag = recd.Tag,
  354. SubCode = recd.SubCode,
  355. };
  356. Reply( reply );
  357. return;
  358. }
  359. if ( recd.Kind == eKind.M && !ModeActive ) //M Code 응답은 없음
  360. {
  361. this.qQ.Enqueue( new QoRecd { Arg0 = recd } );
  362. return;
  363. }
  364. //Send 한 Message 의 Reply 로 판단.
  365. var crc = MakeCRC8CheckSum( recd, false );
  366. if ( this.quzT3.HasId( crc ) )
  367. {
  368. //Send 목록에서 삭제한다.
  369. this.quzT3.Stop( crc );
  370. this._OnLog( $"[Reply] - {recd.LogFormat()}" );
  371. return;
  372. }
  373. this.qQ.Enqueue( new QoRecd { Arg0 = recd } );
  374. }
  375. void TcpError( Exception e )
  376. {
  377. _OnLog( TcpUtils.GetTcpErrMsg( h.IPClient, e ) );
  378. h.CloseSocket();
  379. _OnDicontd( e );
  380. var waitTime = ( this.Config.T3 + 2 ) * ConstUtils.ONE_SECOND;
  381. LockUtils.Wait( waitTime );//잠시대기.
  382. }
  383. #endregion
  384. #region Write Method
  385. void TcpWriteMsg( OCSMessage msg )
  386. {
  387. this.h.WriteFlush( msg.ToMemoryBuffer().ToBytes );
  388. qQ.Enqueue( new QoSent { Arg0 = msg } );
  389. }
  390. void TcpWriteNormalMsg( OCSMessage nm )
  391. {
  392. //nm.Encoding();
  393. //if (nm.IsPrimary && nm.IsWbit)
  394. // quzT3.StartOnce(Config.T3 * ConstUtils.ONE_SECOND, nm.Systembyte, nm);
  395. //int len = nm.Header.Length + nm.Body.Length + 10;
  396. //var mb = new MemoryBuffer(len);
  397. //mb.AppendBeInt(nm.Length);
  398. //mb.Append(nm.Header);
  399. //mb.Append(nm.Body);
  400. //qQ.Enqueue(new QoSent { Arg0 = nm });
  401. //h.WriteFlush(mb.ToBytes);
  402. }
  403. void TcpWriteCtrlMsg( OCSMessage ctrl )
  404. {
  405. //var mb = new MemoryBuffer(16);
  406. //mb.AppendBeInt(10);
  407. //mb.Append(ctrl.Header);
  408. //bool skip = ctrl.CtrlLinkTest && Config.HideLogLink;
  409. //if (!skip)
  410. // _OnLog(ctrl.LogFormat());
  411. //h.WriteFlush(mb.ToBytes);
  412. }
  413. void SendCtrlMsg( int stype )
  414. {
  415. //Send(new OCSMessage { SType = stype });
  416. }
  417. public void Send( OCSMessage msg, int after )
  418. {
  419. if ( after > 0 )
  420. TimerUtils.Once( after, Send, msg );
  421. else
  422. Send( msg );
  423. }
  424. public void Send( OCSMessage msg )
  425. {
  426. //msg.Id = Config.ID;
  427. msg.RevID = Config.HostID;
  428. msg.SendID = Config.ID;
  429. if ( !Connected )
  430. {
  431. _OnLog( "Send fail not connected" + msg.LogFormat() );
  432. return;
  433. }
  434. if ( msg.Kind == eKind.C ) // Control Message 는 페어로 응답이 오지 않는다.
  435. {
  436. qqW.Enqueue( msg );
  437. return;
  438. }
  439. #region 2020.08.25. Kang. OCS에서 응답 안하는 것에 대해 Retry 를 위해 추가.
  440. if ( msg.Kind == eKind.L || msg.Kind == eKind.U )
  441. {
  442. var crc = MakeCRC8CheckSum(msg, true);
  443. if (this.quzT3.HasId(crc))
  444. {
  445. _OnLog("quzT3 Has ID" + msg.LogFormat());
  446. return;
  447. }
  448. this.quzT3.StartOnce(Config.T3 * ConstUtils.ONE_SECOND, crc, msg);
  449. }
  450. #endregion
  451. //if (msg.CtrlMsg)
  452. //{
  453. // if (msg.CtrlSelectReq || msg.CtrlLinkReq)
  454. // {
  455. // msg.Systembyte = Interlocked.Increment(ref ctrl_sysbyte);
  456. // ChgTcpTimeout(false);//select, linktest req
  457. // }
  458. //}
  459. //else
  460. //{
  461. // msg.DeviceId = msg.SessID.HasValue ? msg.SessID.Value : Config.DeviceID;
  462. // if (msg.IsPrimary && msg.NeedSetSysbyte)
  463. // msg.Systembyte = Interlocked.Increment(ref sysbyte);
  464. //}
  465. qqW.Enqueue( msg );
  466. }
  467. /// <summary>
  468. /// 응답을 보낼 때 사용.
  469. /// </summary>
  470. /// <param name="msg"></param>
  471. public void Reply( OCSMessage msg )
  472. {
  473. msg.RevID = Config.HostID;
  474. msg.SendID = Config.ID;
  475. if ( !Connected )
  476. {
  477. _OnLog( "Reply fail not connected" + msg.LogFormat() );
  478. return;
  479. }
  480. qqW.Enqueue( msg );
  481. }
  482. #endregion
  483. #region HelpMothed
  484. byte MakeCRC8CheckSum( OCSMessage msg, bool isSend )
  485. {
  486. if ( isSend )
  487. return ChksumUtils.Crc8( this.crcTable, msg.ToCRC8Byte4Send() );
  488. else
  489. return ChksumUtils.Crc8( this.crcTable, msg.ToCRC8Byte4Received() );
  490. }
  491. void ChgTcpTimeout( bool infinite )
  492. {
  493. if ( h.Connected )
  494. {
  495. if ( infinite )
  496. {
  497. if ( h.Socket.ReceiveTimeout != Timeout.Infinite )
  498. h.ChangeRecvTimeout( Timeout.Infinite );
  499. }
  500. else
  501. {
  502. if ( Config.LinkOn )
  503. h.ChangeRecvTimeout( Config.T6 * ConstUtils.ONE_SECOND );
  504. }
  505. }
  506. }
  507. #endregion`
  508. }
  509. }