{ "cells": [ { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "# Durable Rules로 규칙 구현하기\n", "- 산업인공지능학과 대학원\n", " 2022254026\n", " 김홍열" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "### Durable Rules 란?\n", "* 비즈니스 룰 엔진을 구현하기 위한 라이브러리로 python, ruby, nodejs로 구현할 수 있다." ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "### Durable Rules 설치하기 (Python)\n", "``` plantext\n", "pip install durable_rules" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "### Durable Rules 다운로드\n", "[github - jruizgit/rules](https://github.com/jruizgit/rules)" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "##### 패키지 불러오기" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [], "source": [ "from durable.lang import *" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "#### 구현할 규칙\n", "![2차전지 라인 구성도](./images/2ndBatteryLine.png)\n", "> 규칙\n", "``` plain\n", "1. IF Input Left_JR AND Right_JR\n", "\t\tTHEN Form JR\n", "\t\t\n", "2. IF Form Left_JR AND Right_JR\n", "\t\tTHEN Cut Left_JR AND Right_JR\n", "\t\t\n", "3. IF Cut Left_JR AND Right_JR\n", "\t\tTHEN Align Left_JR AND Right_JR\n", "\t\t\n", "4. IF Pass Align Vision_Inspection\n", "\t\tTHEN Return Result_None\n", "\t\tAND F&C&T\n", "\n", "5. ELSE Align Vision_Inspection\n", "\t\tTHEN Move Left Point\n", "\t\tAND Move Right Point\n", "\n", "6. IF Move Left And Right Point\n", "\t\tTHEN Align Vision_Inspection\n", "\t\tAND Retrun Result_OK\n", "\t\t\tOR Retrun Result_NG\n", "\t\tAND F&C&T\n", "\t\t\n", "7. IF Pass F&C&T Vision_Inspection\n", "\t\tTHEN Return Result_None\n", "\t\tAND I&T\n", "\n", "8. ELSE F&C&T Vision_Inspection\n", "\t\tTHEN Front Vision_Inspection\n", "\t\tAND Rear Vision_Inspection\n", "\t\tAND Retrun Result_OK\n", "\t\t\tOR Retrun Result_NG\n", "\n", "9. IF Front AND Rear Vision_Inspection\n", "\t\tTHEN Rotate JR\n", "\n", "10. IF Rotate JR\n", "\t\tTHEN Left Vision_Inspection\n", "\t\tAND Right Vision_Inspection\n", "\t\tAND Retrun Result_OK\n", "\t\t\tOR Retrun Result_NG\n", "\t\tAND I&T\n", "\n", "11. IF Pass I&T Vision_Inspection\n", "\t\tTHEN Return Result_None\n", "\t\tAND C&W\n", "\t\t\n", "12. ELSE I&T Vision_Inspection\n", "\t\tTHEN Left Vision_Inspection\n", "\t\tAND Right Vision_Inspection\n", "\t\tAND Retrun Result_OK\n", "\t\t\tOR Retrun Result_NG\n", "\t\t\n", "13. IF Left AND Right Vision_Inspection\n", "\t\tTHEN Move Up JR\n", "\t\tAND Rotate JR\n", "\t\t\n", "14. IF Rotate JR\n", "\t\tTHEN Front Vision_Inspection\n", "\t\tAND Rear Vision_Inspection\n", "\t\tAND Retrun Result_OK\n", "\t\t\tOR Retrun Result_NG\n", "\t\tAND C&W\n", "\t\t\n", "15. IF Pass C&W Vision_Inspection\n", "\t\tTHEN Return None Result\n", "\t\tAND R&T\n", "\t\t\n", "16. ELSE C&W Vision_Inspection\n", "\t\tTHEN Left Vision_Inspection\n", "\t\tAND Right Vision_Inspection\n", "\t\tAND Retrun Result_OK\n", "\t\t\tOR Retrun Result_NG\n", "\t\tAND R&T\n", "\t\t\n", "17. IF Pass R&T Vision_Inspection\n", "\t\tTHEN Return Result_None\n", "\t\tAND Wrapping\n", "\t\t\n", "18. ELSE R&T Vision_Inspection\n", "\t\tTHEN Left Vision_Inspection\n", "\t\tAND Right Vision_Inspection\n", "\t\tAND Retrun Result_OK\n", "\t\t\tOR Retrun Result_NG\n", "\t\tAND Wrapping\n", "\t\t\n", "19. IF Pass Wrapping Vision_Inspection\n", "\t\tTHEN Return Result_None\n", "\t\tAND C&C&W\n", "\t\t\n", "20. ELSE Wrapping Vision_Inspection\n", "\t\tTHEN Front Vision_Inspection\n", "\t\tAND Rear Vision_Inspection\n", "\t\tAND Retrun Result_OK\n", "\t\t\tOR Retrun Result_NG\n", "\t\t\n", "21. IF Front AND Rear Vision_Inspection\n", "\t\tTHEN Rotate JR\n", "\t\t\n", "22. IF Rotate JR\n", "\t\tTHEN Left Vision_Inspection\n", "\t\tAND Right Vision_Inspection\n", "\t\tAND Retrun Result_OK\n", "\t\t\tOR Retrun Result_NG\n", "\t\t\n", "23. IF Left AND Right Vision_Inspection\n", "\t\tTHEN Retrun Result_OK\n", "\t\t\tOR Retrun Result_NG\n", "\t\tAND C&C&W\n", "\t\t\n", "24. IF Pass C&C&W Vision_Inspection\n", "\t\tTHEN Return Result_None\n", "\t\t\n", "25. ELSE C&C&W Vision_Inspection\n", "\t\tTHEN Long_Axis Vision_Inspection\n", "\t\tAND Retrun Result_OK\n", "\t\t\tOR Retrun Result_NG\n", "\n", "26. IF Long_Axis Vision_Inspection\n", "\t\tTHEN Retrun Result_OK\n", "\t\t\tOR Retrun Result_NG\n", "\t\tAND Rotate JR\n", "\n", "27. IF Rotate JR\n", "\t\tTHEN Short_Axis Vision_Inspection\n", "\t\tAND Retrun Result_OK\n", "\t\t\tOR Retrun Result_NG" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "### 1. 각 라인 공정별 StateFlow 구현" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "from durable.lang import *\n", "\n", "inspectionResult = ['OK','OK','OK','OK','OK','OK','OK'] #\n", "def updateState(s, m): # \n", " s.JobID = m.JobID\n", " s.Pass = m.Pass\n", " s.Left = m.Left\n", " s.Right = m.Right\n", " s.Judge0 = m.Judge0\n", " s.Judge1 = m.Judge1\n", " s.Judge2 = m.Judge2\n", " s.Judge3 = m.Judge3\n", " s.Judge4 = m.Judge4\n", " s.Judge5 = m.Judge5\n", " s.Judge6 = m.Judge6\n", "\n", "\n", "with flowchart('battery_assembley_process'):\n", " with stage('Input'):\n", " @run\n", " def nextToJR(c):\n", " print('>> current stage is Input')\n", " updateState(c.s, c.m)\n", " print ('Input JobID: {0}'.format(c.m.JobID))\n", "\n", " to('JR').when_all((m.JobID != None))\n", " to('Alarm').when_all((m.JobID == None))\n", " \n", " \n", " with stage('JR'):\n", " @run\n", " def nextToF_C(c):\n", " print('> current stage is JR')\n", " updateState(c.s, c.m) # JobID는 전달되나 나머지 파라미터들은 전달되지 않음\n", " print(c.s.Judge)\n", " if c.m.Left is None: # @when_all(m.Left != None) 조건으로 함수를 별도로도 생성해보았으나 인식 안 됨\n", " print('Alarm: Did not receive the Left JR.')\n", " if c.m.Right is None:\n", " print('Alarm: Did not receive the Right JR.')\n", " if c.m.Left is not None and c.m.Right is not None:\n", " print ('Both the Left JR[{0}] and the Right JR[{1}] have been assembled.'.format(c.m.Left, c.m.Right))\n", " \n", " to('Alarm').when_any((m.Left == None),\n", " (m.Right == None))\n", " to('F&C')\n", "\n", " with stage('F&C'):\n", " @run\n", " def NextToAlign(c):\n", " print ('> current stage is F&C')\n", " updateState(c.s, c.m)\n", " if c.m.Pass is False:\n", " print('F&C Inspection has been processed.')\n", " print(f'F&C Judgement: {inspectionResult[0]}.')\n", " else:\n", " print('F&C Inspection is passed.')\n", "\n", " to('Align')\n", "\n", " with stage('Align'):\n", " @run\n", " def NextToI_T(c):\n", " print ('> current stage is Align')\n", " updateState(c.s, c.m)\n", " if c.m.Pass is False:\n", " print('Align Inspection has been processed.')\n", " print(f'F&C Judgement: {inspectionResult[1]}.')\n", " else:\n", " print('Align Inspection is passed.')\n", "\n", " to('I&T')\n", "\n", " with stage('I&T'):\n", " @run\n", " def NextToCAPWelding(c):\n", " print ('> current stage is I&T')\n", " updateState(c.s, c.m)\n", " if c.m.Pass is False:\n", " print('I&T Inspection has been processed.')\n", " print(f'F&C Judgement: {inspectionResult[2]}.')\n", " else:\n", " print('I&T Inspection is passed.')\n", "\n", " to('CAPWelding')\n", "\n", " with stage('CAPWelding'):\n", " @run\n", " def NextToRetainer(c):\n", " print ('> current stage is CAPWelding')\n", " updateState(c.s, c.m)\n", " if c.m.Pass is False:\n", " print('CAPWelding Inspection has been processed.')\n", " print(f'F&C Judgement: {inspectionResult[3]}.')\n", " else:\n", " print('CAPWelding Inspection is passed.')\n", "\n", " to('Retainer')\n", "\n", " with stage('Retainer'):\n", " @run\n", " def NextToWrapping(c):\n", " print ('> current stage is Retainer')\n", " updateState(c.s, c.m)\n", " if c.m.Pass is False:\n", " print('Retainer Inspection has been processed.')\n", " print(f'F&C Judgement: {inspectionResult[4]}.')\n", " else:\n", " print('Retainer Inspection is passed.')\n", "\n", " to('Wrapping')\n", "\n", " with stage('Wrapping'):\n", " @run\n", " def NextToC_CWelding(c):\n", " print ('> current stage is Wrapping')\n", " updateState(c.s, c.m)\n", " if c.m.Pass is False:\n", " print('Wrapping Inspection has been processed.')\n", " print(f'F&C Judgement: {inspectionResult[5]}.')\n", " else:\n", " print('Wrapping Inspection is passed.')\n", "\n", " to('C&CWelding')\n", "\n", " with stage('C&CWelding'):\n", " @run\n", " def InspectionComplete(c):\n", " print ('> current stage is C&CWelding')\n", " updateState(c.s, c.m)\n", " # c.s.JobID = c.m.JobID\n", " # c.s.Pass = c.m.Pass\n", " # c.s.Left = c.m.Left\n", " # c.s.Right = c.m.Right\n", " # c.s.Judge0 = c.m.Judge0\n", " # c.s.Judge1 = c.m.Judge1\n", " # c.s.Judge2 = c.m.Judge2\n", " # c.s.Judge3 = c.m.Judge3\n", " # c.s.Judge4 = c.m.Judge4\n", " # c.s.Judge5 = c.m.Judge5\n", " # c.s.Judge6 = c.m.Judge6\n", " if c.m.Pass is False:\n", " print('C&CWelding Inspection has been processed.')\n", " # inspectionResult[6] = 'TEST'\n", " # c.s.Judge6 = c.m.Judge6 = 'NG'\n", " print(f'F&C Judgement: {inspectionResult[6]}.')\n", " else:\n", " print('C&CWelding Inspection is passed.')\n", "\n", " to('Output')\n", "\n", " with stage('Output'):\n", " @run\n", " def InspectionComplete(c):\n", " print ('> current stage is Output')\n", " result = 'OK'\n", " for rst in inspectionResult:\n", " if rst == 'NG':\n", " result = 'NG'\n", " break\n", " # print(inspectionResult)\n", " # print (result)\n", " # print(c.s.JobID)\n", " # print(c.s.Judge)\n", " # print(c.s.Judge6)\n", " print ('JobID[{0}]\\'s Result is {1}'.format(c.s.JobID, result))\n", "\n", " with stage('Alarm'):\n", " @run\n", " def Log(c):\n", " print ('The process will not proceed due to an alarm being triggered. - [JobID: ' + str(c.m.JobID) + ']')" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "### 2. Input Data 구현" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ ">> current stage is Input\n", "The process will not proceed due to an alarm being triggered. - [JobID: None]\n", ">> current stage is Input\n", "> current stage is JR\n", "> current stage is F&C\n", "> current stage is Align\n", "> current stage is I&T\n", "> current stage is CAPWelding\n", "> current stage is Retainer\n", "> current stage is Wrapping\n", "> current stage is C&CWelding\n", "> current stage is Output\n", "JobID[20230417]'s Result is OK\n", ">> current stage is Input\n", "> current stage is JR\n", "> current stage is F&C\n", "> current stage is Align\n", "> current stage is I&T\n", "> current stage is CAPWelding\n", "> current stage is Retainer\n", "> current stage is Wrapping\n", "> current stage is C&CWelding\n", "> current stage is Output\n", "JobID[20230418]'s Result is OK\n", ">> current stage is Input\n", "> current stage is JR\n", "> current stage is F&C\n", "> current stage is Align\n", "> current stage is I&T\n", "> current stage is CAPWelding\n", "> current stage is Retainer\n", "> current stage is Wrapping\n", "> current stage is C&CWelding\n", "> current stage is Output\n", "JobID[20230419]'s Result is NG\n" ] }, { "data": { "text/plain": [ "{'sid': '3',\n", " 'id': 'sid-3',\n", " '$s': 1,\n", " 'running': True,\n", " 'exception': 'exception caught \\'NoneType\\' object has no attribute \\'JobID\\', traceback [\\' File \"/config/.local/lib/python3.10/site-packages/durable/engine.py\", line 241, in run\\\\n self._func(c)\\\\n\\', \\' File \"/tmp/ipykernel_31962/4233634066.py\", line 129, in InspectionComplete\\\\n updateState(c.s, c.m)\\\\n\\', \\' File \"/tmp/ipykernel_31962/4233634066.py\", line 5, in updateState\\\\n s.JobID = m.JobID\\\\n\\']',\n", " 'JobID': '20230419',\n", " 'Pass': False,\n", " 'Left': 100,\n", " 'Right': 100}" ] }, "execution_count": 2, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# 만들어진 규칙에 넣을 데이터들, 라인에 들어가는 각 배터리의 정보\n", "post('battery_assembley_process', { 'JobID': None,\n", " 'Pass': False,\n", " 'Left': 100,\n", " 'Right': 100,\n", " # 'Judge0': None,\n", " # 'Judge1': None,\n", " # 'Judge2': None,\n", " # 'Judge3': None,\n", " # 'Judge4': None,\n", " # 'Judge5': None,\n", " # 'Judge6': None})\n", " 'Judge':[None, None, None, None, None, None, None]})\n", "post('battery_assembley_process', { 'sid': 1,\n", " 'JobID': '20230417', \n", " 'Pass': False,\n", " 'Left': 100,\n", " 'Right': None,\n", " # 'Judge0': None,\n", " # 'Judge1': None,\n", " # 'Judge2': None,\n", " # 'Judge3': None,\n", " # 'Judge4': None,\n", " # 'Judge5': None,\n", " # 'Judge6': None})\n", " 'Judge':[None, None, None, None, None, None, None]})\n", "post('battery_assembley_process', { 'sid': 2,\n", " 'JobID': '20230418', \n", " 'Pass': False,\n", " 'Left': None,\n", " 'Right': 100,\n", " # 'Judge0': None,\n", " # 'Judge1': None,\n", " # 'Judge2': None,\n", " # 'Judge3': None,\n", " # 'Judge4': None,\n", " # 'Judge5': None,\n", " # 'Judge6': None})\n", " 'Judge':[None, None, None, None, None, None, None]})\n", "\n", "inspectionResult = ['OK','OK','OK','OK','NG','OK','OK'] # 내부적으로 Judege를 접근해서 변경할 수 없어서 테스트용으로 생성함\n", "post('battery_assembley_process', { 'sid': 3,\n", " 'JobID': '20230419', \n", " 'Pass': False,\n", " 'Left': 100,\n", " 'Right': 100,\n", " 'Judge0': None,\n", " 'Judge1': None,\n", " 'Judge2': None,\n", " 'Judge3': None,\n", " 'Judge4': None,\n", " 'Judge5': None,\n", " 'Judge6': 'OK'})\n", " # 'Judge':[None, None, None, None, None, None, None]}) # Array를 제공하는듯 보이지만 allitems 같은 불편함을 감수해야됨\n" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "### 결과\n", "* Durable_rule에서 사용되는 규칙을 별도로 습득 해야됨\n", "* 내부적으로 메모리에 저장되는 것들을 접근하거나 디버깅할 수 없음\n", "* 위 코드는 flowchart를 사용하였지만, statechart나 ruleset을 사용하여도 post를 상태마다 날려야 됨\n", "* 내부적으로 어떻게 병렬처리 되는지 커스터마이징 할 수 없음\n", "* 실무에 적용하기에 라이센스나 개발환경에 제약이 많음\n", "* 상태패턴을 구현해서 직접 제어하는 것이 훨씬 효율적으로 보임" ] }, { "cell_type": "markdown", "metadata": {}, "source": [] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.10.6" }, "orig_nbformat": 4 }, "nbformat": 4, "nbformat_minor": 2 }