{"id":54,"date":"2025-10-24T00:44:10","date_gmt":"2025-10-24T00:44:10","guid":{"rendered":"https:\/\/5047878.net\/blog\/?p=54"},"modified":"2025-10-24T00:52:23","modified_gmt":"2025-10-24T00:52:23","slug":"%ed%8c%8c%ec%9d%b4%ec%8d%ac%ec%9c%bc%eb%a1%9c-opc-%ed%86%b5%ec%8b%a0%ed%95%98%ea%b8%b0-%ec%8b%a4%ec%a0%84%ec%9d%91%ec%9a%a9%ed%8e%b8","status":"publish","type":"post","link":"https:\/\/5047878.net\/?p=54","title":{"rendered":"\ud30c\uc774\uc36c\uc73c\ub85c OPC \ud1b5\uc2e0\ud558\uae30 (\uc2e4\uc804\uc751\uc6a9\ud3b8)"},"content":{"rendered":"\n<h1 class=\"wp-block-heading\">\ud83e\udde0 \uc2e4\uc804 \uc608\uc81c\ub85c \ubc30\uc6b0\ub294 \ud30c\uc774\uc36c OPC UA \ud1b5\uc2e0 (Python OPCUA Client)<\/h1>\n\n\n\n<p><strong>\ud0a4\uc6cc\ub4dc:<\/strong> \ud30c\uc774\uc36c OPC \ud1b5\uc2e0, OPC UA, Python OPCUA Client, \uc0b0\uc5c5\uc790\ub3d9\ud654, MES \uc5f0\ub3d9, PLC \ub370\uc774\ud130, \uacf5\uc7a5\uc790\ub3d9\ud654, OPCUA Python \uc608\uc81c<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">\ud83c\udfed OPC UA\ub780 \ubb34\uc5c7\uc778\uac00?<\/h2>\n\n\n\n<p>OPC UA(Open Platform Communications Unified Architecture)\ub294 <strong>\uc0b0\uc5c5 \uc790\ub3d9\ud654 \ubd84\uc57c\uc758 \ud45c\uc900 \ud1b5\uc2e0 \ud504\ub85c\ud1a0\ucf5c<\/strong>\uc785\ub2c8\ub2e4.<br>PLC(Programmable Logic Controller), MES, SCADA, HMI \ub4f1 \ub2e4\uc591\ud55c \uc2dc\uc2a4\ud15c \uac04\uc758 <strong>\ub370\uc774\ud130 \uad50\ud658\uc744 \ud45c\uc900\ud654<\/strong>\ud558\uae30 \uc704\ud574 \uc0ac\uc6a9\ub429\ub2c8\ub2e4.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u2705 <strong>TCP \uae30\ubc18 \uc548\uc815\uc801\uc778 \ud1b5\uc2e0<\/strong><\/li>\n\n\n\n<li>\u2705 <strong>\ubcf4\uc548(\uc554\ud638\ud654, \uc778\uc99d) \uc9c0\uc6d0<\/strong><\/li>\n\n\n\n<li>\u2705 <strong>\ud06c\ub85c\uc2a4 \ud50c\ub7ab\ud3fc (Windows, Linux, Python, C#, Node.js \ub4f1)<\/strong><\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">\ud83e\uddf0 Python\uc5d0\uc11c OPC UA \ud1b5\uc2e0\ud558\uae30<\/h2>\n\n\n\n<p>Python\uc5d0\uc11c\ub294 <code>opcua<\/code> \ub77c\uc774\ube0c\ub7ec\ub9ac\ub97c \ud1b5\ud574 OPC \uc11c\ubc84\uc640 \uc27d\uac8c \ud1b5\uc2e0\ud560 \uc218 \uc788\uc2b5\ub2c8\ub2e4.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>pip install opcua\n<\/code><\/pre>\n\n\n\n<p>\uc774 \ud55c \uc904\uc774\uba74 \uc124\uce58 \uc644\ub8cc!<br>\uc774\uc81c \uc2e4\uc81c OPC \uc11c\ubc84\uc758 \ub370\uc774\ud130(\uc608: MES_STRING, \uc0dd\uc0b0\uba85, \uc8fc\ubb38\ubc88\ud638 \ub4f1)\ub97c \uc77d\uc5b4\uc62c \uc218 \uc788\uc2b5\ub2c8\ub2e4.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">\u2699\ufe0f \uc2e4\uc804 \uc608\uc81c: MES-PLC OPC \ud1b5\uc2e0 \uc790\ub3d9\ud654 \uc2a4\ud06c\ub9bd\ud2b8<\/h2>\n\n\n\n<p>\uc544\ub798 \uc608\uc81c\ub294 \uc2e4\uc81c \uc0dd\uc0b0\ub77c\uc778\uc5d0\uc11c \uc0ac\uc6a9 \uac00\ub2a5\ud55c \uc218\uc900\uc73c\ub85c \uc791\uc131\ub41c <strong>\uc644\uc804\ud55c OPC \ud1b5\uc2e0 \ucf54\ub4dc<\/strong>\uc785\ub2c8\ub2e4.<br>INI \uc124\uc815 \ud30c\uc77c\uc744 \uc774\uc6a9\ud574 <strong>IP, \ud3ec\ud2b8, \ub77c\uc778 \uc815\ubcf4<\/strong>\ub97c \uc678\ubd80\uc5d0\uc11c \uc81c\uc5b4\ud560 \uc218 \uc788\uace0,<br>\uc2e4\ud589 \uc911\ubcf5 \ubc29\uc9c0 \ubc0f \uc5d0\ub7ec \ub85c\uae45\uae4c\uc9c0 \uc9c0\uc6d0\ud569\ub2c8\ub2e4.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>from opcua import Client\nimport configparser, sys, os, msvcrt\n\n# -----------------------------\n# \uc911\ubcf5 \uc2e4\ud589 \ubc29\uc9c0\n# -----------------------------\nLOCK_FILE = \"program.lock\"\nfp = open(LOCK_FILE, \"w\")\ntry:\n    msvcrt.locking(fp.fileno(), msvcrt.LK_NBLCK, 1)\nexcept OSError:\n    print(\"\uc774\ubbf8 \uc2e4\ud589 \uc911\uc785\ub2c8\ub2e4.\")\n    sys.exit(0)\n\n# -----------------------------\n# INI \ud30c\uc77c \ucc98\ub9ac\n# -----------------------------\nINI_FILE = \"APIDATA.INI\"\n\ndef write_ini(key, value):\n    config = configparser.ConfigParser()\n    config.read(INI_FILE, encoding=\"utf-8\")\n    if \"RESULT\" not in config:\n        config&#91;\"RESULT\"] = {}\n    config&#91;\"RESULT\"]&#91;key] = value\n    with open(INI_FILE, \"w\", encoding=\"utf-8\") as f:\n        config.write(f)\n\ndef read_ini(key, default=\"\"):\n    config = configparser.ConfigParser()\n    config.read(INI_FILE, encoding=\"utf-8\")\n    return config&#91;\"RESULT\"].get(key, default) if \"RESULT\" in config else default\n\n# \ucd08\uae30\uac12 \uc124\uc815\nwrite_ini(\"DATA_YN\", \"N\")\nwrite_ini(\"PROD_NAME\", \"\")\nwrite_ini(\"PROCESS_ORDER_NO\", \"\")\nwrite_ini(\"DATA_ERR\", \"\")\n\n# -----------------------------\n# OPC \uc11c\ubc84 \uc5f0\uacb0\n# -----------------------------\nip1 = read_ini(\"IP\", \"127.0.0.1\")\nport = read_ini(\"PORT\", \"4840\")\nip = ip1 + \":\" + port\nline = read_ini(\"LINE\", \"1\")\n\nurl = f\"opc.tcp:\/\/{ip}\"\nclient = Client(url)\n\ntry:\n    client.connect()\n    write_ini(\"DATA_YN\", \"Y\")\n    print(\"\u2705 OPC UA \uc11c\ubc84 \uc5f0\uacb0 \uc131\uacf5\")\n\n    # \ub77c\uc778\ubcc4 NodeId \uc124\uc815\n    if line == '1':\n        node_prod = client.get_node(\"ns=7;s=L1.CPU.MES_STRING.Prod_Name\")\n        node_order = client.get_node(\"ns=7;s=L1.CPU.MES_STRING.Process_order_NO\")\n    elif line == '2':\n        node_prod = client.get_node(\"ns=7;s=L2.CPU.MES_STRING.Prod_Name\")\n        node_order = client.get_node(\"ns=7;s=L2.CPU.MES_STRING.Process_order_NO\")\n    elif line == '3':\n        node_prod = client.get_node(\"ns=7;s=L3.CPU.MES_STRING.Prod_Name\")\n        node_order = client.get_node(\"ns=7;s=L3.CPU.MES_STRING.Process_order_NO\")\n    else:\n        raise ValueError(f\"LINE \uac12\uc774 \uc798\ubabb\ub418\uc5c8\uc2b5\ub2c8\ub2e4: {line}\")\n\n    # \uac12 \uc77d\uae30 \ubc0f \uc815\uc81c\n    prod_name = str(node_prod.get_value()).replace('\\x00', '').strip()\n    process_order = str(node_order.get_value()).replace('\\x00', '').strip()\n\n    print(f\"Prod_Name \uac12: {prod_name}\")\n    print(f\"Process_order_NO \uac12: {process_order}\")\n\n    # INI \uc800\uc7a5\n    write_ini(\"PROD_NAME\", prod_name)\n    write_ini(\"PROCESS_ORDER_NO\", process_order)\n\nexcept Exception as e:\n    print(\"\u274c \uc624\ub958:\", e)\n    write_ini(\"DATA_ERR\", f\"ERROR: {e}\")\n\nfinally:\n    try:\n        client.disconnect()\n    except:\n        pass\n<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">\ud83d\uddc2\ufe0f INI \ud30c\uc77c \uad6c\uc131 \uc608\uc2dc<\/h2>\n\n\n\n<p><code>APIDATA.INI<\/code> \ud30c\uc77c\uc740 \uc124\uc815 \ubc0f \uacb0\uacfc\uac12 \uc800\uc7a5\uc6a9\uc785\ub2c8\ub2e4.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&#91;RESULT]\nIP = 192.168.0.100\nPORT = 4840\nLINE = 1\nPROD_NAME =\nPROCESS_ORDER_NO =\nDATA_YN = N\nDATA_ERR =\n<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">\u2705 \uc2e4\ud589 \uacb0\uacfc \uc608\uc2dc<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>\u2705 OPC UA \uc11c\ubc84 \uc5f0\uacb0 \uc131\uacf5\nProd_Name \uac12: TEST_PRODUCT_001\nProcess_order_NO \uac12: P20251023001\n<\/code><\/pre>\n\n\n\n<p>\uacb0\uacfc\ub294 <code>APIDATA.INI<\/code> \ud30c\uc77c\uc5d0 \uc790\ub3d9\uc73c\ub85c \uae30\ub85d\ub429\ub2c8\ub2e4.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">\ud83d\ude80 \uc2e4\ubb34 \ud65c\uc6a9 \ud3ec\uc778\ud2b8<\/h2>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>\ud56d\ubaa9<\/th><th>\uc124\uba85<\/th><\/tr><\/thead><tbody><tr><td>\ud83d\udd12 \uc2e4\ud589 \uc911\ubcf5 \ubc29\uc9c0<\/td><td><code>msvcrt.locking()<\/code> \uc0ac\uc6a9\uc73c\ub85c \uc911\ubcf5 \uc2e4\ud589 \ucc28\ub2e8<\/td><\/tr><tr><td>\ud83e\uddfe \uc124\uc815 \ubd84\ub9ac<\/td><td><code>APIDATA.INI<\/code>\ub85c \uc720\uc5f0\ud55c \ud658\uacbd \uc81c\uc5b4<\/td><\/tr><tr><td>\ud83d\udce1 \uc2e4\uc2dc\uac04 \ub370\uc774\ud130 \uc77d\uae30<\/td><td><code>get_node().get_value()<\/code>\ub85c \uc989\uc2dc \uac12 \uc218\uc2e0<\/td><\/tr><tr><td>\ud83d\udcbe \uc790\ub3d9 \ub85c\uadf8 \uc800\uc7a5<\/td><td>\uc0dd\uc0b0\uba85, \uc8fc\ubb38\ubc88\ud638 \uacb0\uacfc\uac12 \uc790\ub3d9 \uc800\uc7a5<\/td><\/tr><tr><td>\u26a0\ufe0f \uc5d0\ub7ec \ucc98\ub9ac<\/td><td>\ud1b5\uc2e0 \uc624\ub958 \ubc1c\uc0dd \uc2dc \uc790\ub3d9 \uae30\ub85d<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">\ud83d\udca1 \ub9c8\ubb34\ub9ac<\/h2>\n\n\n\n<p>\uc774 \uc2a4\ud06c\ub9bd\ud2b8\ub294 \uc2e4\uc81c MES-PLC \ud658\uacbd\uc5d0\uc11c<br><strong>\uc0dd\uc0b0\uba85 \/ \uc791\uc5c5\uc9c0\uc2dc\ubc88\ud638 \/ \uc2e4\uc2dc\uac04 \uc0c1\ud0dc\uac12<\/strong> \ub4f1\uc744 \uc790\ub3d9 \uc218\uc9d1\ud558\ub294 \ub370 \ud65c\uc6a9\ud560 \uc218 \uc788\uc2b5\ub2c8\ub2e4.<\/p>\n\n\n\n<p>Python\uc758 <code>opcua<\/code> \ubaa8\ub4c8\uc740 \uac00\ubccd\uace0 \ube60\ub974\uba70,<br><strong>\uc790\ub3d9\ud654, \ub370\uc774\ud130 \uc218\uc9d1, \ubd84\uc11d \uc2dc\uc2a4\ud15c\uacfc \uc5f0\ub3d9\ud558\uae30\uc5d0 \ucd5c\uc801<\/strong>\uc785\ub2c8\ub2e4.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h3 class=\"wp-block-heading\">\ud83d\udcce \ub2e4\uc74c\uae00 \uc608\uace0<\/h3>\n\n\n\n<p>\ud83d\udc49 [Python OPCUA\ub85c PLC \uc4f0\uae30(Write) \uba85\ub839 \uad6c\ud604\ud558\uae30]<br>\ud83d\udc49 [\uc2e4\uc2dc\uac04 \ud0dc\uadf8 \ubaa8\ub2c8\ud130\ub9c1 \uc790\ub3d9\ud654 \uc608\uc81c]<\/p>\n","protected":false},"excerpt":{"rendered":"<p>\ud83e\udde0 \uc2e4\uc804 \uc608\uc81c\ub85c \ubc30\uc6b0\ub294 \ud30c\uc774\uc36c OPC UA \ud1b5\uc2e0 (Python OPCUA Client) \ud0a4\uc6cc\ub4dc: \ud30c\uc774\uc36c OPC \ud1b5\uc2e0, OPC UA, Python<\/p>\n","protected":false},"author":1,"featured_media":50,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[2,7],"tags":[],"class_list":["post-54","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-it","category-python"],"_links":{"self":[{"href":"https:\/\/5047878.net\/index.php?rest_route=\/wp\/v2\/posts\/54","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/5047878.net\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/5047878.net\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/5047878.net\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/5047878.net\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=54"}],"version-history":[{"count":3,"href":"https:\/\/5047878.net\/index.php?rest_route=\/wp\/v2\/posts\/54\/revisions"}],"predecessor-version":[{"id":57,"href":"https:\/\/5047878.net\/index.php?rest_route=\/wp\/v2\/posts\/54\/revisions\/57"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/5047878.net\/index.php?rest_route=\/wp\/v2\/media\/50"}],"wp:attachment":[{"href":"https:\/\/5047878.net\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=54"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/5047878.net\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=54"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/5047878.net\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=54"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}