Vehicle.cs 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941
  1. using System;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using System.Linq;
  5. using System.Threading;
  6. using GSG.NET.Concurrent;
  7. using GSG.NET.LINQ;
  8. using GSG.NET.Logging;
  9. using GSG.NET.Quartz;
  10. using GSG.NET.Utils;
  11. using OHV.Common.Events;
  12. using OHV.Common.Model;
  13. using OHV.Common.Shareds;
  14. using OHV.SqliteDAL;
  15. using Prism.Events;
  16. using VehicleControlSystem.ControlLayer.Actuator.Cylinder;
  17. using VehicleControlSystem.ControlLayer.IO;
  18. using VehicleControlSystem.ControlLayer.Motion;
  19. using VehicleControlSystem.Managers;
  20. namespace VehicleControlSystem.ControlLayer
  21. {
  22. /// <summary>
  23. /// Control Layer 의 자원을 여기서 사용하자.
  24. /// </summary>
  25. public class Vehicle : ControlObjectBase, IDisposable
  26. {
  27. /// <summary>
  28. /// OCS Report Code
  29. /// 목적지에 도착해서 Load, Unload 시 발생하는 Alarm
  30. /// </summary>
  31. public enum eFailCode
  32. {
  33. Load_PortHasNotCarrier = 1,
  34. Load_VehicleHasCarrier,
  35. Unload_PortHasCarrier,
  36. Unload_VehicleHasNotCarrier,
  37. LoadPIOInterlockTimeout,
  38. UnlaodPIOInterlockTimeout,
  39. }
  40. static Logger logger = Logger.GetLogger();
  41. static Logger loggerPIO = Logger.GetLogger( "PIO" );
  42. #region Properties
  43. private double currentPosition;
  44. /// <summary>
  45. /// Tag 위치
  46. /// </summary>
  47. public double CurrentPosition
  48. {
  49. get { return currentPosition; }
  50. set
  51. {
  52. if ( this.currentPosition == value ) return;
  53. this.currentPosition = value;
  54. this.OnCurrentPotisionChanged?.Invoke( (int)value );
  55. }
  56. }
  57. //이동
  58. public bool Busy
  59. {
  60. get
  61. {
  62. return this.CurrentSubCommand == null ? false : true;
  63. }
  64. private set { }
  65. }
  66. public bool IsMoving { get; set; }
  67. public double BatteryVolt { get; set; }
  68. public bool IsError { get; set; }
  69. public bool IsContain { get { return this.IsDetectedCenter(); } }
  70. public SubCmd CurrentSubCommand { get; private set; }
  71. #endregion
  72. #region Event
  73. public event Action OnMoveReady;
  74. public event Action OnMoving;
  75. public event Action OnMoveFinish;
  76. public event Action OnChargingStart;
  77. public event Action OnCharging;
  78. public event Action OnChargingFull;
  79. public event Action<double> OnBatteryVelueChanged;
  80. public event Action<bool> OnPIOStart;
  81. public event Action<bool> OnConveyorStart;
  82. public event Action<bool> OnConveyorStop;
  83. public event Action<bool> OnCarrierDetected;
  84. public event Action OnLoadComplete;
  85. public event Action OnUnloadComplete;
  86. public event Action<int> OnCurrentPotisionChanged;
  87. public event Action OnManualMove;
  88. public event Action OnManualLoad;
  89. public event Action OnManualUnload;
  90. public event Action OnManualCharging;
  91. public event Action<eFailCode> OnFailReport;
  92. #endregion
  93. IIO iO = null;
  94. GSIMotion motion = null;
  95. SqliteManager sql = null;
  96. Clamp clamp = null;
  97. Steering steering = null;
  98. AutoManager autoManager = null;
  99. #region List.
  100. List<ICylinder> cylinders = new List<ICylinder>();
  101. List<string> obstacleBitList = new List<string>();
  102. #endregion
  103. ThreadCancel cancel = new ThreadCancel();
  104. private eObstacleState obstacleState = eObstacleState.Normal;
  105. public eObstacleState ObstacleStateProperty
  106. {
  107. get { return obstacleState; }
  108. set { SetField( ref this.obstacleState, value ); }
  109. }
  110. private eVehicleState vehicleState;
  111. public eVehicleState VehicleStateProperty
  112. {
  113. get { return vehicleState; }
  114. set { SetField( ref this.vehicleState, value ); }
  115. }
  116. IEventAggregator eventAggregator;
  117. public Vehicle( IIO io, SqliteManager sqliteManager, IEventAggregator ea, AutoManager auto )
  118. {
  119. this.iO = io;
  120. this.motion = new GSIMotion();
  121. this.sql = sqliteManager;
  122. this.autoManager = auto;
  123. this.obstacleBitList.AddRange( new string[]
  124. {
  125. "OUT_OBSTRUCTION_PATTERN_00",
  126. "OUT_OBSTRUCTION_PATTERN_01",
  127. "OUT_OBSTRUCTION_PATTERN_02",
  128. "OUT_OBSTRUCTION_PATTERN_03",
  129. "OUT_OBSTRUCTION_PATTERN_04",
  130. } );
  131. this.eventAggregator = ea;
  132. this.eventAggregator.GetEvent<DirveControlPubSubEvent>().Unsubscribe( ReceiveDriveControlEvent );
  133. this.eventAggregator.GetEvent<DirveControlPubSubEvent>().Subscribe( ReceiveDriveControlEvent );
  134. }
  135. private void ReceiveDriveControlEvent( DriveControlEventArgs obj )
  136. {
  137. if ( this.autoManager.OperationModeProperty != eOperatationMode.ManualMode )
  138. return;
  139. var msg = obj;
  140. if ( msg.EventDir == DriveControlEventArgs.eEventDir.ToBack )
  141. {
  142. switch ( msg.ControlKind )
  143. {
  144. case DriveControlEventArgs.eControlKind.MOVE:
  145. break;
  146. case DriveControlEventArgs.eControlKind.STOP:
  147. break;
  148. case DriveControlEventArgs.eControlKind.Steering:
  149. if ( msg.MoveDir == DriveControlEventArgs.eMoveDir.LEFT )
  150. this.steering.ControlSteering( true );
  151. else
  152. this.steering.ControlSteering();
  153. break;
  154. case DriveControlEventArgs.eControlKind.SteeringState:
  155. {
  156. DriveControlEventArgs reply = new DriveControlEventArgs();
  157. reply.ControlKind = DriveControlEventArgs.eControlKind.SteeringState;
  158. if ( this.steering.IsLeft() )
  159. reply.Result = FluentResults.Results.Ok<DriveControlEventArgs.eMoveDir>( DriveControlEventArgs.eMoveDir.LEFT );
  160. else
  161. reply.Result = FluentResults.Results.Ok<DriveControlEventArgs.eMoveDir>( DriveControlEventArgs.eMoveDir.RIGHT );
  162. this.DriveControlEventPublish( reply );
  163. }
  164. break;
  165. default:
  166. break;
  167. }
  168. }
  169. }
  170. private void DriveControlEventPublish( DriveControlEventArgs args )
  171. {
  172. args.EventDir = DriveControlEventArgs.eEventDir.ToFront;
  173. this.eventAggregator.GetEvent<DirveControlPubSubEvent>().Publish( args );
  174. }
  175. public void Init()
  176. {
  177. this.CreateClamp();
  178. this.CreateSteering();
  179. ThreadStart();
  180. }
  181. public int InitializationVehicle()
  182. {
  183. int result = 0;
  184. if ( this.IsDetectedCenter() ) //자제가 있으면 Lock
  185. result = this.clamp.Lock_Sync();
  186. else
  187. result = this.clamp.Unlock_Sync();
  188. if ( this.motion.IsErrorOn )
  189. return 22;
  190. return result;
  191. }
  192. public void Dispose()
  193. {
  194. this.cancel.Cancel();
  195. this.cancel.StopWaitAll();
  196. }
  197. #region Thread
  198. void ThreadStart()
  199. {
  200. this.cancel.AddGo( new Action( this._ThSubCmdWorker ) );
  201. this.cancel.AddGo( new Action( this._ThObstacleChecker ) );
  202. }
  203. //장애물 감지 Thread
  204. //장애물 감지 패턴 변경도 여기 하자.
  205. private void _ThObstacleChecker()
  206. {
  207. while ( !this.cancel.Canceled )
  208. {
  209. try
  210. {
  211. if ( this.autoManager.OperationModeProperty == eOperatationMode.AutoMode )
  212. this.CheckObstacle();
  213. }
  214. catch ( ThreadInterruptedException threadInterruptedException )
  215. {
  216. }
  217. catch ( Exception exception )
  218. {
  219. logger.E( exception );
  220. }
  221. finally
  222. {
  223. LockUtils.Wait( 5 );
  224. }
  225. }
  226. logger.D( "Vehicle - _ThObstacleChecker Dispose" );
  227. }
  228. /// <summary>
  229. /// Scheduler 가 주는 Sub Command 를 이용하여 동작하자.
  230. /// </summary>
  231. public void _ThSubCmdWorker()
  232. {
  233. while ( !this.cancel.Canceled )
  234. {
  235. try
  236. {
  237. if ( this.ObstacleStateProperty != eObstacleState.Normal ) //장애물 감지 상태 시 조그 동작만 가능하게.
  238. continue;
  239. if ( this.autoManager.AutoModeStateProperty != eAutoModeState.Run ) //
  240. continue;
  241. var subCmd = sql.SubCmdDAL.GetSubCmd();
  242. if ( subCmd == null ) continue;
  243. if ( !sql.CommandDAL.All.Any( x => x.CommandID.Equals( subCmd.CmdID ) ) )
  244. {
  245. if ( subCmd.CmdType == SubCmd.eCmdType.Auto ) //자동 명령중 Main Command 가 없으면 삭제.
  246. {
  247. sql.SubCmdDAL.Delete( subCmd );
  248. logger.I( $"SubCmd Deleted - ID={subCmd.ID}, CommandID={subCmd.CmdID}" );
  249. }
  250. }
  251. switch ( subCmd.Type )
  252. {
  253. case SubCmd.eType.Move:
  254. this.CurrentSubCommand = subCmd;
  255. this.Move( subCmd );
  256. break;
  257. case SubCmd.eType.Load:
  258. this.CurrentSubCommand = subCmd;
  259. this.LoadCarrier( subCmd );
  260. break;
  261. case SubCmd.eType.Unload:
  262. this.CurrentSubCommand = subCmd;
  263. this.UnloadCarrier( subCmd );
  264. break;
  265. case SubCmd.eType.Charge:
  266. this.CurrentSubCommand = subCmd;
  267. this.BatteryCharge( subCmd );
  268. break;
  269. default:
  270. break;
  271. }
  272. }
  273. catch ( ThreadInterruptedException threadInterruptedException )
  274. {
  275. }
  276. catch ( Exception exception )
  277. {
  278. logger.E( exception );
  279. }
  280. finally
  281. {
  282. LockUtils.Wait( 500 );
  283. }
  284. }
  285. logger.D( "Vehicle - _ThSubCmdWorker Dispose" );
  286. }
  287. #endregion
  288. #region Control Action Method
  289. void Move( SubCmd sub )
  290. {
  291. if ( this.MoveTo( sub.TargetID ) )
  292. {
  293. sql.SubCmdDAL.Delete( sub );
  294. }
  295. else
  296. {
  297. if ( this.ObstacleStateProperty == eObstacleState.Blocked )
  298. {
  299. }
  300. }
  301. }
  302. bool MoveTo( string pointID )
  303. {
  304. //this.BuzzerOnOff(true, eBuzzerKind.StartWarn);
  305. ////TimerUtils.Once(3000, BuzzerOnOff, false, eBuzzerKind.StartWarn );
  306. //Thread.Sleep(3000);
  307. //this.BuzzerOnOff(false);
  308. this.OnMoveReady?.Invoke();
  309. var moveReadyBuzzerTime = sql.ConfigDal.GetValueToInt( ConstString.BuzzerStartReadyTime );
  310. Thread.Sleep( moveReadyBuzzerTime );
  311. this.OnMoving?.Invoke();
  312. this.IsMoving = true;
  313. //this.BuzzerOnOff(true, eBuzzerKind.Moving);
  314. this.motion.MoveToPoint( pointID, 100 );
  315. bool result = Wait4MoveDone();
  316. this.IsMoving = false;
  317. //this.BuzzerOnOff(false);
  318. this.OnMoveFinish?.Invoke();
  319. return result;
  320. }
  321. bool Wait4MoveDone()
  322. {
  323. int waitTime = 6000; //설정 할 수있게.
  324. long st = SwUtils.CurrentTimeMillis;
  325. //Todo: 이동시 확인 사항들.
  326. while ( true )
  327. {
  328. Thread.Sleep( 5 );
  329. if ( SwUtils.Gt( st, waitTime ) )
  330. {
  331. //Todo: 이동시간 초과 시 동작들.
  332. break;
  333. }
  334. if ( this.ObstacleStateProperty == eObstacleState.Blocked )
  335. return false;
  336. }
  337. return true;
  338. }
  339. public bool LoadCarrier( SubCmd sub )
  340. {
  341. var route = sql.RouteDal.GetRoute( sub.TargetID );
  342. if ( !CorrectPosition( route, this.CurrentPosition ) )
  343. {
  344. this.autoManager.ProcessAlarm( 20 );
  345. return false; //Alarm
  346. }
  347. int result = this.clamp.Unlock_Sync();
  348. if ( result != 0 )
  349. {
  350. this.autoManager.ProcessAlarm( result );
  351. return false;
  352. }
  353. result = this.PIOAndLoad( sub.TargetID );
  354. if ( result != 0 )
  355. {
  356. this.autoManager.ProcessAlarm( result );
  357. return false;
  358. }
  359. result = this.clamp.Lock_Sync();
  360. if ( result != 0 )
  361. {
  362. this.autoManager.ProcessAlarm( result );
  363. return false;
  364. }
  365. sql.CommandDAL.UpdateState( sub.CmdID, eCommandState.Complete );
  366. sql.SubCmdDAL.Delete( sub );
  367. return true;
  368. }
  369. public bool UnloadCarrier( SubCmd sub )
  370. {
  371. var route = sql.RouteDal.GetRoute( sub.TargetID );
  372. if ( !CorrectPosition( route, this.CurrentPosition ) )
  373. {
  374. this.autoManager.ProcessAlarm( 21 );
  375. return false; //Alarm
  376. }
  377. int result = this.clamp.Unlock_Sync();
  378. if ( result != 0 )
  379. {
  380. this.autoManager.ProcessAlarm( result );
  381. return false;
  382. }
  383. result = this.PIOAndUnload( sub.TargetID );
  384. if ( result != 0 )
  385. {
  386. this.autoManager.ProcessAlarm( result );
  387. return false;
  388. }
  389. sql.CommandDAL.UpdateState( sub.CmdID, eCommandState.Complete );
  390. sql.SubCmdDAL.Delete( sub );
  391. return true;
  392. }
  393. public bool BatteryCharge( SubCmd sub )
  394. {
  395. return true;
  396. }
  397. #endregion
  398. #region Check Method
  399. bool CheckObstacle()
  400. {
  401. if ( this.iO.IsOn( "IN_OBSTRUCTION_DETECT_SAFETY" ) || this.iO.IsOn( "IN_OBSTRUCTION_DETECT_ERROR" ) )
  402. {
  403. this.motion.Stop();
  404. this.ObstacleStateProperty = eObstacleState.Abnormal;
  405. return true;
  406. }
  407. if ( this.iO.IsOn( "IN_OBSTRUCTION_DETECT_STOP" ) )
  408. {
  409. this.motion.Stop();
  410. this.ObstacleStateProperty = eObstacleState.Blocked;
  411. return true;
  412. }
  413. if ( this.iO.IsOn( "IN_OBSTRUCTION_DETECT_SLOW" ) )
  414. {
  415. this.motion.SlowStop();
  416. this.ObstacleStateProperty = eObstacleState.Decelerate;
  417. return true;
  418. }
  419. this.ObstacleStateProperty = eObstacleState.Normal;
  420. return false;
  421. }
  422. #endregion
  423. #region Machanical Method
  424. #region Conveyor
  425. int OnOffConveyor( bool isOn, bool isCW = false )
  426. {
  427. if ( IsInverterError() )
  428. return 16;
  429. if ( isCW )
  430. this.iO.OutputOn( "OUT_CV_CWCCW" );
  431. else
  432. this.iO.OutputOff( "OUT_CV_CWCCW" );
  433. if ( isOn )
  434. this.iO.OutputOn( "OUT_CV_RUN" );
  435. else
  436. this.iO.OutputOff( "OUT_CV_RUN" );
  437. return 0;
  438. }
  439. void SetConveyorSpeed( bool IsHight )
  440. {
  441. if ( IsHight )
  442. this.iO.WriteOutputIO( "OUT_CV_DA", true );
  443. else
  444. this.iO.WriteOutputIO( "OUT_CV_DA", false );
  445. }
  446. /// <summary>
  447. /// 입구 감지 로딩시 감속 사용
  448. /// </summary>
  449. /// <returns></returns>
  450. bool IsDetectedLoadStart() => this.iO.IsOn( "IN_CV_DETECT_00" );
  451. /// <summary>
  452. /// 실물 감지
  453. /// </summary>
  454. /// <returns></returns>
  455. public bool IsDetectedCenter() => this.iO.IsOn( "IN_CV_DETECT_01" );
  456. bool IsDetectedLoadStop() => this.iO.IsOn( "IN_CV_DETECT_02" );
  457. bool IsInverterError() => this.iO.IsOn( "IN_CV_ERROR" );
  458. bool IsLifterPositinCheck() => this.iO.IsOn( "IN_LIFTER_POSITION_DETECT" );
  459. bool IsLifterDuplication() => this.iO.IsOn( "IN_LIFTER_DUPLICATION_DETECT" );
  460. bool IsPIOInterLockOn() => this.iO.IsOn( "OUT_PIO_INTERLOCK" );
  461. int Load_Carrier()
  462. {
  463. if ( IsDetectedCenter() )
  464. return 9;
  465. OnOffConveyor( true, true );
  466. long sTime = SwUtils.CurrentTimeMillis;
  467. while ( true )
  468. {
  469. if ( SwUtils.Gt( sTime, 20 * ConstUtils.ONE_SECOND ) ) //Wait 20Sec
  470. {
  471. OnOffConveyor( false, true );
  472. return 10;
  473. }
  474. if ( IsDetectedLoadStart() )
  475. break;
  476. }
  477. return 0;
  478. }
  479. int UnloadCarrier()
  480. {
  481. if ( !IsDetectedLoadStart() )
  482. return 11;
  483. OnOffConveyor( true, true );
  484. long sTime = SwUtils.CurrentTimeMillis;
  485. while ( true )
  486. {
  487. if ( SwUtils.Gt( sTime, 20 * ConstUtils.ONE_SECOND ) ) //Wait 20Sec
  488. {
  489. OnOffConveyor( false, true );
  490. return 12;
  491. }
  492. if ( !IsDetectedLoadStart() )
  493. break;
  494. }
  495. return 0;
  496. }
  497. public int PIOAndLoad( string targetName )
  498. {
  499. #if SIMULATION
  500. PIOClear();
  501. loggerPIO.I($"Start Load PIO - [{targetName}]");
  502. this.OnPIOStart?.Invoke(true);
  503. this.iO.WriteOutputIO("OUT_PIO_RECEIVE_RUN", true);
  504. loggerPIO.I("[Vehicle] - 4 Receive Run On");
  505. Thread.Sleep(1000);//상대 IO 기다린다 생각.
  506. loggerPIO.E("[Port] - 4 Ready On");
  507. //Conveyor Start
  508. loggerPIO.I("[Vehicle] - Conveyor Run");
  509. this.OnConveyorStart?.Invoke(true);
  510. Thread.Sleep(10000);//Conveyor 구동
  511. this.OnCarrierDetected?.Invoke(true);
  512. PIOClear();
  513. Thread.Sleep(1000);
  514. this.OnConveyorStop?.Invoke(true);
  515. #else
  516. var pioTimeout = sql.ConfigDal.GetValueToInt( ConstString.PIOTimeOut );
  517. if ( this.IsInverterError() )
  518. return 16;
  519. if ( this.IsLifterPositinCheck() )
  520. return 14;
  521. if ( !this.IsLifterDuplication() )
  522. {
  523. this.OnFailReport?.Invoke( eFailCode.Load_PortHasNotCarrier );
  524. return 0;
  525. }
  526. if ( this.IsDetectedCenter() )
  527. {
  528. this.OnFailReport?.Invoke( eFailCode.Load_VehicleHasCarrier );
  529. return 0;
  530. }
  531. PIOClear();
  532. loggerPIO.I( $"Start Load PIO - [{targetName}]" );
  533. this.OnPIOStart?.Invoke( true );
  534. this.iO.WriteOutputIO( "OUT_PIO_RECEIVE_RUN", true );
  535. loggerPIO.I( "[Vehicle] - 4 Receive Run On" );
  536. if ( !this.iO.WaitChangeInputIO( true, pioTimeout, "IN_PIO_SENDABLE" ) )
  537. {
  538. PIOClear();
  539. loggerPIO.E( "[Port] - 4 Ready Time Out" );
  540. this.OnFailReport?.Invoke( eFailCode.LoadPIOInterlockTimeout );
  541. return 0;
  542. }
  543. loggerPIO.E( "[Port] - 4 Ready On" );
  544. this.SetConveyorSpeed( true );
  545. this.OnOffConveyor( true, true );
  546. this.iO.WriteOutputIO( "OUT_PIO_RECEIVE_RUN", true );
  547. loggerPIO.I( "[Vehicle] - Conveyor Run" );
  548. this.OnConveyorStart?.Invoke( true );
  549. if ( !this.iO.WaitChangeInputIO( true, pioTimeout, "IN_PIO_SEND_RUN" ) )
  550. {
  551. this.OnOffConveyor( false, true );
  552. PIOClear();
  553. loggerPIO.E( "[Port] - 5 Sending Run Time Out" );
  554. this.OnFailReport?.Invoke( eFailCode.LoadPIOInterlockTimeout );
  555. return 0;
  556. }
  557. bool isStartDetected = false;
  558. var sTime = SwUtils.CurrentTimeMillis;
  559. while ( true )
  560. {
  561. if ( SwUtils.Gt( sTime, 20 * ConstUtils.ONE_SECOND ) )
  562. {
  563. PIOClear();
  564. this.OnOffConveyor( false, true );
  565. loggerPIO.E( "[Vehicle] Conveyor Wait Time Out" );
  566. this.OnFailReport?.Invoke( eFailCode.LoadPIOInterlockTimeout );
  567. if ( this.IsDetectedLoadStart() ) // 감지가 시작 되었으면 이동중 Error 로 판단 설비를 정지 상태로
  568. return 10; //Conveyor Moving Timeout
  569. else
  570. return 0;
  571. }
  572. if ( this.IsDetectedLoadStart() && !isStartDetected )
  573. isStartDetected = true;
  574. if ( !this.IsDetectedLoadStart() && isStartDetected )
  575. this.SetConveyorSpeed( false );
  576. if ( this.IsDetectedLoadStop() ) break;
  577. if ( this.IsPIOInterLockOn() )
  578. {
  579. PIOClear();
  580. this.OnOffConveyor( false ); //Stop
  581. loggerPIO.E( "[Port] PIO InterLock On " );
  582. return 19; //
  583. }
  584. }
  585. if ( this.IsDetectedCenter() )
  586. this.OnCarrierDetected?.Invoke( true );
  587. this.OnOffConveyor( false ); //Stop
  588. PIOClear();
  589. this.OnConveyorStop?.Invoke( true );
  590. this.iO.WriteOutputIO( "OUT_PIO_RECIVE_COMPLITE", true );
  591. this.iO.WriteOutputIO( "OUT_PIO_RECIVE_COMPLITE", false, 1000 );
  592. this.OnLoadComplete?.Invoke();
  593. #endif
  594. return 0;
  595. }
  596. public int PIOAndUnload( string targetName )
  597. {
  598. #if SIMULATION
  599. PIOClear();
  600. loggerPIO.I($"Start Unload PIO - [{targetName}]");
  601. this.OnPIOStart?.Invoke(false);
  602. Thread.Sleep(1000);
  603. this.iO.WriteOutputIO("OUT_PIO_READY", true);
  604. loggerPIO.I("[Vehicle] - 1 Ready On");
  605. Thread.Sleep(1000);
  606. this.OnConveyorStart?.Invoke(false);
  607. Thread.Sleep(10000);
  608. this.OnOffConveyor(false); //Stop
  609. this.OnConveyorStop?.Invoke(false);
  610. PIOClear();
  611. this.OnUnloadComplete?.Invoke();
  612. #else
  613. var pioTimeout = sql.ConfigDal.GetValueToInt( ConstString.PIOTimeOut );
  614. if ( this.IsInverterError() )
  615. return 16;
  616. if ( this.IsLifterDuplication() )
  617. {
  618. this.OnFailReport?.Invoke( eFailCode.Unload_PortHasCarrier );
  619. return 0;
  620. }
  621. if ( !this.IsDetectedCenter() )
  622. {
  623. this.OnFailReport?.Invoke( eFailCode.Unload_VehicleHasNotCarrier );
  624. return 0;
  625. }
  626. if ( this.IsLifterPositinCheck() )
  627. return 13;
  628. PIOClear();
  629. loggerPIO.I( $"Start Unload PIO - [{targetName}]" );
  630. this.OnPIOStart?.Invoke( false );
  631. if ( !this.iO.IsOn( "IN_PIO_READY" ) )
  632. {
  633. loggerPIO.E( "[Port] - 1 Ready not On" );
  634. this.OnFailReport?.Invoke( eFailCode.UnlaodPIOInterlockTimeout );
  635. return 0;
  636. }
  637. this.iO.WriteOutputIO( "OUT_PIO_READY", true );
  638. loggerPIO.I( "[Vehicle] - 1 Ready On" );
  639. if ( !this.iO.WaitChangeInputIO( true, pioTimeout, "IN_PIO_RECEIVE_RUN" ) )
  640. {
  641. PIOClear();
  642. loggerPIO.E( "[Port] - 2 Receive CV Run Timeout" );
  643. this.OnFailReport?.Invoke( eFailCode.UnlaodPIOInterlockTimeout );
  644. return 0;
  645. }
  646. this.iO.WriteOutputIO( "OUT_PIO_SENDING_RUN", true );
  647. loggerPIO.I( "[Vehicle] - 2 Send Run On" );
  648. this.SetConveyorSpeed( true );
  649. this.OnOffConveyor( true );
  650. this.OnConveyorStart?.Invoke( false );
  651. var sTime = SwUtils.CurrentTimeMillis;
  652. while ( true )
  653. {
  654. if ( SwUtils.Gt( sTime, 20 * ConstUtils.ONE_SECOND ) )
  655. {
  656. PIOClear();
  657. this.OnOffConveyor( false, true );
  658. loggerPIO.E( "[Port] Conveyor Wait Time Out" );
  659. this.OnFailReport?.Invoke( eFailCode.UnlaodPIOInterlockTimeout );
  660. if ( IsDetectedLoadStart() || IsDetectedCenter() ) //중간에 걸려 있다고 생각해서 알람 처리.
  661. return 12; //Conveyor Moving Timeout
  662. else
  663. return 0;
  664. }
  665. if ( this.iO.IsOn( "IN_PIO_RECEIVE_COMPLITE" ) )
  666. break;
  667. }
  668. if ( !IsDetectedCenter() )
  669. this.OnCarrierDetected?.Invoke( false );
  670. this.OnOffConveyor( false ); //Stop
  671. this.OnConveyorStop?.Invoke( false );
  672. PIOClear();
  673. this.iO.WriteOutputIO( "OUT_PIO_SEND_COMPLITE", true );
  674. this.iO.WriteOutputIO( "OUT_PIO_SEND_COMPLITE", false, 1000 );
  675. this.OnUnloadComplete?.Invoke();
  676. #endif
  677. return 0;
  678. }
  679. void PIOClear()
  680. {
  681. string[] pio = { "OUT_PIO_READY", "OUT_PIO_SENDING_RUN", "OUT_PIO_SEND_COMPLITE", "OUT_PIO_RECEIVABLE", "OUT_PIO_RECEIVE_RUN", "OUT_PIO_RECIVE_COMPLITE", "OUT_PIO_INTERLOCK" };
  682. pio.FwEach( x => { this.iO.OutputOff( x ); } );
  683. }
  684. #endregion
  685. #endregion
  686. #region Hardware Create Method
  687. void CreateSteering()
  688. {
  689. this.steering = new Steering( this.iO, this.sql, this.eventAggregator );
  690. this.steering.OnSteeringError += Steering_OnSteeringError;
  691. }
  692. void CreateClamp()
  693. {
  694. this.clamp = new Clamp( this.sql, this.eventAggregator );
  695. this.clamp.Init();
  696. }
  697. #endregion
  698. #region Help Method
  699. /// <summary>
  700. /// 현재 좌표 값이 등록된 Route 에 맞는 위치인지 확인한다.
  701. /// 판단 기준은 Route 에 Tolerance 범위를 사용.
  702. /// </summary>
  703. /// <param name="route"></param>
  704. /// <param name="currentPosition"></param>
  705. /// <returns></returns>
  706. bool CorrectPosition( Route route, double currentPosition )
  707. {
  708. var rScale = route.ScaleValue;
  709. var rTolerance = route.ScaleTolerance;
  710. var result = currentPosition - rScale;
  711. if ( rTolerance < Math.Abs( result ) )
  712. return false;
  713. return true;
  714. }
  715. /// <summary>
  716. /// if no is zero, Laser Off
  717. /// bit Off, On, On, On,On Area1
  718. /// </summary>
  719. /// <param name="no"> 0 == Off Laser</param>
  720. /// <returns></returns>
  721. bool ChgObstacleDetectPattern( int no )
  722. {
  723. var bitArray = BitUtils.ChgBitArray( no );
  724. int bitIndex = 0;
  725. this.obstacleBitList.ForEach( b =>
  726. {
  727. if ( bitArray[bitIndex] )
  728. this.iO.OutputOff( b );
  729. else
  730. this.iO.OutputOn( b );
  731. bitIndex++;
  732. } );
  733. return true;
  734. }
  735. int GetObstacleDetectPattern()
  736. {
  737. int bitIndex = 0;
  738. BitArray bitArray = new BitArray( this.obstacleBitList.Count );
  739. this.obstacleBitList.ForEach( b =>
  740. {
  741. if ( this.iO.IsOn( b ) )
  742. bitArray.Set( bitIndex, false );
  743. else
  744. bitArray.Set( bitIndex, true );
  745. bitIndex++;
  746. } );
  747. return BitUtils.ChgInt32( bitArray );
  748. }
  749. #endregion
  750. #region Event Subscribe
  751. private void Steering_OnSteeringError( object sender, int e )
  752. {
  753. if ( e != 0 )
  754. {
  755. logger.E( $"[Steering] - Control Error {e}" );
  756. this.autoManager.ProcessAlarm( e );
  757. }
  758. else
  759. {
  760. var msg = new DriveControlEventArgs()
  761. {
  762. EventDir = DriveControlEventArgs.eEventDir.ToFront,
  763. ControlKind = DriveControlEventArgs.eControlKind.Steering,
  764. Result = FluentResults.Results.Ok<DriveControlEventArgs.eMoveDir>( DriveControlEventArgs.eMoveDir.LEFT ),
  765. };
  766. this.eventAggregator.GetEvent<DirveControlPubSubEvent>().Publish( msg );
  767. }
  768. }
  769. #endregion
  770. }
  771. }