diff --git a/.idea/.gitignore b/.idea/.gitignore
new file mode 100644
index 0000000..35410ca
--- /dev/null
+++ b/.idea/.gitignore
@@ -0,0 +1,8 @@
+# 默认忽略的文件
+/shelf/
+/workspace.xml
+# 基于编辑器的 HTTP 客户端请求
+/httpRequests/
+# Datasource local storage ignored files
+/dataSources/
+/dataSources.local.xml
diff --git a/.idea/gateway.iml b/.idea/gateway.iml
new file mode 100644
index 0000000..8b8c395
--- /dev/null
+++ b/.idea/gateway.iml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml
new file mode 100644
index 0000000..105ce2d
--- /dev/null
+++ b/.idea/inspectionProfiles/profiles_settings.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/modules.xml b/.idea/modules.xml
new file mode 100644
index 0000000..5e1fb45
--- /dev/null
+++ b/.idea/modules.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 0000000..35eb1dd
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/gateway/__pycache__/api_server.cpython-313.pyc b/gateway/__pycache__/api_server.cpython-313.pyc
new file mode 100644
index 0000000..9f3655e
Binary files /dev/null and b/gateway/__pycache__/api_server.cpython-313.pyc differ
diff --git a/gateway/__pycache__/cache_manager.cpython-313.pyc b/gateway/__pycache__/cache_manager.cpython-313.pyc
new file mode 100644
index 0000000..a917bb8
Binary files /dev/null and b/gateway/__pycache__/cache_manager.cpython-313.pyc differ
diff --git a/gateway/__pycache__/config_loader.cpython-313.pyc b/gateway/__pycache__/config_loader.cpython-313.pyc
new file mode 100644
index 0000000..226af8b
Binary files /dev/null and b/gateway/__pycache__/config_loader.cpython-313.pyc differ
diff --git a/gateway/__pycache__/config_manager.cpython-313.pyc b/gateway/__pycache__/config_manager.cpython-313.pyc
new file mode 100644
index 0000000..48957b7
Binary files /dev/null and b/gateway/__pycache__/config_manager.cpython-313.pyc differ
diff --git a/gateway/__pycache__/config_validator.cpython-313.pyc b/gateway/__pycache__/config_validator.cpython-313.pyc
new file mode 100644
index 0000000..b2204d0
Binary files /dev/null and b/gateway/__pycache__/config_validator.cpython-313.pyc differ
diff --git a/gateway/__pycache__/plc_manager.cpython-313.pyc b/gateway/__pycache__/plc_manager.cpython-313.pyc
new file mode 100644
index 0000000..e011a97
Binary files /dev/null and b/gateway/__pycache__/plc_manager.cpython-313.pyc differ
diff --git a/gateway/__pycache__/snap7_client.cpython-313.pyc b/gateway/__pycache__/snap7_client.cpython-313.pyc
new file mode 100644
index 0000000..bcf5711
Binary files /dev/null and b/gateway/__pycache__/snap7_client.cpython-313.pyc differ
diff --git a/gateway/api_server.py b/gateway/api_server.py
index a70941e..9cac2dc 100644
--- a/gateway/api_server.py
+++ b/gateway/api_server.py
@@ -6,13 +6,14 @@ from functools import wraps
from config_manager import ConfigManager
import logging
+
class APIServer:
"""REST API服务器,提供PLC数据访问和配置管理功能"""
-
+
def __init__(self, cache_manager, config_path="config/config.json"):
"""
初始化API服务器
-
+
Args:
cache_manager: 缓存管理器实例
config_path: 配置文件路径
@@ -25,10 +26,10 @@ class APIServer:
self.username = "admin"
self.password = "admin123" # 实际应用中应从安全存储获取
self.start_time = time.strftime("%Y-%m-%d %H:%M:%S")
-
+
# 在初始化方法中调用 setup_routes
self.setup_routes()
-
+
def check_auth(self, username, password):
"""验证用户名和密码"""
return username == self.username and password == self.password
@@ -36,22 +37,24 @@ class APIServer:
def authenticate(self):
"""发送401响应要求认证"""
return Response(
- "Unauthorized",
- 401,
+ "Unauthorized",
+ 401,
{"WWW-Authenticate": 'Basic realm="PLC Gateway Configuration"'}
)
def requires_auth(self, f):
"""装饰器:需要认证的路由,保留函数元数据"""
+
@wraps(f)
def decorated(*args, **kwargs):
if not self.auth_enabled:
return f(*args, **kwargs)
-
+
auth = request.authorization
if not auth or not self.check_auth(auth.username, auth.password):
return self.authenticate()
return f(*args, **kwargs)
+
return decorated
def get_summary(self):
@@ -62,11 +65,12 @@ class APIServer:
for area_name, area in areas.items():
last_update = self.cache_manager.last_update[plc_name][area_name]
plc_status = self.cache_manager.plc_connection_status.get(plc_name, "unknown")
-
+
summary[plc_name][area_name] = {
"status": area["status"],
"plc_connection_status": plc_status,
- "last_update": time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(last_update)) if last_update > 0 else "Never",
+ "last_update": time.strftime("%Y-%m-%d %H:%M:%S",
+ time.localtime(last_update)) if last_update > 0 else "Never",
"size": area["size"],
"type": area["type"]
}
@@ -74,7 +78,7 @@ class APIServer:
def setup_routes(self):
"""设置所有API路由"""
-
+
# ===========================
# 主页面 - 状态摘要
# ===========================
@@ -108,7 +112,7 @@ class APIServer:
PLC Gateway Status
Gateway running since: {{ start_time }}
"""
-
+
for plc_name, areas in summary.items():
plc_status = self.cache_manager.plc_connection_status.get(plc_name, "unknown")
plc_class = ""
@@ -118,7 +122,7 @@ class APIServer:
plc_class = "plc-disconnected"
else:
plc_class = "plc-never-connected"
-
+
html += f'
PLC: {plc_name} (Status: {plc_status})
'
html += """
@@ -131,11 +135,11 @@ class APIServer:
Last Update
"""
-
+
for area_name, area in areas.items():
status_class = ""
status_text = area["status"]
-
+
if area["status"] == "connected":
status_class = "status-connected"
elif area["status"] == "never_connected":
@@ -146,7 +150,7 @@ class APIServer:
status_text = "Disconnected"
else:
status_class = "status-disconnected"
-
+
html += f"""
{area_name}
@@ -157,69 +161,76 @@ class APIServer:
{area['last_update']}
"""
-
+
html += "
"
-
+
# 添加API文档部分
html += """
API Endpoints
-
+
Single Read: GET /api/read////
Example: /api/read/PLC1/DB100_Read/10/4
-
+
Single Write: POST /api/write///
Body: Raw binary data
Example: POST /api/write/PLC1/DB100_Write/10 with 4 bytes of data
-
+
- Single Read_Bool: GET /api/read_bool////
- Example: /api/read_bool/PLC1/DB100_Read/0/2
+ Single Data Write: POST /api/write_data////
+ Body: {"value": value}
+ Example: POST /api/write_data/PLC1/DB100_Write/10/int with {"value": 12345}
-
+
- Single Write_Bool: POST /api/write_bool///
- Body: Raw binary data
- Example: POST /api/write_bool/PLC1/DB100_Write/0
-
-
+ Array Write: POST /api/write_array////
+ Body: {"values": [values]}
+ Example: POST /api/write_array/PLC1/DB100_Write/0/int with {"values": [1,2,3,4,5]}
+
+
+
+ Bit Write: POST /api/write_bit////
+ Body: 0 or 1
+ Example: POST /api/write_bit/PLC1/DB100_Write/10/3 with 1
+
+
Batch Read: POST /api/batch_read
Body: JSON array of read requests
Example: [{"plc_name":"PLC1", "area_name":"DB100_Read", "offset":0, "length":4}]
-
+
Batch Write: POST /api/batch_write
Body: JSON array of write requests
Example: [{"plc_name":"PLC1", "area_name":"DB100_Write", "offset":0, "data":[1,2,3,4]}]