passive_server 1.0.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- passive_server-1.0.0/PKG-INFO +36 -0
- passive_server-1.0.0/README.md +1 -0
- passive_server-1.0.0/passive_server/__init__.py +0 -0
- passive_server-1.0.0/passive_server/active_host.py +119 -0
- passive_server-1.0.0/passive_server/common_func.py +82 -0
- passive_server-1.0.0/passive_server/enum_sece_data_type.py +24 -0
- passive_server-1.0.0/passive_server/exception.py +17 -0
- passive_server-1.0.0/passive_server/factory.py +108 -0
- passive_server-1.0.0/passive_server/models_class.py +247 -0
- passive_server-1.0.0/passive_server/passive_equipment.py +315 -0
- passive_server-1.0.0/passive_server/passive_host.py +69 -0
- passive_server-1.0.0/passive_server/passive_server.py +634 -0
- passive_server-1.0.0/passive_server/plc_address_operation.py +172 -0
- passive_server-1.0.0/passive_server/secs_config.py +218 -0
- passive_server-1.0.0/passive_server/thread_methods.py +165 -0
- passive_server-1.0.0/pyproject.toml +45 -0
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: passive_server
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: 作为服务端的设备
|
|
5
|
+
Author: WeiLiu
|
|
6
|
+
Author-email: 183074632@qq.com
|
|
7
|
+
Requires-Python: >=3.11,<3.13
|
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
11
|
+
Requires-Dist: alembic (>=1.14.0,<2.0.0)
|
|
12
|
+
Requires-Dist: bar-tender-api (>=1.0.0)
|
|
13
|
+
Requires-Dist: cffi (>=1.17.0,<2.0.0)
|
|
14
|
+
Requires-Dist: gkg-laser (>=1.0.0)
|
|
15
|
+
Requires-Dist: hslcommunication (>=1.2.0,<2.0.0)
|
|
16
|
+
Requires-Dist: inovance-tag-cyg (>=1.8.1)
|
|
17
|
+
Requires-Dist: mitsubishi-plc (>=1.4.0)
|
|
18
|
+
Requires-Dist: modbus-api (>=1.3.0)
|
|
19
|
+
Requires-Dist: modbus-tk (>=1.1.4,<2.0.0)
|
|
20
|
+
Requires-Dist: mysql-api (>=1.8.0)
|
|
21
|
+
Requires-Dist: openpyxl (>=3.1.5,<4.0.0)
|
|
22
|
+
Requires-Dist: pandas (>=2.2.3,<3.0.0)
|
|
23
|
+
Requires-Dist: pillow (>=10.4.0,<11.0.0)
|
|
24
|
+
Requires-Dist: pyairtable (>=2.3.3,<3.0.0)
|
|
25
|
+
Requires-Dist: pymysql (>=1.1.1,<2.0.0)
|
|
26
|
+
Requires-Dist: python-snap7 (>=2.0.2,<3.0.0)
|
|
27
|
+
Requires-Dist: pythonnet (>=3.0.3,<4.0.0)
|
|
28
|
+
Requires-Dist: secsgem_cyg (>=1.7.0)
|
|
29
|
+
Requires-Dist: siemens-plc (>=1.5.0)
|
|
30
|
+
Requires-Dist: socket-cyg (>=1.7.0)
|
|
31
|
+
Requires-Dist: sqlalchemy (>=2.0.36,<3.0.0)
|
|
32
|
+
Requires-Dist: suds-community (>=1.2.0,<2.0.0)
|
|
33
|
+
Requires-Dist: websockets (>=14.1,<15.0)
|
|
34
|
+
Description-Content-Type: text/markdown
|
|
35
|
+
|
|
36
|
+
### PASSIVE - 服务端
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
### PASSIVE - 服务端
|
|
File without changes
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
# pylint: skip-file
|
|
2
|
+
"""Host 主机, 用来监控设备端 secs服务发上来的数据, 然后处理后再发给工厂."""
|
|
3
|
+
import collections
|
|
4
|
+
import logging
|
|
5
|
+
from typing import Callable, Union
|
|
6
|
+
|
|
7
|
+
from secsgem.secs.variables import Array, String, U4
|
|
8
|
+
from secsgem import hsms, gem
|
|
9
|
+
|
|
10
|
+
from passive_server import factory, secs_config
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class ActiveHost:
|
|
15
|
+
"""ActiveHost class."""
|
|
16
|
+
|
|
17
|
+
def __init__(self, passive_ips: list[str]):
|
|
18
|
+
"""ActiveHost 构造函数.
|
|
19
|
+
|
|
20
|
+
Args:
|
|
21
|
+
passive_ips: 要监控设备 secs 服务的 ip 和 端口列表.
|
|
22
|
+
"""
|
|
23
|
+
self.logger = logging.getLogger("ActiveHost")
|
|
24
|
+
self.passive_ips = passive_ips
|
|
25
|
+
self._host_handlers = {}
|
|
26
|
+
self._create_gem_host_handler()
|
|
27
|
+
|
|
28
|
+
@property
|
|
29
|
+
def host_handlers(self) -> dict[str, gem.GemHostHandler]:
|
|
30
|
+
"""监控设备 secs 服务的 GemHostHandler 实例字典."""
|
|
31
|
+
return self._host_handlers
|
|
32
|
+
|
|
33
|
+
def _create_gem_host_handler(self):
|
|
34
|
+
"""根据配置文件创建连接设备的客户端."""
|
|
35
|
+
for equipment_ip_port in self.passive_ips:
|
|
36
|
+
equipment_ip, port = equipment_ip_port.split(":")
|
|
37
|
+
setting = hsms.HsmsSettings(
|
|
38
|
+
address=equipment_ip,
|
|
39
|
+
port=int(port),
|
|
40
|
+
connect_mode=getattr(hsms.HsmsConnectMode, "ACTIVE"),
|
|
41
|
+
device_type=hsms.DeviceType.HOST
|
|
42
|
+
)
|
|
43
|
+
host_handler = gem.GemHostHandler(setting)
|
|
44
|
+
self._host_handlers[equipment_ip_port] = host_handler
|
|
45
|
+
self.report_link_sv_or_dv(host_handler, equipment_ip, port)
|
|
46
|
+
|
|
47
|
+
@staticmethod
|
|
48
|
+
def report_link_sv_or_dv(host_handler: gem.GemHostHandler, equipment_ip: str, port: str):
|
|
49
|
+
"""将报告和 sv 或 dv 关联.
|
|
50
|
+
|
|
51
|
+
Args:
|
|
52
|
+
host_handler: GemHostHandler 实例.
|
|
53
|
+
equipment_ip: host监控设备的 secs 服务 ip.
|
|
54
|
+
port: host监控设备的 secs 服务端口.
|
|
55
|
+
"""
|
|
56
|
+
mysql = factory.get_mysql_instance(equipment_ip, f"{equipment_ip}:{port}")
|
|
57
|
+
report_link_list = secs_config.get_report_link_info(mysql)
|
|
58
|
+
for report_link in report_link_list:
|
|
59
|
+
host_handler.report_subscriptions.update(report_link)
|
|
60
|
+
|
|
61
|
+
def enable_host_handler(self):
|
|
62
|
+
"""启动监控设备 secs 服务的客户端"""
|
|
63
|
+
for equipment_ip_port, host_handler in self._host_handlers.items():
|
|
64
|
+
host_handler.enable()
|
|
65
|
+
self.logger.info("已启动监控 %s 设备 secs 服务的 Active 客户端", equipment_ip_port)
|
|
66
|
+
|
|
67
|
+
def set_call_back(self, ip_port: str, func_name: str, func: Callable):
|
|
68
|
+
"""设置监控到设备上报数据触发的回调函数.
|
|
69
|
+
|
|
70
|
+
Args:
|
|
71
|
+
ip_port: 监控的设备服务端的 ip port.
|
|
72
|
+
func_name: 函数名称.
|
|
73
|
+
func: 函数本体.
|
|
74
|
+
"""
|
|
75
|
+
setattr(self.get_host_instance(ip_port).callbacks, func_name, func)
|
|
76
|
+
|
|
77
|
+
def set_receive_event_callback(self, ip_port: str, func: Callable):
|
|
78
|
+
"""设置监控到设备上事件据触发的回调函数.
|
|
79
|
+
|
|
80
|
+
Args:
|
|
81
|
+
ip_port: 监控的设备服务端的 ip port.
|
|
82
|
+
func: 函数本体.
|
|
83
|
+
"""
|
|
84
|
+
host_handler = self.get_host_instance(ip_port)
|
|
85
|
+
host_handler.protocol.events.collection_event_received += func
|
|
86
|
+
|
|
87
|
+
def get_host_instance(self, ip_port: str) -> gem.GemHostHandler:
|
|
88
|
+
"""获取监控设备 secs 服务的 GemHostHandler.
|
|
89
|
+
|
|
90
|
+
Args:
|
|
91
|
+
ip_port: 监控设备 secs 服务的 ip 和 端口.
|
|
92
|
+
|
|
93
|
+
Returns:
|
|
94
|
+
gem.GemHostHandler: 返回 gem.GemHostHandler.
|
|
95
|
+
"""
|
|
96
|
+
return self._host_handlers[ip_port]
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def send_s2f41(self, ip_port: str, rcmd: str, params_dict: Union[list[str], dict]):
|
|
100
|
+
"""给指定设备的 secs 服务下发 s2f41.
|
|
101
|
+
|
|
102
|
+
Args:
|
|
103
|
+
rcmd: 远程命令名称.
|
|
104
|
+
params_dict: 远程命令参数字典.
|
|
105
|
+
ip_port: 指定设备的 secs 服务的 ip 和端口.
|
|
106
|
+
"""
|
|
107
|
+
params_dict_send = collections.OrderedDict()
|
|
108
|
+
for param in params_dict:
|
|
109
|
+
param_value = params_dict[param]
|
|
110
|
+
if isinstance(param_value, list):
|
|
111
|
+
if isinstance(param_value[0], str):
|
|
112
|
+
params_dict_send[param] = Array(String,param_value)
|
|
113
|
+
elif isinstance(param_value[0], int):
|
|
114
|
+
params_dict_send[param] = Array(U4, param_value)
|
|
115
|
+
else:
|
|
116
|
+
params_dict_send[param] = param_value
|
|
117
|
+
|
|
118
|
+
host_handler = self.get_host_instance(ip_port)
|
|
119
|
+
host_handler.send_remote_command(rcmd, params_dict_send)
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# pylint: skip-file
|
|
2
|
+
"""通用的操作函数."""
|
|
3
|
+
import json
|
|
4
|
+
import os
|
|
5
|
+
import pathlib
|
|
6
|
+
import subprocess
|
|
7
|
+
from datetime import datetime
|
|
8
|
+
from typing import Union
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def parse_value(value: str, value_type: str) -> Union[int, float, str, bool, list]:
|
|
12
|
+
"""解析数值.
|
|
13
|
+
|
|
14
|
+
Args:
|
|
15
|
+
value: 解析的数据
|
|
16
|
+
value_type: 数据类型.
|
|
17
|
+
|
|
18
|
+
Returns:
|
|
19
|
+
Union[int, float, str, bool, list]: 解析后的数据.
|
|
20
|
+
"""
|
|
21
|
+
int_type_flag = "U1,U4,I4"
|
|
22
|
+
float_type_flag = "F4"
|
|
23
|
+
bool_type_flag = "BOOL"
|
|
24
|
+
list_type_flag = "ARRAY"
|
|
25
|
+
binary_type_flag = "BINARY"
|
|
26
|
+
|
|
27
|
+
if value_type in int_type_flag:
|
|
28
|
+
return int(value) if value else 0
|
|
29
|
+
elif value_type in float_type_flag:
|
|
30
|
+
return float(value) if value else 0.0
|
|
31
|
+
elif value_type in bool_type_flag:
|
|
32
|
+
if value in ["false", "False", "FALSE", 0, "0", "", None]:
|
|
33
|
+
return False
|
|
34
|
+
return True
|
|
35
|
+
elif value_type in list_type_flag:
|
|
36
|
+
if isinstance(value, str):
|
|
37
|
+
return json.loads(value) if value else []
|
|
38
|
+
return value
|
|
39
|
+
elif value_type in binary_type_flag:
|
|
40
|
+
return int(value) if value else 0
|
|
41
|
+
else:
|
|
42
|
+
return value
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def set_date_time(modify_time_str) -> bool:
|
|
46
|
+
"""设置windows系统日期和时间.
|
|
47
|
+
|
|
48
|
+
Args:
|
|
49
|
+
modify_time_str (str): 要修改的时间字符串.
|
|
50
|
+
|
|
51
|
+
Returns:
|
|
52
|
+
bool: 修改成功或者失败.
|
|
53
|
+
"""
|
|
54
|
+
date_time = datetime.strptime(modify_time_str, "%Y%m%d%H%M%S%f")
|
|
55
|
+
date_command = f"date {date_time.year}-{date_time.month}-{date_time.day}"
|
|
56
|
+
result_date = subprocess.run(date_command, shell=True, check=False)
|
|
57
|
+
time_command = f"time {date_time.hour}:{date_time.minute}:{date_time.second}"
|
|
58
|
+
result_time = subprocess.run(time_command, shell=True, check=False)
|
|
59
|
+
if result_date.returncode == 0 and result_time.returncode == 0:
|
|
60
|
+
return True
|
|
61
|
+
return False
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def custom_log_name(log_path: str) -> str:
|
|
65
|
+
"""自定义新生成的日志名称.
|
|
66
|
+
|
|
67
|
+
Args:
|
|
68
|
+
log_path: 原始的日志文件路径.
|
|
69
|
+
|
|
70
|
+
Returns:
|
|
71
|
+
str: 新生成的自定义日志文件路径.
|
|
72
|
+
"""
|
|
73
|
+
_, suffix, date_str, *__ = log_path.split(".")
|
|
74
|
+
new_log_path = f"{os.getcwd()}/log/all_{date_str}.{suffix}"
|
|
75
|
+
return new_log_path
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def create_log_dir():
|
|
79
|
+
"""判断log目录是否存在, 不存在就创建."""
|
|
80
|
+
log_dir = pathlib.Path(f"{os.getcwd()}/log")
|
|
81
|
+
if not log_dir.exists():
|
|
82
|
+
os.mkdir(log_dir)
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"""枚举 secs 数据类型."""
|
|
2
|
+
from enum import Enum
|
|
3
|
+
|
|
4
|
+
from secsgem.secs.variables import Array, List
|
|
5
|
+
from secsgem.secs.variables.f4 import F4
|
|
6
|
+
from secsgem.secs.variables.string import String
|
|
7
|
+
from secsgem.secs.variables.boolean import Boolean
|
|
8
|
+
from secsgem.secs.variables.u1 import U1
|
|
9
|
+
from secsgem.secs.variables.u4 import U4
|
|
10
|
+
from secsgem.secs.variables.i4 import I4
|
|
11
|
+
from secsgem.secs.variables.binary import Binary
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class EnumSecsDataType(Enum):
|
|
15
|
+
"""Secs 数据类型枚举类."""
|
|
16
|
+
F4 = F4
|
|
17
|
+
ASCII = String
|
|
18
|
+
BOOL = Boolean
|
|
19
|
+
U1 = U1
|
|
20
|
+
U4 = U4
|
|
21
|
+
I4 = I4
|
|
22
|
+
BINARY = Binary
|
|
23
|
+
ARRAY = Array
|
|
24
|
+
LIST = List
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"""Equipment exception base class."""
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class EquipmentBaseException(Exception):
|
|
5
|
+
"""Base exception class for Equipment errors.
|
|
6
|
+
|
|
7
|
+
This exception class inherits from Exception and serves as the base for all
|
|
8
|
+
exceptions related to Equipment operations.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class EquipmentRuntimeError(EquipmentBaseException):
|
|
13
|
+
"""Exception raised for Equipment runtime errors.
|
|
14
|
+
|
|
15
|
+
This exception is a subclass of EquipmentBaseException and is raised when there
|
|
16
|
+
is a runtime error during Equipment operations.
|
|
17
|
+
"""
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
# pylint: skip-file
|
|
2
|
+
"""生成实例的方法集合."""
|
|
3
|
+
import os
|
|
4
|
+
from logging.handlers import TimedRotatingFileHandler
|
|
5
|
+
from typing import Union
|
|
6
|
+
|
|
7
|
+
from inovance_tag.tag_communication import TagCommunication
|
|
8
|
+
from mitsubishi_plc.mitsubishi_plc import MitsubishiPlc
|
|
9
|
+
from modbus_api.modbus_api import ModbusApi
|
|
10
|
+
from mysql_api.mysql_database import MySQLDatabase
|
|
11
|
+
from secsgem.common import DeviceType
|
|
12
|
+
from secsgem.hsms import HsmsSettings, HsmsConnectMode
|
|
13
|
+
from siemens_plc.s7_plc import S7PLC
|
|
14
|
+
from socket_cyg.socket_server_asyncio import CygSocketServerAsyncio
|
|
15
|
+
|
|
16
|
+
from passive_server import models_class, common_func, active_host
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def get_mysql_instance(host_ip: str, database_name: str, user_name: str = "cyg", password: str = "liuwei.520") -> MySQLDatabase:
|
|
20
|
+
"""获取数据库实例对象.
|
|
21
|
+
|
|
22
|
+
Args:
|
|
23
|
+
host_ip: 要连接的数据库 ip.
|
|
24
|
+
database_name: 数据库名称.
|
|
25
|
+
user_name: 用户名.
|
|
26
|
+
password: 密码.
|
|
27
|
+
|
|
28
|
+
Returns:
|
|
29
|
+
MySQLDatabase: 返回 secs 数据库实例对象.
|
|
30
|
+
"""
|
|
31
|
+
return MySQLDatabase(user_name, password, database_name=database_name, host=host_ip)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def get_mysql_secs() -> MySQLDatabase:
|
|
35
|
+
"""获取 secs 数据库实例对象.
|
|
36
|
+
|
|
37
|
+
Returns:
|
|
38
|
+
MySQLDatabase: 返回 secs 数据库实例对象.
|
|
39
|
+
"""
|
|
40
|
+
return MySQLDatabase("root", "liuwei.520")
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def get_socket_server():
|
|
44
|
+
"""获取 socket 服务端示例"""
|
|
45
|
+
return CygSocketServerAsyncio("127.0.0.1", 1830)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def get_hsms_setting() -> HsmsSettings:
|
|
49
|
+
"""获取 HsmsSettings 实例对象.
|
|
50
|
+
|
|
51
|
+
Returns:
|
|
52
|
+
HsmsSettings: 返回 HsmsSettings 实例对象.
|
|
53
|
+
"""
|
|
54
|
+
mysql = get_mysql_secs()
|
|
55
|
+
secs_ip = mysql.query_data(models_class.EcList, {"ec_name": "secs_ip"})[0].get("value", "127.0.0.1")
|
|
56
|
+
secs_port = mysql.query_data(models_class.EcList, {"ec_name": "secs_port"})[0].get("value", 5000)
|
|
57
|
+
hsms_settings = HsmsSettings(
|
|
58
|
+
address=secs_ip, port=int(secs_port),
|
|
59
|
+
connect_mode=getattr(HsmsConnectMode, "PASSIVE"),
|
|
60
|
+
device_type=DeviceType.EQUIPMENT
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
return hsms_settings
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def get_time_rotating_handler() -> TimedRotatingFileHandler:
|
|
67
|
+
"""获取自动生成日志的日志器实例.
|
|
68
|
+
|
|
69
|
+
Returns:
|
|
70
|
+
TimedRotatingFileHandler: 返回自动生成日志的日志器实例.
|
|
71
|
+
"""
|
|
72
|
+
return TimedRotatingFileHandler(
|
|
73
|
+
f"{os.getcwd()}/log/all.log",
|
|
74
|
+
when="D", interval=1, backupCount=10, encoding="UTF-8"
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def get_active_host_instance() -> active_host.ActiveHost:
|
|
79
|
+
"""获取 ActiveHost 实例.
|
|
80
|
+
|
|
81
|
+
Returns:
|
|
82
|
+
ActiveHost: 返回 ActiveHost 实例.
|
|
83
|
+
"""
|
|
84
|
+
mysql = get_mysql_secs()
|
|
85
|
+
passives_ip_ports_str = mysql.query_data(models_class.EcList, {"ec_name": "low_ip_port_list"})[0].get("value", [])
|
|
86
|
+
passives_ip_port_list = common_func.parse_value(passives_ip_ports_str, "ARRAY")
|
|
87
|
+
return active_host.ActiveHost(passives_ip_port_list)
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def get_plc_instance() -> Union[S7PLC, MitsubishiPlc, ModbusApi, TagCommunication]:
|
|
91
|
+
"""获取设备端 passive server 实例.
|
|
92
|
+
|
|
93
|
+
Returns:
|
|
94
|
+
Union[S7PLC, MitsubishiPlc, ModbusApi, TagCommunication]: 返回 plc 实例对象.
|
|
95
|
+
"""
|
|
96
|
+
mysql = get_mysql_secs()
|
|
97
|
+
equipment_name = mysql.query_data(models_class.EcList, {"ec_name": "equipment_name"})[0]["value"]
|
|
98
|
+
plc_ip = mysql.query_data(models_class.EcList, {"ec_name": "plc_ip"})[0]["value"]
|
|
99
|
+
if "snap7" in equipment_name:
|
|
100
|
+
plc = S7PLC(plc_ip)
|
|
101
|
+
elif "tag" in equipment_name:
|
|
102
|
+
plc = TagCommunication(plc_ip)
|
|
103
|
+
elif "modbus" in equipment_name:
|
|
104
|
+
plc = ModbusApi(plc_ip)
|
|
105
|
+
else:
|
|
106
|
+
plc_port = mysql.query_data(models_class.EcList, {"ec_name": "plc_port"})[0]["value"]
|
|
107
|
+
plc = MitsubishiPlc(plc_ip, plc_port)
|
|
108
|
+
return plc
|
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
# pylint: skip-file
|
|
2
|
+
"""数据表模型."""
|
|
3
|
+
import datetime
|
|
4
|
+
|
|
5
|
+
from mysql_api.mysql_database import MySQLDatabase
|
|
6
|
+
from sqlalchemy import Column, String, Integer, DateTime, JSON
|
|
7
|
+
from sqlalchemy.orm import declarative_base
|
|
8
|
+
|
|
9
|
+
BASE = declarative_base()
|
|
10
|
+
mysql_api = MySQLDatabase("root", "liuwei.520")
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class EquipmentState(BASE):
|
|
14
|
+
"""Mes 状态模型."""
|
|
15
|
+
__tablename__ = "equipment_state"
|
|
16
|
+
__table_args__ = {"comment": "设备状态表"}
|
|
17
|
+
|
|
18
|
+
id = Column(Integer, primary_key=True, unique=True, nullable=False, autoincrement=True)
|
|
19
|
+
eap_state = Column(Integer, nullable=True, comment="0: plc 离线, 1: 本地模式, 2: 远程模式")
|
|
20
|
+
machine_state = Column(Integer, nullable=True, comment="1: Manual, 2: Auto, 3: Auto Run, 4: Alarm")
|
|
21
|
+
mes_state = Column(Integer, nullable=True, comment="0: 设备 MES 服务未打开, 1: 设备 MES 服务已打开")
|
|
22
|
+
|
|
23
|
+
updated_at = Column(DateTime, default=datetime.datetime.now, onupdate=datetime.datetime.now)
|
|
24
|
+
created_at = Column(DateTime, default=datetime.datetime.now)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class LotList(BASE):
|
|
28
|
+
"""工单列表模型."""
|
|
29
|
+
__tablename__ = "lot_list"
|
|
30
|
+
__table_args__ = {"comment": "工单列表"}
|
|
31
|
+
|
|
32
|
+
id = Column(Integer, primary_key=True, unique=True, nullable=False, autoincrement=True)
|
|
33
|
+
lot_name = Column(String(50), unique=True, comment="工单名称")
|
|
34
|
+
recipe_name = Column(String(50), nullable=True, comment="配方名称")
|
|
35
|
+
lot_quantity = Column(Integer, nullable=True, comment="工单数量")
|
|
36
|
+
lot_state = Column(Integer, nullable=True, default=1, comment="工单状态")
|
|
37
|
+
updated_at = Column(DateTime, default=datetime.datetime.now, onupdate=datetime.datetime.now)
|
|
38
|
+
created_at = Column(DateTime, default=datetime.datetime.now)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class SvList(BASE):
|
|
42
|
+
"""SV列表模型."""
|
|
43
|
+
__tablename__ = "sv_list"
|
|
44
|
+
__table_args__ = {"comment": "sv 列表"}
|
|
45
|
+
|
|
46
|
+
id = Column(Integer, primary_key=True, unique=True, nullable=False, autoincrement=True)
|
|
47
|
+
sv_id = Column(Integer, nullable=True, comment="sv id")
|
|
48
|
+
sv_name = Column(String(50), nullable=True, comment="sv 名称")
|
|
49
|
+
value_type = Column(String(50), nullable=True, comment="sv 值类型")
|
|
50
|
+
base_value_type = Column(String(50), nullable=True, comment="若 sv 值类型是 ARRAY, 子元素值类型")
|
|
51
|
+
value = Column(JSON, nullable=True, comment="sv 值")
|
|
52
|
+
description = Column(String(250), nullable=True, comment="sv 描述信息")
|
|
53
|
+
updated_at = Column(DateTime, default=datetime.datetime.now, onupdate=datetime.datetime.now)
|
|
54
|
+
created_at = Column(DateTime, default=datetime.datetime.now)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
class DvList(BASE):
|
|
58
|
+
"""SV列表模型."""
|
|
59
|
+
__tablename__ = "dv_list"
|
|
60
|
+
__table_args__ = {"comment": "dv 列表"}
|
|
61
|
+
|
|
62
|
+
id = Column(Integer, primary_key=True, unique=True, nullable=False, autoincrement=True)
|
|
63
|
+
dv_id = Column(Integer, nullable=True, comment="dv id")
|
|
64
|
+
dv_name = Column(String(50), nullable=True, comment="dv 名称")
|
|
65
|
+
value_type = Column(String(50), nullable=True, comment="dv 值类型")
|
|
66
|
+
base_value_type = Column(String(50), nullable=True, comment="若 dv 值类型是 ARRAY, 子元素值类型")
|
|
67
|
+
value = Column(JSON, nullable=True, comment="dv 值")
|
|
68
|
+
description = Column(String(250), nullable=True, comment="dv 描述信息")
|
|
69
|
+
updated_at = Column(DateTime, default=datetime.datetime.now, onupdate=datetime.datetime.now)
|
|
70
|
+
created_at = Column(DateTime, default=datetime.datetime.now)
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
class EcList(BASE):
|
|
74
|
+
"""EC列表模型."""
|
|
75
|
+
__tablename__ = "ec_list"
|
|
76
|
+
__table_args__ = {"comment": "ec 列表"}
|
|
77
|
+
|
|
78
|
+
id = Column(Integer, primary_key=True, unique=True, nullable=False, autoincrement=True)
|
|
79
|
+
ec_id = Column(Integer, nullable=True, comment="ec id")
|
|
80
|
+
ec_name = Column(String(50), nullable=True, comment="ec 名称")
|
|
81
|
+
value_type = Column(String(50), nullable=True, comment="ec 值类型")
|
|
82
|
+
value = Column(JSON, nullable=True, comment="ec 值")
|
|
83
|
+
description = Column(String(250), nullable=True, comment="ec 描述信息")
|
|
84
|
+
updated_at = Column(DateTime, default=datetime.datetime.now, onupdate=datetime.datetime.now)
|
|
85
|
+
created_at = Column(DateTime, default=datetime.datetime.now)
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
class ReportList(BASE):
|
|
89
|
+
"""报告列表模型."""
|
|
90
|
+
__tablename__ = "report_list"
|
|
91
|
+
__table_args__ = {"comment": "报告列表"}
|
|
92
|
+
|
|
93
|
+
id = Column(Integer, primary_key=True, unique=True, nullable=False, autoincrement=True)
|
|
94
|
+
report_id = Column(Integer, nullable=True, comment="报告 id")
|
|
95
|
+
associate_sv = Column(String(250), nullable=True, comment="关联的 sv")
|
|
96
|
+
associate_dv = Column(String(250), nullable=True, comment="关联的 dv")
|
|
97
|
+
description = Column(String(250), nullable=True, comment="报告描述信息")
|
|
98
|
+
updated_at = Column(DateTime, default=datetime.datetime.now, onupdate=datetime.datetime.now)
|
|
99
|
+
created_at = Column(DateTime, default=datetime.datetime.now)
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
class EventList(BASE):
|
|
103
|
+
"""事件列表模型."""
|
|
104
|
+
__tablename__ = "event_list"
|
|
105
|
+
__table_args__ = {"comment": "事件列表"}
|
|
106
|
+
|
|
107
|
+
id = Column(Integer, primary_key=True, unique=True, nullable=False, autoincrement=True)
|
|
108
|
+
event_id = Column(Integer, nullable=True, comment="事件 id")
|
|
109
|
+
event_name = Column(String(250), nullable=True, comment="事件名称")
|
|
110
|
+
associate_report = Column(String(250), nullable=True, comment="关联的报告")
|
|
111
|
+
description = Column(String(250), nullable=True, comment="报告描述信息")
|
|
112
|
+
updated_at = Column(DateTime, default=datetime.datetime.now, onupdate=datetime.datetime.now)
|
|
113
|
+
created_at = Column(DateTime, default=datetime.datetime.now)
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
class RemoteCommandList(BASE):
|
|
117
|
+
"""远程命令列表模型."""
|
|
118
|
+
__tablename__ = "remote_command_list"
|
|
119
|
+
__table_args__ = {"comment": "远程命令列表"}
|
|
120
|
+
|
|
121
|
+
id = Column(Integer, primary_key=True, unique=True, nullable=False, autoincrement=True)
|
|
122
|
+
remote_command = Column(String(250), nullable=True, comment="远程命令")
|
|
123
|
+
parameters = Column(String(520), nullable=True, comment="要传入的参数")
|
|
124
|
+
description = Column(String(250), nullable=True, comment="远程命令描述信息")
|
|
125
|
+
updated_at = Column(DateTime, default=datetime.datetime.now, onupdate=datetime.datetime.now)
|
|
126
|
+
created_at = Column(DateTime, default=datetime.datetime.now)
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
class AlarmList(BASE):
|
|
130
|
+
"""报警信息模型."""
|
|
131
|
+
__tablename__ = "alarm_list"
|
|
132
|
+
__table_args__ = {"comment": "报警信息模型"}
|
|
133
|
+
|
|
134
|
+
id = Column(Integer, primary_key=True, unique=True, nullable=False, autoincrement=True)
|
|
135
|
+
alarm_id = Column(Integer, nullable=True, unique=True, comment="报警 id")
|
|
136
|
+
alarm_text_zh = Column(String(520), nullable=True, comment="中文报警内容")
|
|
137
|
+
alarm_text_en = Column(String(520), nullable=True, comment="英文报警内容")
|
|
138
|
+
updated_at = Column(DateTime, default=datetime.datetime.now, onupdate=datetime.datetime.now)
|
|
139
|
+
created_at = Column(DateTime, default=datetime.datetime.now)
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
class AlarmRecordList(BASE):
|
|
143
|
+
"""报警信息模型."""
|
|
144
|
+
__tablename__ = "alarm_record_list"
|
|
145
|
+
__table_args__ = {"comment": "设备报警记录模型"}
|
|
146
|
+
|
|
147
|
+
id = Column(Integer, primary_key=True, unique=True, nullable=False, autoincrement=True)
|
|
148
|
+
alarm_id = Column(Integer, nullable=True, comment="报警 id")
|
|
149
|
+
alarm_text = Column(String(520), nullable=True, comment="报警内容")
|
|
150
|
+
created_at = Column(DateTime, default=datetime.datetime.now)
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
class PlcAddressList(BASE):
|
|
154
|
+
"""PLC plc 2 mes 地址列表模型."""
|
|
155
|
+
__tablename__ = "plc_address_list"
|
|
156
|
+
__table_args__ = {"comment": "plc 2 mes 地址列表"}
|
|
157
|
+
|
|
158
|
+
id = Column(Integer, primary_key=True, unique=True, nullable=False, autoincrement=True)
|
|
159
|
+
address = Column(String(250), nullable=True, comment="标签地址")
|
|
160
|
+
data_type = Column(
|
|
161
|
+
String(250), nullable=True,
|
|
162
|
+
comment="标签地址值数据类型: bool, string, sint, int, dint, lint, byte, word, dword, lword, real, lreal"
|
|
163
|
+
)
|
|
164
|
+
bit_index = Column(Integer, nullable=True, comment="bool 类型的 bit 位")
|
|
165
|
+
size = Column(Integer, nullable=True, comment="地址大小")
|
|
166
|
+
count_num = Column(Integer, nullable=True, default=1, comment="当这个地址连续时, 代表连续读或写几个")
|
|
167
|
+
gap = Column(Integer, nullable=True, default=1, comment="当这个地址连续时, 每两个地址的见间隔大小")
|
|
168
|
+
operation_type = Column(String(50), nullable=True, comment="操作地址的方式, 读或写(read or write)")
|
|
169
|
+
associate_sv_or_dv = Column(String(250), nullable=True, comment="关联的 sv 或 dv")
|
|
170
|
+
associate_signal = Column(String(250), nullable=True, comment="关联信号")
|
|
171
|
+
step = Column(Integer, nullable=True, comment="所属信号的第几步流程")
|
|
172
|
+
event_id = Column(Integer, nullable=True, comment="要发送的事件id")
|
|
173
|
+
description = Column(String(250), nullable=True, comment="地址描述信息")
|
|
174
|
+
updated_at = Column(DateTime, default=datetime.datetime.now, onupdate=datetime.datetime.now)
|
|
175
|
+
created_at = Column(DateTime, default=datetime.datetime.now)
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
class MesAddressList(BASE):
|
|
179
|
+
"""PLC plc 2 mes 地址列表模型."""
|
|
180
|
+
__tablename__ = "mes_address_list"
|
|
181
|
+
__table_args__ = {"comment": "mes 2 plc 地址列表"}
|
|
182
|
+
|
|
183
|
+
id = Column(Integer, primary_key=True, unique=True, nullable=False, autoincrement=True)
|
|
184
|
+
address = Column(String(250), nullable=True, comment="标签地址")
|
|
185
|
+
data_type = Column(
|
|
186
|
+
String(250), nullable=True,
|
|
187
|
+
comment="标签地址值数据类型: bool, string, sint, int, dint, lint, byte, word, dword, lword, real, lreal"
|
|
188
|
+
)
|
|
189
|
+
size = Column(Integer, nullable=True, comment="地址大小")
|
|
190
|
+
count_num = Column(Integer, nullable=True, default=1, comment="当这个地址连续时, 代表连续读或写几个")
|
|
191
|
+
gap = Column(Integer, nullable=True, default=1, comment="当这个地址连续时, 每两个地址的见间隔大小")
|
|
192
|
+
operation_type = Column(String(50), nullable=True, comment="操作地址的方式, 读或写(read or write)")
|
|
193
|
+
associate_sv_or_dv = Column(String(250), nullable=True, comment="关联的 sv 或 dv")
|
|
194
|
+
associate_signal = Column(String(250), nullable=True, comment="关联信号")
|
|
195
|
+
step = Column(Integer, nullable=True, comment="所属信号的第几步流程")
|
|
196
|
+
event_id = Column(Integer, nullable=True, comment="要发送的事件id")
|
|
197
|
+
description = Column(String(250), nullable=True, comment="地址描述信息")
|
|
198
|
+
updated_at = Column(DateTime, default=datetime.datetime.now, onupdate=datetime.datetime.now)
|
|
199
|
+
created_at = Column(DateTime, default=datetime.datetime.now)
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
class SignalAddressList(BASE):
|
|
203
|
+
"""PLC 信号地址列表模型."""
|
|
204
|
+
__tablename__ = "signal_address_list"
|
|
205
|
+
__table_args__ = {"comment": "PLC 信号地址列表"}
|
|
206
|
+
|
|
207
|
+
id = Column(Integer, primary_key=True, unique=True, nullable=False, autoincrement=True)
|
|
208
|
+
address = Column(String(250), nullable=True, comment="标签地址")
|
|
209
|
+
data_type = Column(
|
|
210
|
+
String(250), nullable=True,
|
|
211
|
+
comment="标签地址值数据类型: bool, string, sint, int, dint, lint, byte, word, dword, lword, real, lreal"
|
|
212
|
+
)
|
|
213
|
+
signal_value = Column(Integer, nullable=True, comment="监控信号值")
|
|
214
|
+
clean_signal_value = Column(Integer, nullable=True, comment="清除信号值")
|
|
215
|
+
state = Column(Integer, nullable=True, comment="是否监控地址信号, 1: 监控, 0: 不监控")
|
|
216
|
+
description = Column(String(250), nullable=True, comment="地址描述信息")
|
|
217
|
+
updated_at = Column(DateTime, default=datetime.datetime.now, onupdate=datetime.datetime.now)
|
|
218
|
+
created_at = Column(DateTime, default=datetime.datetime.now)
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
class FlowFunc(BASE):
|
|
222
|
+
"""流程函数的表模型."""
|
|
223
|
+
__tablename__ = "flow_func"
|
|
224
|
+
__table_args__ = {"comment": "流程函数的表模型"}
|
|
225
|
+
|
|
226
|
+
id = Column(Integer, primary_key=True, unique=True, nullable=False, autoincrement=True)
|
|
227
|
+
func_name = Column(String(50), nullable=True, comment="函数名称")
|
|
228
|
+
associate_dv = Column(String(250), nullable=True, comment="关联dv")
|
|
229
|
+
associate_signal = Column(String(250), nullable=True, comment="关联信号")
|
|
230
|
+
step = Column(Integer, nullable=True, comment="所属信号的第几步流程")
|
|
231
|
+
is_wait = Column(Integer, nullable=True, comment="是否需要等待 eap 回复, 1: 需要, 0: 不需要")
|
|
232
|
+
description = Column(String(250), nullable=True, comment="函数描述信息")
|
|
233
|
+
updated_at = Column(DateTime, default=datetime.datetime.now, onupdate=datetime.datetime.now)
|
|
234
|
+
created_at = Column(DateTime, default=datetime.datetime.now)
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
class Recipes(BASE):
|
|
238
|
+
"""配方表模型."""
|
|
239
|
+
__tablename__ = "recipes"
|
|
240
|
+
__table_args__ = {"comment": "配方表模型"}
|
|
241
|
+
|
|
242
|
+
id = Column(Integer, primary_key=True, unique=True, nullable=False, autoincrement=True)
|
|
243
|
+
recipe_id = Column(Integer, nullable=True, unique=True, comment="配方 id")
|
|
244
|
+
recipe_name = Column(String(50), nullable=True, comment="配方名称")
|
|
245
|
+
description = Column(String(250), nullable=True, comment="描述信息")
|
|
246
|
+
updated_at = Column(DateTime, default=datetime.datetime.now, onupdate=datetime.datetime.now)
|
|
247
|
+
created_at = Column(DateTime, default=datetime.datetime.now)
|