cfquant 0.2.0__py3-none-any.whl
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.
- LTtx/__init__.py +2 -0
- LTtx/tx/Config.txt +8 -0
- LTtx/tx/LTtx_server.py +1177 -0
- LTtx/tx/__init__.py +5 -0
- LTtx/tx/tx.py +1935 -0
- cfquant/__init__.py +26 -0
- cfquant/account_routing.py +119 -0
- cfquant/channels.py +76 -0
- cfquant/cli.py +266 -0
- cfquant/client.py +365 -0
- cfquant/config.py +86 -0
- cfquant/hub.py +304 -0
- cfquant/logging_i18n.py +348 -0
- cfquant/normal_bridge.py +676 -0
- cfquant/pipe_bridge.py +249 -0
- cfquant/pipe_client.py +223 -0
- cfquant/pipe_hub.py +602 -0
- cfquant/pipe_transport.py +493 -0
- cfquant/protocol.py +238 -0
- cfquant/qmt_bridge.py +1081 -0
- cfquant/runtime_report.py +334 -0
- cfquant/tx.py +1898 -0
- cfquant/tx_trade_bridge.py +1905 -0
- cfquant/version.py +5 -0
- cfquant/xtconstant.py +262 -0
- cfquant/xtdata.py +462 -0
- cfquant/xttrader.py +955 -0
- cfquant/xttype.py +586 -0
- cfquant-0.2.0.dist-info/LICENSE +21 -0
- cfquant-0.2.0.dist-info/METADATA +144 -0
- cfquant-0.2.0.dist-info/RECORD +90 -0
- cfquant-0.2.0.dist-info/WHEEL +5 -0
- cfquant-0.2.0.dist-info/entry_points.txt +4 -0
- cfquant-0.2.0.dist-info/top_level.txt +6 -0
- cfquant_pipe_hub.py +45 -0
- cfquant_web_server.py +12137 -0
- qmt_scripts/CFQUANT.py +507 -0
- qmt_scripts/CFQUANT_CTYPE_ALL_LOWLAT.py +830 -0
- qmt_scripts/CFQUANT_LITE.py +4928 -0
- qmt_scripts/CFQUANT_TRADE_LOWLAT.py +382 -0
- qmt_scripts/__init__.py +1 -0
- qmt_scripts//321/205/320/240/320/234/321/210/342/224/244/320/266/321/205/320/237/342/225/226/321/207/320/233/320/274/321/207/320/273/320/233/321/205/342/225/225/320/222/321/205/320/254/342/225/221/CFQUANT_CTYPE_ALL_LOWLAT_SH.py +866 -0
- qmt_scripts//321/205/320/240/320/234/321/210/342/224/244/320/266/321/205/320/237/342/225/226/321/207/320/233/320/274/321/207/320/273/320/233/321/205/342/225/225/320/222/321/205/320/254/342/225/221/CFQUANT_CTYPE_ALL_LOWLAT_SZ.py +866 -0
- qmt_scripts//321/205/320/240/320/234/321/210/342/224/244/320/266/321/205/320/237/342/225/226/321/207/320/233/320/274/321/207/320/273/320/233/321/205/342/225/225/320/222/321/205/320/254/342/225/221/CFQUANT_LITE_SH.py +5020 -0
- qmt_scripts//321/205/320/240/320/234/321/210/342/224/244/320/266/321/205/320/237/342/225/226/321/207/320/233/320/274/321/207/320/273/320/233/321/205/342/225/225/320/222/321/205/320/254/342/225/221/CFQUANT_LITE_SZ.py +5020 -0
- qmt_scripts//321/205/320/240/320/234/321/210/342/224/244/320/266/321/205/320/237/342/225/226/321/207/320/233/320/274/321/207/320/273/320/233/321/205/342/225/225/320/222/321/205/320/254/342/225/221/CFQUANT_TRADE_LOWLAT_SH.py +436 -0
- qmt_scripts//321/205/320/240/320/234/321/210/342/224/244/320/266/321/205/320/237/342/225/226/321/207/320/233/320/274/321/207/320/273/320/233/321/205/342/225/225/320/222/321/205/320/254/342/225/221/CFQUANT_TRADE_LOWLAT_SZ.py +436 -0
- qmt_scripts//321/205/320/240/320/234/321/210/342/224/244/320/266/321/205/320/237/342/225/226/321/207/320/233/320/274/321/207/320/273/320/233/321/205/342/225/225/320/222/321/205/320/254/342/225/221/readme.md +95 -0
- web_dashboard/__init__.py +1 -0
- web_dashboard/app.js +9847 -0
- web_dashboard/avatars/amber-pulse.svg +15 -0
- web_dashboard/avatars/copper-grid.svg +15 -0
- web_dashboard/avatars/market-blue.svg +15 -0
- web_dashboard/avatars/rose-circuit.svg +15 -0
- web_dashboard/avatars/signal-green.svg +15 -0
- web_dashboard/avatars/slate-wave.svg +15 -0
- web_dashboard/avatars/teal-orbit.svg +16 -0
- web_dashboard/avatars/violet-node.svg +15 -0
- web_dashboard/favicon.svg +5 -0
- web_dashboard/image/qmt_deploy/image-10.png +0 -0
- web_dashboard/image/qmt_deploy/image-11.png +0 -0
- web_dashboard/image/qmt_deploy/image-12.png +0 -0
- web_dashboard/image/qmt_deploy/image-13.png +0 -0
- web_dashboard/image/qmt_deploy/image-14.png +0 -0
- web_dashboard/image/qmt_deploy/image-15.png +0 -0
- web_dashboard/image/qmt_deploy/image-16.png +0 -0
- web_dashboard/image/qmt_deploy/image-17.png +0 -0
- web_dashboard/image/qmt_deploy/image-18.png +0 -0
- web_dashboard/image/qmt_deploy/image-19.png +0 -0
- web_dashboard/image/qmt_deploy/image-20.png +0 -0
- web_dashboard/image/qmt_deploy/image-21.png +0 -0
- web_dashboard/image/qmt_deploy/image-4.png +0 -0
- web_dashboard/image/qmt_deploy/image-5.png +0 -0
- web_dashboard/image/qmt_deploy/image-6.png +0 -0
- web_dashboard/image/qmt_deploy/image-7.png +0 -0
- web_dashboard/image/qmt_deploy/image-8.png +0 -0
- web_dashboard/image/qmt_deploy/image-9.png +0 -0
- web_dashboard/image//321/211/320/220/320/252/321/207/320/244/320/270/321/206/320/270/320/261/321/205/342/225/235/320/237/321/211/320/223/320/270/321/207/342/225/234/342/226/223/321/206/320/234/320/227/321/205/320/235/320/247/image-1.png +0 -0
- web_dashboard/image//321/211/320/220/320/252/321/207/320/244/320/270/321/206/320/270/320/261/321/205/342/225/235/320/237/321/211/320/223/320/270/321/207/342/225/234/342/226/223/321/206/320/234/320/227/321/205/320/235/320/247/image-2.png +0 -0
- web_dashboard/image//321/211/320/220/320/252/321/207/320/244/320/270/321/206/320/270/320/261/321/205/342/225/235/320/237/321/211/320/223/320/270/321/207/342/225/234/342/226/223/321/206/320/234/320/227/321/205/320/235/320/247/image-3.png +0 -0
- web_dashboard/image//321/211/320/220/320/252/321/207/320/244/320/270/321/206/320/270/320/261/321/205/342/225/235/320/237/321/211/320/223/320/270/321/207/342/225/234/342/226/223/321/206/320/234/320/227/321/205/320/235/320/247/image.png +0 -0
- web_dashboard/image//321/211/320/223/320/270/321/207/342/225/234/342/226/223/321/206/320/234/320/227/321/205/320/235/320/247/1783479325179.png +0 -0
- web_dashboard/image//321/211/320/223/320/270/321/207/342/225/234/342/226/223/321/206/320/234/320/227/321/205/320/235/320/247/1783479745262.png +0 -0
- web_dashboard/image//321/211/320/223/320/270/321/207/342/225/234/342/226/223/321/206/320/234/320/227/321/205/320/235/320/247/1783480178700.png +0 -0
- web_dashboard/image//321/211/320/223/320/270/321/207/342/225/234/342/226/223/321/206/320/234/320/227/321/205/320/235/320/247/1783480428759.png +0 -0
- web_dashboard/image//321/211/320/223/320/270/321/207/342/225/234/342/226/223/321/206/320/234/320/227/321/205/320/235/320/247/1783489807217.png +0 -0
- web_dashboard/image//321/211/320/223/320/270/321/207/342/225/234/342/226/223/321/206/320/234/320/227/321/205/320/235/320/247/1783490503491.png +0 -0
- web_dashboard/index.html +2317 -0
- web_dashboard/styles.css +7817 -0
- web_dashboard/vendor/mermaid.min.js +2029 -0
LTtx/tx/LTtx_server.py
ADDED
|
@@ -0,0 +1,1177 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""
|
|
3
|
+
Created on Fri Oct 14 21:16:19 2022
|
|
4
|
+
|
|
5
|
+
@author: Administrator
|
|
6
|
+
"""
|
|
7
|
+
import threading
|
|
8
|
+
import socket
|
|
9
|
+
import os
|
|
10
|
+
import time
|
|
11
|
+
import json
|
|
12
|
+
import struct
|
|
13
|
+
import signal
|
|
14
|
+
import queue
|
|
15
|
+
import datetime
|
|
16
|
+
import importlib
|
|
17
|
+
import subprocess
|
|
18
|
+
import sys
|
|
19
|
+
from packaging import version
|
|
20
|
+
|
|
21
|
+
#运行需要的库
|
|
22
|
+
need_packge = {
|
|
23
|
+
'psutil': {'pip_name': 'psutil', 'version': '1.0.0'},
|
|
24
|
+
'pandas': {'pip_name': 'pandas', 'version': '0.0.1'},
|
|
25
|
+
'tabulate': {'pip_name': 'tabulate', 'version': '0.0.1'},
|
|
26
|
+
'pandas_market_calendars': {'pip_name': 'pandas-market-calendars', 'version': '0.0.1'},
|
|
27
|
+
'pytz': {'pip_name': 'pytz', 'version': '0.0.0'},
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def _hidden_subprocess_kwargs():
|
|
32
|
+
if os.name != "nt":
|
|
33
|
+
return {}
|
|
34
|
+
kwargs = {}
|
|
35
|
+
create_no_window = getattr(subprocess, "CREATE_NO_WINDOW", 0)
|
|
36
|
+
if create_no_window:
|
|
37
|
+
kwargs["creationflags"] = create_no_window
|
|
38
|
+
try:
|
|
39
|
+
startupinfo = subprocess.STARTUPINFO()
|
|
40
|
+
startupinfo.dwFlags |= getattr(subprocess, "STARTF_USESHOWWINDOW", 1)
|
|
41
|
+
startupinfo.wShowWindow = getattr(subprocess, "SW_HIDE", 0)
|
|
42
|
+
kwargs["startupinfo"] = startupinfo
|
|
43
|
+
except Exception:
|
|
44
|
+
pass
|
|
45
|
+
return kwargs
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def ensure_modules_with_version(modules: dict):
|
|
49
|
+
"""
|
|
50
|
+
- 确保指定模块及版本已安装或自动升级(使用清华源)。
|
|
51
|
+
- 版本号中如果是latest,则每次都会去尝试更新到最新的版本,这样可以确保程序每次启动时都是用最新的库。
|
|
52
|
+
- 版本号中如果是数字,如果当前安装的版本低于该版本,则会升级到最新版本,如果高于或等于则跳过。
|
|
53
|
+
|
|
54
|
+
:param modules: dict,格式为:
|
|
55
|
+
{
|
|
56
|
+
"导入模块名": {
|
|
57
|
+
"pip_name": "pip安装名",
|
|
58
|
+
"version": "版本号(如2.2.0或latest)"
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
"""
|
|
62
|
+
for import_name, meta in modules.items():
|
|
63
|
+
pip_name = meta.get("pip_name", import_name)
|
|
64
|
+
required_version = meta.get("version", "latest")
|
|
65
|
+
|
|
66
|
+
try:
|
|
67
|
+
mod = importlib.import_module(import_name)
|
|
68
|
+
if required_version == "latest":
|
|
69
|
+
print(f"[AutoInstall] 已安装 {import_name},但指定为最新版本,将尝试升级...")
|
|
70
|
+
raise ImportError("强制升级")
|
|
71
|
+
else:
|
|
72
|
+
# 获取已安装版本
|
|
73
|
+
installed_version = getattr(mod, '__version__', None)
|
|
74
|
+
if installed_version is None:
|
|
75
|
+
print(f"[AutoInstall] 警告:无法检测 {import_name} 的版本信息,尝试强制安装指定版本 {required_version}...")
|
|
76
|
+
raise ImportError()
|
|
77
|
+
if version.parse(installed_version) < version.parse(required_version):
|
|
78
|
+
print(f"[AutoInstall] 检测到 {import_name} 当前版本为 {installed_version},小于要求的 {required_version},将自动升级...")
|
|
79
|
+
raise ImportError()
|
|
80
|
+
else:
|
|
81
|
+
print(f"[AutoInstall] {import_name} 已安装,版本为 {installed_version},满足要求")
|
|
82
|
+
except ImportError:
|
|
83
|
+
# 安装或升级
|
|
84
|
+
install_target = pip_name if required_version == "latest" else "%s>=%s" % (pip_name, required_version)
|
|
85
|
+
print(f"[AutoInstall] 正在安装/升级 {pip_name} → {required_version}...")
|
|
86
|
+
try:
|
|
87
|
+
subprocess.check_call([
|
|
88
|
+
sys.executable, "-m", "pip", "install", install_target,
|
|
89
|
+
"-i", "https://pypi.tuna.tsinghua.edu.cn/simple"
|
|
90
|
+
], **_hidden_subprocess_kwargs())
|
|
91
|
+
print(f"[AutoInstall] 成功安装 {pip_name} {required_version}")
|
|
92
|
+
except Exception as e:
|
|
93
|
+
print(f"[AutoInstall] 安装 {pip_name} 失败: {e}")
|
|
94
|
+
|
|
95
|
+
#先执行安装,再导入对应的库
|
|
96
|
+
ensure_modules_with_version(need_packge)
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
import pytz
|
|
102
|
+
import tabulate
|
|
103
|
+
import hashlib
|
|
104
|
+
import psutil
|
|
105
|
+
import pandas_market_calendars as mcal
|
|
106
|
+
|
|
107
|
+
id_code = 1
|
|
108
|
+
all_que = queue.Queue(maxsize=0)
|
|
109
|
+
|
|
110
|
+
def get_local_ip():
|
|
111
|
+
"""
|
|
112
|
+
查询本机ip地址
|
|
113
|
+
:return: ip
|
|
114
|
+
"""
|
|
115
|
+
try:
|
|
116
|
+
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
|
117
|
+
s.connect(('8.8.8.8', 80))
|
|
118
|
+
ip = s.getsockname()[0]
|
|
119
|
+
finally:
|
|
120
|
+
s.close()
|
|
121
|
+
return ip
|
|
122
|
+
def my_handler(signum, frame):
|
|
123
|
+
print('程序退出')
|
|
124
|
+
os._exit(0)
|
|
125
|
+
|
|
126
|
+
signal.signal(signal.SIGINT, my_handler)
|
|
127
|
+
dict_connect_info = {}#根据id_code存储连接方信息
|
|
128
|
+
|
|
129
|
+
def send_msg(client,result):
|
|
130
|
+
result['version'] = self_version
|
|
131
|
+
result = json.dumps(result).encode('utf-8')
|
|
132
|
+
client.sendall(result)
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def wait_accept(server):
|
|
136
|
+
while True:
|
|
137
|
+
try:
|
|
138
|
+
client, address = server.accept()
|
|
139
|
+
client.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
|
|
140
|
+
start_thread(target = handle_connect,args=(client,address))
|
|
141
|
+
except Exception as e:
|
|
142
|
+
time.sleep(1)
|
|
143
|
+
server.shutdown(socket.SHUT_RDWR)
|
|
144
|
+
close_con(server)
|
|
145
|
+
|
|
146
|
+
def start_thread(target,args):
|
|
147
|
+
thp1 = threading.Thread(target=target,args=args)
|
|
148
|
+
thp1.start()
|
|
149
|
+
|
|
150
|
+
log_que = queue.Queue()
|
|
151
|
+
log_list = []
|
|
152
|
+
|
|
153
|
+
def _project_root():
|
|
154
|
+
return os.path.abspath(os.path.join(os.path.dirname(__file__), os.pardir, os.pardir))
|
|
155
|
+
|
|
156
|
+
def _lttx_runtime_dir():
|
|
157
|
+
configured = os.environ.get("CFQUANT_LTTX_RUNTIME_DIR")
|
|
158
|
+
if configured:
|
|
159
|
+
return os.path.abspath(configured)
|
|
160
|
+
runtime_dir = os.environ.get("CFQUANT_RUNTIME_DIR")
|
|
161
|
+
if runtime_dir:
|
|
162
|
+
return os.path.abspath(os.path.join(runtime_dir, "lttx"))
|
|
163
|
+
return os.path.join(_project_root(), "runtime", "lttx")
|
|
164
|
+
|
|
165
|
+
def _lttx_file_data_dir():
|
|
166
|
+
configured = os.environ.get("CFQUANT_LTTX_FILE_DATA_DIR")
|
|
167
|
+
if configured:
|
|
168
|
+
return os.path.abspath(configured)
|
|
169
|
+
return os.path.join(_lttx_runtime_dir(), "file_data")
|
|
170
|
+
|
|
171
|
+
def _lttx_dataframe_data_dir():
|
|
172
|
+
configured = os.environ.get("CFQUANT_LTTX_DATAFRAME_DIR")
|
|
173
|
+
if configured:
|
|
174
|
+
return os.path.abspath(configured)
|
|
175
|
+
return os.path.join(_lttx_runtime_dir(), "dataframe_data")
|
|
176
|
+
|
|
177
|
+
def _ensure_lttx_dir(path):
|
|
178
|
+
os.makedirs(path, exist_ok=True)
|
|
179
|
+
return path
|
|
180
|
+
|
|
181
|
+
def _lttx_file_path(file_name):
|
|
182
|
+
return os.path.join(_ensure_lttx_dir(_lttx_file_data_dir()), file_name)
|
|
183
|
+
|
|
184
|
+
def _lttx_dataframe_path(var):
|
|
185
|
+
return os.path.join(_ensure_lttx_dir(_lttx_dataframe_data_dir()), "%s.csv" % var)
|
|
186
|
+
|
|
187
|
+
def _lttx_dict_var_path():
|
|
188
|
+
configured = os.environ.get("CFQUANT_LTTX_DICT_VAR_FILE")
|
|
189
|
+
if configured:
|
|
190
|
+
return os.path.abspath(configured)
|
|
191
|
+
return os.path.join(_ensure_lttx_dir(_lttx_runtime_dir()), "data0.txt")
|
|
192
|
+
|
|
193
|
+
def _lttx_log_dir():
|
|
194
|
+
configured = os.environ.get("CFQUANT_LTTX_LOG_DIR")
|
|
195
|
+
if configured:
|
|
196
|
+
return os.path.abspath(configured)
|
|
197
|
+
log_dir = os.environ.get("CFQUANT_LOG_DIR")
|
|
198
|
+
if log_dir:
|
|
199
|
+
return os.path.abspath(os.path.join(log_dir, "lttx"))
|
|
200
|
+
return os.path.abspath("log_data")
|
|
201
|
+
|
|
202
|
+
def _cleanup_lttx_logs(log_dir):
|
|
203
|
+
try:
|
|
204
|
+
retention_days = int(os.environ.get("CFQUANT_LOG_RETENTION_DAYS") or "30")
|
|
205
|
+
cutoff = time.time() - max(1, retention_days) * 86400
|
|
206
|
+
for name in os.listdir(log_dir):
|
|
207
|
+
path = os.path.join(log_dir, name)
|
|
208
|
+
if os.path.isfile(path) and os.path.getmtime(path) < cutoff:
|
|
209
|
+
os.remove(path)
|
|
210
|
+
except Exception:
|
|
211
|
+
pass
|
|
212
|
+
|
|
213
|
+
def sys_show_on(data):
|
|
214
|
+
'''
|
|
215
|
+
日志显示
|
|
216
|
+
'''
|
|
217
|
+
log_list.append(data)
|
|
218
|
+
if len(log_list) > 50:
|
|
219
|
+
log_list.pop(0)
|
|
220
|
+
log_que.put(data)
|
|
221
|
+
# connect_show_list.append(data)
|
|
222
|
+
# print(data)
|
|
223
|
+
|
|
224
|
+
def main_save_log():
|
|
225
|
+
try:
|
|
226
|
+
log_dir = _lttx_log_dir()
|
|
227
|
+
os.makedirs(log_dir, exist_ok=True)
|
|
228
|
+
_cleanup_lttx_logs(log_dir)
|
|
229
|
+
except:
|
|
230
|
+
log_dir = os.path.abspath("log_data")
|
|
231
|
+
try:
|
|
232
|
+
os.makedirs(log_dir, exist_ok=True)
|
|
233
|
+
except:
|
|
234
|
+
pass
|
|
235
|
+
cleanup_day = time.strftime("%Y-%m-%d")
|
|
236
|
+
while 1:
|
|
237
|
+
data = log_que.get()
|
|
238
|
+
current_day = time.strftime("%Y-%m-%d")
|
|
239
|
+
if current_day != cleanup_day:
|
|
240
|
+
_cleanup_lttx_logs(log_dir)
|
|
241
|
+
cleanup_day = current_day
|
|
242
|
+
with open(os.path.join(log_dir, '%s_log.csv'%(current_day)),'a+',encoding='utf-8') as f:
|
|
243
|
+
f.write(str(data)+'\n')
|
|
244
|
+
|
|
245
|
+
msg = '%s>>>>>>>[info] LTtx程序启动'%(time.strftime("%Y-%m-%d %H:%M:%S"))
|
|
246
|
+
threading.Thread(target = main_save_log).start()
|
|
247
|
+
|
|
248
|
+
def close_con(client):
|
|
249
|
+
try:
|
|
250
|
+
# 强制停止读写操作,确保 recv 不再阻塞
|
|
251
|
+
client.shutdown(socket.SHUT_RDWR)
|
|
252
|
+
except socket.error as e:
|
|
253
|
+
pass
|
|
254
|
+
finally:
|
|
255
|
+
# 释放套接字资源
|
|
256
|
+
client.close()
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
def handle_connect(client,address):
|
|
260
|
+
global id_code
|
|
261
|
+
while True:
|
|
262
|
+
try:
|
|
263
|
+
# 接收客户端发过来的数据 循环为这个客户端服务多次
|
|
264
|
+
recv_data = client.recv(1024)
|
|
265
|
+
sys_show_on("%s:收到客户端%s请求连接的消息:%s" %(get_str_time(),address,recv_data.decode("utf-8")))
|
|
266
|
+
#print("%s:收到客户端%s请求连接的消息:%s" %(get_str_time(),address,recv_data.decode("utf-8")))
|
|
267
|
+
dict_data = json.loads(recv_data)
|
|
268
|
+
con_type = dict_data['con_type']
|
|
269
|
+
con_tocken = dict_data['tocken']
|
|
270
|
+
dict_data['加入时间'] = get_str_time()
|
|
271
|
+
dict_data['con'] = client
|
|
272
|
+
if con_tocken != tocken:
|
|
273
|
+
code = '-1'
|
|
274
|
+
msg = '传入的tocken:%s不匹配,当前服务器版本%s'%(con_tocken,self_version)
|
|
275
|
+
result = {'code':code,'msg':'服务器发来消息:%s'%(msg)}
|
|
276
|
+
send_msg(client,result)
|
|
277
|
+
elif con_type == 'put_mode':
|
|
278
|
+
result = handle_connect_put(client,address,dict_data)
|
|
279
|
+
C_que = queue.Queue(maxsize=0)
|
|
280
|
+
C_que_push = queue.Queue(maxsize=0)
|
|
281
|
+
start_thread(target = main_handle_client_que,args=(client, C_que, C_que_push))
|
|
282
|
+
start_thread(target = main_push_data_que,args=(client, C_que_push))
|
|
283
|
+
send_msg(client,result)
|
|
284
|
+
start_thread(target = main_handle_msg_tx,args=(client,address,C_que,id_code))
|
|
285
|
+
elif con_type == 'push_mode':
|
|
286
|
+
who = dict_data['who']
|
|
287
|
+
if '@' not in who:
|
|
288
|
+
who = [who]
|
|
289
|
+
else:
|
|
290
|
+
who = who.split('@')
|
|
291
|
+
pwd = dict_data['pwd']
|
|
292
|
+
result = handle_connect_push(client,address,who,pwd,dict_data=dict_data)
|
|
293
|
+
send_msg(client,result)
|
|
294
|
+
elif con_type == 'file_mode':
|
|
295
|
+
handle_connect_file(client,address,dict_data)
|
|
296
|
+
close_con(client)
|
|
297
|
+
break
|
|
298
|
+
elif con_type == 'plus_mode':
|
|
299
|
+
id_code = id_code + 1
|
|
300
|
+
dict_data['id_code'] = id_code
|
|
301
|
+
result = {'code':0,'msg':'plus mode连接成功','id_code':id_code}
|
|
302
|
+
start_thread(target = main_handle_push_plus,args=(client,))
|
|
303
|
+
dict_connect_info[id_code] = dict_data
|
|
304
|
+
send_msg(client,result)
|
|
305
|
+
elif con_type == 'check_version':
|
|
306
|
+
from tx import txl
|
|
307
|
+
tx1 = txl('a',2025,'test')
|
|
308
|
+
version = tx1.version
|
|
309
|
+
result = {'LTtx_lastest':version}
|
|
310
|
+
send_msg(client,result)
|
|
311
|
+
# 如果客户端发送的数据不为空那么就是需要服务
|
|
312
|
+
else:
|
|
313
|
+
result = '当前连接未通过'
|
|
314
|
+
send_msg(client,result)
|
|
315
|
+
close_con(client)
|
|
316
|
+
break
|
|
317
|
+
except Exception as e:
|
|
318
|
+
try:
|
|
319
|
+
result = '当前连接未通过>>>%s'%(str(e))
|
|
320
|
+
sys_show_on(result)
|
|
321
|
+
send_msg(client,result)
|
|
322
|
+
close_con(client)
|
|
323
|
+
except:
|
|
324
|
+
break
|
|
325
|
+
|
|
326
|
+
|
|
327
|
+
|
|
328
|
+
def handle_connect_file(client,address,dict_data):
|
|
329
|
+
file_mode = dict_data['file_mode']
|
|
330
|
+
if file_mode == 'upload_file':
|
|
331
|
+
file_name = dict_data['file_name']
|
|
332
|
+
file_hash = dict_data['file_hash']
|
|
333
|
+
file_path = _lttx_file_path(file_name)
|
|
334
|
+
tmp_path = _lttx_file_path(file_name + '.tmp')
|
|
335
|
+
file = open(tmp_path,'wb')
|
|
336
|
+
client.sendall(b'i am ok')
|
|
337
|
+
file_data = client.recv(1024)
|
|
338
|
+
while file_data:
|
|
339
|
+
file.write(file_data)
|
|
340
|
+
file_data = client.recv(1024)
|
|
341
|
+
file.close()
|
|
342
|
+
sys_show_on('%s文件接收完成'%(file_name))
|
|
343
|
+
if os.path.isfile(file_path):
|
|
344
|
+
os.remove(file_path)
|
|
345
|
+
os.rename(tmp_path, file_path)
|
|
346
|
+
|
|
347
|
+
elif file_mode == 'download_file':
|
|
348
|
+
file_name = dict_data['file_name']
|
|
349
|
+
hash_md5 = hashlib.md5()
|
|
350
|
+
file_path = _lttx_file_path(file_name)
|
|
351
|
+
if os.path.isfile(file_path):
|
|
352
|
+
client.sendall('file exist'.encode())
|
|
353
|
+
file = open(file_path, 'rb')
|
|
354
|
+
file_data = file.read(1024)
|
|
355
|
+
while file_data:
|
|
356
|
+
client.sendall(file_data)
|
|
357
|
+
file_data = file.read(1024)
|
|
358
|
+
file.close()
|
|
359
|
+
time.sleep(1)
|
|
360
|
+
close_con(client)
|
|
361
|
+
else:
|
|
362
|
+
client.sendall('file does not exist'.encode())
|
|
363
|
+
else:
|
|
364
|
+
pass
|
|
365
|
+
|
|
366
|
+
|
|
367
|
+
|
|
368
|
+
def handle_put(var,data):
|
|
369
|
+
que_put.put((var,data))
|
|
370
|
+
return {'code':0,'msg':'put work down'}
|
|
371
|
+
|
|
372
|
+
que_put = queue.Queue()
|
|
373
|
+
def main_handle_put():
|
|
374
|
+
file_path = os.path.join(os.path.dirname(__file__), 'data0.txt')
|
|
375
|
+
while True:
|
|
376
|
+
var,data = que_put.get()
|
|
377
|
+
try:
|
|
378
|
+
data = json.loads(data)
|
|
379
|
+
except:
|
|
380
|
+
pass
|
|
381
|
+
dict_var[var] = data
|
|
382
|
+
if que_put.qsize() > 50:
|
|
383
|
+
while True:
|
|
384
|
+
if que_put.qsize() > 2:
|
|
385
|
+
try:
|
|
386
|
+
data = json.loads(data)
|
|
387
|
+
except:
|
|
388
|
+
pass
|
|
389
|
+
dict_var[var] = data
|
|
390
|
+
else:
|
|
391
|
+
break
|
|
392
|
+
with open(file_path, 'w', encoding='utf-8') as f:
|
|
393
|
+
json.dump(dict_var, f, ensure_ascii=False, indent=4)
|
|
394
|
+
|
|
395
|
+
def load_and_run(module_name: str, class_name: str, method_name: str):
|
|
396
|
+
# 导入并 reload
|
|
397
|
+
module = importlib.import_module(module_name)
|
|
398
|
+
importlib.reload(module)
|
|
399
|
+
|
|
400
|
+
# 判断类是否存在
|
|
401
|
+
if not hasattr(module, class_name):
|
|
402
|
+
print(f"模块中未找到类:{class_name}")
|
|
403
|
+
return None
|
|
404
|
+
|
|
405
|
+
cls1 = getattr(module, class_name)
|
|
406
|
+
instance = cls1() # 实例化类
|
|
407
|
+
|
|
408
|
+
# 判断方法是否存在且可调用
|
|
409
|
+
if hasattr(instance, method_name):
|
|
410
|
+
method = getattr(instance, method_name)
|
|
411
|
+
if callable(method):
|
|
412
|
+
print('work here')
|
|
413
|
+
return method() # 调用并返回结果
|
|
414
|
+
else:
|
|
415
|
+
print(f"{method_name} 不是可调用对象")
|
|
416
|
+
else:
|
|
417
|
+
print(f"类 {class_name} 中无方法 {method_name}")
|
|
418
|
+
|
|
419
|
+
|
|
420
|
+
|
|
421
|
+
class SysTools:
|
|
422
|
+
def judge_market_open(dict_data):
|
|
423
|
+
'''
|
|
424
|
+
判断开市时间
|
|
425
|
+
'''
|
|
426
|
+
exchange_code = dict_data['exchange_code']
|
|
427
|
+
now = dict_data['now']
|
|
428
|
+
|
|
429
|
+
def get_market_calendars(dict_data):
|
|
430
|
+
'''
|
|
431
|
+
返回一个交易日
|
|
432
|
+
'''
|
|
433
|
+
|
|
434
|
+
def is_market_open(exchange_code: str) -> bool:
|
|
435
|
+
"""检测上海交易所是否开盘"""
|
|
436
|
+
# 获取交易所日历
|
|
437
|
+
cal = mcal.get_calendar(exchange_code)
|
|
438
|
+
|
|
439
|
+
# 根据交易所设置本地时区
|
|
440
|
+
tz_map = {
|
|
441
|
+
"XSHG": "Asia/Shanghai", # A股(上交所)
|
|
442
|
+
"XSHE": "Asia/Shanghai", # A股(深交所)
|
|
443
|
+
"XHKG": "Asia/Hong_Kong", # 港股
|
|
444
|
+
"XNYS": "America/New_York" # 美股(纽交所)
|
|
445
|
+
}
|
|
446
|
+
tz = pytz.timezone(tz_map.get(exchange_code, "UTC"))
|
|
447
|
+
now = datetime.datetime.now(tz) # 带时区的当前时间
|
|
448
|
+
|
|
449
|
+
# 1. 检查是否为交易日
|
|
450
|
+
schedule = cal.schedule(start_date=now.date(), end_date=now.date())
|
|
451
|
+
if schedule.empty:
|
|
452
|
+
return False # 非交易日(休市)
|
|
453
|
+
|
|
454
|
+
# 2. 提取交易时段并统一时区
|
|
455
|
+
market_open = schedule["market_open"].iloc[0].tz_convert(tz.zone)
|
|
456
|
+
market_close = schedule["market_close"].iloc[0].tz_convert(tz.zone)
|
|
457
|
+
|
|
458
|
+
# 3. 判断当前是否在交易时段内
|
|
459
|
+
return market_open <= now <= market_close
|
|
460
|
+
|
|
461
|
+
def handle_sys_tools(dict_data):
|
|
462
|
+
'''
|
|
463
|
+
提供一些系统级功能函数
|
|
464
|
+
'''
|
|
465
|
+
tools = dict_data['tools']
|
|
466
|
+
|
|
467
|
+
|
|
468
|
+
def handle_get(var):
|
|
469
|
+
if var in dict_var:
|
|
470
|
+
return {'value':dict_var[var]}
|
|
471
|
+
else:
|
|
472
|
+
return {'value':None}
|
|
473
|
+
def get_str_time():
|
|
474
|
+
'''
|
|
475
|
+
返回2024-11-23 13:00:09数据格式
|
|
476
|
+
'''
|
|
477
|
+
return time.strftime('%Y-%m-%d %H:%M:%S', time.localtime())
|
|
478
|
+
|
|
479
|
+
|
|
480
|
+
def recv_data_from_tx(client):
|
|
481
|
+
recv_bytes = client.recv(8,socket.MSG_WAITALL)
|
|
482
|
+
bytes_len = struct.unpack("Q",recv_bytes)[0]
|
|
483
|
+
recv_data = client.recv(bytes_len,socket.MSG_WAITALL)
|
|
484
|
+
recv_data = json.loads(recv_data.decode('utf-8',errors='replace'))
|
|
485
|
+
return recv_data
|
|
486
|
+
|
|
487
|
+
def recv_data_from_tx_fast(client):
|
|
488
|
+
recv_bytes = client.recv(8,socket.MSG_WAITALL)
|
|
489
|
+
bytes_len = struct.unpack("Q",recv_bytes)[0]
|
|
490
|
+
recv_data = client.recv(bytes_len,socket.MSG_WAITALL)
|
|
491
|
+
recv_data = recv_data.decode('utf-8',errors='replace')
|
|
492
|
+
return recv_data
|
|
493
|
+
|
|
494
|
+
def send_data_to_client_fast(client,msg):
|
|
495
|
+
msg = msg.encode('utf-8')
|
|
496
|
+
data_len = len(msg)
|
|
497
|
+
struct_bytes = struct.pack('Q', data_len)
|
|
498
|
+
client.sendall(struct_bytes)
|
|
499
|
+
client.sendall(msg)
|
|
500
|
+
|
|
501
|
+
def send_data_to_client(client,msg):
|
|
502
|
+
msg=json.dumps(msg).encode('utf-8')
|
|
503
|
+
data_len = len(msg)
|
|
504
|
+
struct_bytes = struct.pack('Q', data_len)
|
|
505
|
+
client.sendall(struct_bytes)
|
|
506
|
+
client.sendall(msg)
|
|
507
|
+
# client.sendall(struct_bytes+msg)
|
|
508
|
+
|
|
509
|
+
def main_handle_push_plus(client):
|
|
510
|
+
while True:
|
|
511
|
+
recv_data = recv_data_from_tx_fast(client).rsplit(':who:',1)
|
|
512
|
+
who = recv_data[1]
|
|
513
|
+
if who in dict_client_push_group:
|
|
514
|
+
if len(dict_client_push_group[who]['group']) == 0:
|
|
515
|
+
pass
|
|
516
|
+
else:
|
|
517
|
+
dict_client_push_group[who]['que'].put((client,recv_data[0]))
|
|
518
|
+
|
|
519
|
+
|
|
520
|
+
def main_handle_msg_tx(client,address,C_que,id_code):
|
|
521
|
+
sys_show_on(get_str_time()+':'+'客户端'+str(address)+'加入tx')
|
|
522
|
+
while True:
|
|
523
|
+
try:
|
|
524
|
+
recv_data = recv_data_from_tx(client)
|
|
525
|
+
C_que.put(recv_data)
|
|
526
|
+
except Exception as e:
|
|
527
|
+
try:
|
|
528
|
+
close_con(client)
|
|
529
|
+
msg = 'TX客户端%s退出,退出原因%s'%(dict_client[id_code]['address'],str(e))
|
|
530
|
+
sys_show_on(msg)
|
|
531
|
+
del dict_client[id_code]
|
|
532
|
+
del dict_connect_info[id_code]
|
|
533
|
+
break
|
|
534
|
+
except:
|
|
535
|
+
pass
|
|
536
|
+
break
|
|
537
|
+
|
|
538
|
+
def main_handle_client_que(client,C_que,C_que_push):
|
|
539
|
+
while True:
|
|
540
|
+
try:
|
|
541
|
+
recv_data = C_que.get()
|
|
542
|
+
func = recv_data['func']
|
|
543
|
+
if func == 'push':
|
|
544
|
+
who = recv_data['who']
|
|
545
|
+
if who in dict_client_push_group:
|
|
546
|
+
if len(dict_client_push_group[who]['group']) == 0:
|
|
547
|
+
pass
|
|
548
|
+
else:
|
|
549
|
+
recv_data = recv_data['value']
|
|
550
|
+
dict_client_push_group[who]['que'].put((client,recv_data))
|
|
551
|
+
else:
|
|
552
|
+
pass
|
|
553
|
+
elif func == 'get':
|
|
554
|
+
var = recv_data['value']
|
|
555
|
+
result = handle_get(var)
|
|
556
|
+
C_que_push.put(result)
|
|
557
|
+
elif func == 'put':
|
|
558
|
+
data = recv_data['value'][1]
|
|
559
|
+
var = recv_data['value'][0]
|
|
560
|
+
result = handle_put(var,data)
|
|
561
|
+
elif func == 'list_append':
|
|
562
|
+
data = recv_data['value'][1]
|
|
563
|
+
var = recv_data['value'][0]
|
|
564
|
+
result = handle_list_append(var,data)
|
|
565
|
+
elif func == 'dict_change':
|
|
566
|
+
value = recv_data['value'][2]
|
|
567
|
+
data = recv_data['value'][1]
|
|
568
|
+
var = recv_data['value'][0]
|
|
569
|
+
result = handle_dict_change(var,data,value)
|
|
570
|
+
elif func == 'list_pop':
|
|
571
|
+
data = recv_data['value'][1]
|
|
572
|
+
var = recv_data['value'][0]
|
|
573
|
+
result = handle_list_pop(var,data)
|
|
574
|
+
elif func == 'list_remove':
|
|
575
|
+
data = recv_data['value'][1]
|
|
576
|
+
var = recv_data['value'][0]
|
|
577
|
+
result = handle_list_remove(var,data)
|
|
578
|
+
elif func == 'put_dataframe':
|
|
579
|
+
data = recv_data['value'][1]
|
|
580
|
+
var = recv_data['value'][0]
|
|
581
|
+
result = handle_put_dataframe(var,data)
|
|
582
|
+
elif func == 'get_dataframe':
|
|
583
|
+
var = recv_data['value']
|
|
584
|
+
result = handle_get_dataframe(var)
|
|
585
|
+
C_que_push.put(result)
|
|
586
|
+
elif func == 'get_dict_value':
|
|
587
|
+
result = handle_get_dict_value(recv_data['value'][0],recv_data['value'][1])
|
|
588
|
+
C_que_push.put(result)
|
|
589
|
+
elif func == 'get_list_value':
|
|
590
|
+
result = handle_get_list_value(recv_data['value'][0],recv_data['value'][1])
|
|
591
|
+
C_que_push.put(result)
|
|
592
|
+
except:
|
|
593
|
+
pass
|
|
594
|
+
|
|
595
|
+
|
|
596
|
+
def handle_get_dict_value(var,key):
|
|
597
|
+
if var in dict_var:
|
|
598
|
+
if key in dict_var[var]:
|
|
599
|
+
return {'value':dict_var[var][key]}
|
|
600
|
+
else:
|
|
601
|
+
return {'value':None}
|
|
602
|
+
else:
|
|
603
|
+
return {'value':None}
|
|
604
|
+
|
|
605
|
+
def handle_get_list_value(var,index):
|
|
606
|
+
if var in dict_var:
|
|
607
|
+
if abs(index) > len(dict_var[var]):
|
|
608
|
+
return {'value':dict_var[var][index]}
|
|
609
|
+
else:
|
|
610
|
+
return {'value':None}
|
|
611
|
+
else:
|
|
612
|
+
return {'value':None}
|
|
613
|
+
|
|
614
|
+
def handle_get_dataframe(var):
|
|
615
|
+
'''
|
|
616
|
+
处理get dataframe
|
|
617
|
+
'''
|
|
618
|
+
import pandas as pd
|
|
619
|
+
if var in dict_df:
|
|
620
|
+
try:
|
|
621
|
+
df = pd.read_csv(_lttx_dataframe_path(var))
|
|
622
|
+
except:
|
|
623
|
+
df = pd.DataFrame()
|
|
624
|
+
else:
|
|
625
|
+
df = pd.DataFrame()
|
|
626
|
+
|
|
627
|
+
return {'value':json.dumps(df.to_dict(orient='records'))}
|
|
628
|
+
|
|
629
|
+
dict_df = {}
|
|
630
|
+
def handle_put_dataframe(var,data):
|
|
631
|
+
import pandas as pd
|
|
632
|
+
try:
|
|
633
|
+
df = pd.DataFrame(json.loads(data))
|
|
634
|
+
df.to_csv(_lttx_dataframe_path(var),index=False)
|
|
635
|
+
dict_df[var] = 1
|
|
636
|
+
tem_dict = {'code':0,'msg':'success','value':{}}
|
|
637
|
+
except Exception as e:
|
|
638
|
+
tem_dict = {'code':-1,'msg':str(e)}
|
|
639
|
+
print(tem_dict)
|
|
640
|
+
return tem_dict
|
|
641
|
+
|
|
642
|
+
|
|
643
|
+
def handle_list_remove(var,data):
|
|
644
|
+
global dict_var_on
|
|
645
|
+
try:
|
|
646
|
+
dict_var[var].remove(data)
|
|
647
|
+
if dict_var_on == False:
|
|
648
|
+
dict_var_on = True
|
|
649
|
+
except:
|
|
650
|
+
pass
|
|
651
|
+
|
|
652
|
+
def handle_list_pop(var,data):
|
|
653
|
+
global dict_var_on
|
|
654
|
+
try:
|
|
655
|
+
data = int(data)
|
|
656
|
+
dict_var[var].pop(data)
|
|
657
|
+
if dict_var_on == False:
|
|
658
|
+
dict_var_on = True
|
|
659
|
+
except:
|
|
660
|
+
pass
|
|
661
|
+
|
|
662
|
+
def handle_list_append(var,data):
|
|
663
|
+
global dict_var_on
|
|
664
|
+
try:
|
|
665
|
+
dict_var[var].append(data)
|
|
666
|
+
if dict_var_on == False:
|
|
667
|
+
dict_var_on = True
|
|
668
|
+
except:
|
|
669
|
+
pass
|
|
670
|
+
|
|
671
|
+
def handle_dict_change(var,key,value):
|
|
672
|
+
global dict_var_on
|
|
673
|
+
# try:
|
|
674
|
+
print(dict_var)
|
|
675
|
+
# tem_dict = json.loads(dict_var[var])
|
|
676
|
+
# tem_dict[key] = value
|
|
677
|
+
dict_var[var][key] = value
|
|
678
|
+
if dict_var_on == False:
|
|
679
|
+
dict_var_on = True
|
|
680
|
+
# except:
|
|
681
|
+
# pass
|
|
682
|
+
|
|
683
|
+
def main_push_data_que(client,C_que_push):
|
|
684
|
+
while True:
|
|
685
|
+
result = C_que_push.get()
|
|
686
|
+
# print(result,'----')
|
|
687
|
+
send_data_to_client(client,msg=json.dumps(result))
|
|
688
|
+
|
|
689
|
+
|
|
690
|
+
def handle_connect_put(con,address,dict_data):
|
|
691
|
+
global id_code
|
|
692
|
+
id_code = id_code + 1
|
|
693
|
+
dict_data['id_code'] = id_code
|
|
694
|
+
dict_client[id_code] = {'con':con,'address':address,'id_code':id_code,}
|
|
695
|
+
# dict_data['con']
|
|
696
|
+
dict_connect_info[id_code] = dict_data
|
|
697
|
+
msg = '服务端发来信息:新连接加入成功'
|
|
698
|
+
code = 0
|
|
699
|
+
result = {'code':code,'msg':msg,'id_code':id_code}
|
|
700
|
+
return result
|
|
701
|
+
|
|
702
|
+
def main_heartbeat():
|
|
703
|
+
'''
|
|
704
|
+
客户端心跳维护子线程
|
|
705
|
+
|
|
706
|
+
Returns
|
|
707
|
+
-------
|
|
708
|
+
None.
|
|
709
|
+
|
|
710
|
+
'''
|
|
711
|
+
recv_data = {1:1}
|
|
712
|
+
while True:
|
|
713
|
+
for who,value in dict_client_push_group.items():
|
|
714
|
+
value['que'].put((1,recv_data))
|
|
715
|
+
time.sleep(5)
|
|
716
|
+
|
|
717
|
+
def main_push_client(tem_que,client,id_code,who):
|
|
718
|
+
while True:
|
|
719
|
+
msg = tem_que.get()
|
|
720
|
+
try:
|
|
721
|
+
send_data_to_client(client, msg)
|
|
722
|
+
except Exception as e:
|
|
723
|
+
msg = '%s:有broadcast客户端%s退出,退出原因%s'%(get_str_time(),dict_client_push[id_code]['address'],str(e))
|
|
724
|
+
sys_show_on(msg)
|
|
725
|
+
close_con(client)
|
|
726
|
+
try:
|
|
727
|
+
del dict_connect_info[id_code]
|
|
728
|
+
# print(1)
|
|
729
|
+
del dict_client_que[client]
|
|
730
|
+
# print(2)
|
|
731
|
+
del dict_client_push[id_code]
|
|
732
|
+
# print(3)
|
|
733
|
+
dict_client_push_group[who]['group'].remove(client)
|
|
734
|
+
# print(4)
|
|
735
|
+
except Exception as e:
|
|
736
|
+
pass
|
|
737
|
+
print('删除连接出错',e,id_code)
|
|
738
|
+
break
|
|
739
|
+
|
|
740
|
+
dict_client_que_id_code = {}#根据id_code存放队列,用这个来查找待推送的数据量
|
|
741
|
+
def handle_connect_push(con,address,who_list,pwd='520',dict_data={}):
|
|
742
|
+
global id_code
|
|
743
|
+
id_code = id_code + 1
|
|
744
|
+
dict_client_push[id_code] = {'con':con,'address':address,'id_code':id_code}
|
|
745
|
+
dict_data['con'] = con
|
|
746
|
+
dict_connect_info[id_code] = dict_data
|
|
747
|
+
for who in who_list:
|
|
748
|
+
if who in dict_client_push_group:
|
|
749
|
+
if pwd == dict_client_push_group[who]['pwd']:
|
|
750
|
+
dict_client_push_group[who]['group'].append(con)
|
|
751
|
+
msg = '服务端发来信息:欢迎加入%s信道,当前总信道连接数为:%s'%(who,len(dict_client_push_group[who]['group']))
|
|
752
|
+
tem_que = queue.Queue()
|
|
753
|
+
dict_client_que[con] = tem_que
|
|
754
|
+
dict_client_que_id_code[id_code] = tem_que
|
|
755
|
+
threading.Thread(target = main_push_client,args=(tem_que,con,id_code,who)).start()
|
|
756
|
+
threading.Thread(target = main_txg_heartbeat,args=(con,)).start()
|
|
757
|
+
code = 0
|
|
758
|
+
else:
|
|
759
|
+
msg = '服务端发来信息:当前信道%s已经存在,传入的密码:%s不正确,请重新传入密码'%(who,pwd)
|
|
760
|
+
del dict_client_push[id_code]
|
|
761
|
+
code = -1
|
|
762
|
+
else:
|
|
763
|
+
tem_que = queue.Queue()
|
|
764
|
+
dict_client_que[con] = tem_que
|
|
765
|
+
dict_client_que_id_code[id_code] = tem_que
|
|
766
|
+
threading.Thread(target = main_push_client,args=(tem_que,con,id_code,who)).start()
|
|
767
|
+
tem_que = queue.Queue(maxsize=0)
|
|
768
|
+
dict_client_push_group[who] = {'group':[con],'pwd':pwd,'que':tem_que}
|
|
769
|
+
thp0 = threading.Thread(target = main_push_queue,args=(tem_que,who))
|
|
770
|
+
thp0.start()
|
|
771
|
+
threading.Thread(target = main_txg_heartbeat,args=(con,)).start()
|
|
772
|
+
msg = '服务端发来信息:新信道%s创建成功'%(who)
|
|
773
|
+
code = 0
|
|
774
|
+
result = {'code':code,'msg':msg}
|
|
775
|
+
return result
|
|
776
|
+
|
|
777
|
+
def main_txg_heartbeat(client):
|
|
778
|
+
'''
|
|
779
|
+
处理txg的心跳
|
|
780
|
+
'''
|
|
781
|
+
while 1:
|
|
782
|
+
try:
|
|
783
|
+
client.recv(1,socket.MSG_WAITALL)
|
|
784
|
+
# recv_data = json.loads(recv_data.decode('utf-8',errors='replace'))
|
|
785
|
+
except:
|
|
786
|
+
break
|
|
787
|
+
|
|
788
|
+
def main_push_queue(que,who):
|
|
789
|
+
sys_show_on('新信道%s子线程启动'%(who))
|
|
790
|
+
while True:
|
|
791
|
+
# try:
|
|
792
|
+
client,recv_data = que.get()
|
|
793
|
+
group_list = dict_client_push_group[who]['group']
|
|
794
|
+
if len(group_list) ==0:
|
|
795
|
+
sys_show_on('删除%s信道'%(who))
|
|
796
|
+
del dict_client_push_group[who]
|
|
797
|
+
break
|
|
798
|
+
else:
|
|
799
|
+
for i in group_list:
|
|
800
|
+
if i in dict_client_que:
|
|
801
|
+
dict_client_que[i].put(recv_data)
|
|
802
|
+
else:
|
|
803
|
+
group_list.remove(i)
|
|
804
|
+
|
|
805
|
+
|
|
806
|
+
|
|
807
|
+
def main_test():
|
|
808
|
+
while True:
|
|
809
|
+
print('\n\r')
|
|
810
|
+
a=input('输入要执行的python代码:\n\n')
|
|
811
|
+
print('执行结果如下:\n\n')
|
|
812
|
+
if a=='clear':
|
|
813
|
+
os.system('clear')
|
|
814
|
+
else:
|
|
815
|
+
try:
|
|
816
|
+
exec(a)
|
|
817
|
+
print('\n')
|
|
818
|
+
except Exception as e:
|
|
819
|
+
print(e)
|
|
820
|
+
|
|
821
|
+
def load_dict_var():
|
|
822
|
+
try:
|
|
823
|
+
config_path = _lttx_dict_var_path()
|
|
824
|
+
|
|
825
|
+
# 尝试打开并读取 JSON 文件
|
|
826
|
+
with open(config_path, 'r', encoding='utf-8') as f:
|
|
827
|
+
dict_var = json.load(f)
|
|
828
|
+
# print('Load dict_var success!')
|
|
829
|
+
except FileNotFoundError:
|
|
830
|
+
#print(f"Load Error: File not found at {config_path}")
|
|
831
|
+
dict_var = {}
|
|
832
|
+
except json.JSONDecodeError:
|
|
833
|
+
# print("Load Error: File is not valid JSON")
|
|
834
|
+
dict_var = {}
|
|
835
|
+
except Exception as e:
|
|
836
|
+
# print('Load Error:', e)
|
|
837
|
+
dict_var = {}
|
|
838
|
+
|
|
839
|
+
return dict_var
|
|
840
|
+
|
|
841
|
+
def main_show():
|
|
842
|
+
while True:
|
|
843
|
+
table_data = []
|
|
844
|
+
for who,value in dict_client_push_group.items():
|
|
845
|
+
print(who,value['que'].qsize())
|
|
846
|
+
tem_list = [who[:10],len(value['group']),value['que'].qsize(),'']
|
|
847
|
+
table_data.append(tem_list)
|
|
848
|
+
print('\033c', end='')
|
|
849
|
+
|
|
850
|
+
print('#'*20,'服务器信息V5','#'*20)
|
|
851
|
+
print('ip:%s'%(ip),' '*10,'port:%s'%(port),' '*10,'tocken:%s'%(tocken))
|
|
852
|
+
|
|
853
|
+
print('#'*20,'当前LTtx状态','#'*20)
|
|
854
|
+
print('当前TX连接数:%s 当前TXG信道数'%(len(dict_client)),len(dict_client_push_group))
|
|
855
|
+
|
|
856
|
+
print(tabulate.tabulate(table_data, headers=['信道名称', '当前连接数', '信道队列数据量','备注'], tablefmt='fancy_grid',stralign='wrap'))
|
|
857
|
+
|
|
858
|
+
print('%sLTtx系统最新日志信息:'%(time.strftime('%Y-%m-%d %H:%M:%S',time.localtime())))
|
|
859
|
+
print('#'*30)
|
|
860
|
+
for i in connect_show_list:
|
|
861
|
+
print(i)
|
|
862
|
+
if len (connect_show_list) > 10:
|
|
863
|
+
connect_show_list.remove(i)
|
|
864
|
+
print('\n\r')
|
|
865
|
+
print('#'*30)
|
|
866
|
+
|
|
867
|
+
|
|
868
|
+
time.sleep(1)
|
|
869
|
+
|
|
870
|
+
|
|
871
|
+
def show_channel_list():
|
|
872
|
+
table_data = []
|
|
873
|
+
for channel in dict_client_push_group:
|
|
874
|
+
tem_que_len = dict_client_push_group[channel]['que'].qsize()
|
|
875
|
+
user_num = len(dict_client_push_group[channel]['group'])
|
|
876
|
+
tem_list = [channel[:10],user_num,tem_que_len]
|
|
877
|
+
table_data.append(tem_list)
|
|
878
|
+
print(tabulate.tabulate(table_data, headers=['信道名称', '当前连接数', '信道队列数据量','备注'], tablefmt='fancy_grid',stralign='wrap'))
|
|
879
|
+
|
|
880
|
+
def show_push_client_qsize():
|
|
881
|
+
'''
|
|
882
|
+
打印每个客户端的待推送数据长度
|
|
883
|
+
'''
|
|
884
|
+
for i in dict_client_que:
|
|
885
|
+
print(i,'>>>>',dict_client_que[i].qsize())
|
|
886
|
+
|
|
887
|
+
def channel_mange():
|
|
888
|
+
while True:
|
|
889
|
+
print('#'*20,'信道管理','#'*20)
|
|
890
|
+
choice_dict = {'1----查看当前所有信道':show_channel_list,
|
|
891
|
+
'2----查看所有连接待推送数据量':show_push_client_qsize,
|
|
892
|
+
'99----返回上一级':'',
|
|
893
|
+
}
|
|
894
|
+
for i in choice_dict:
|
|
895
|
+
print(i)
|
|
896
|
+
cmd_dict = {}
|
|
897
|
+
for i in choice_dict:
|
|
898
|
+
cmd_dict[i.split('-')[0]] = choice_dict[i]
|
|
899
|
+
cmd = input('输入指令:')
|
|
900
|
+
if cmd == '99':
|
|
901
|
+
break
|
|
902
|
+
if cmd not in cmd_dict:
|
|
903
|
+
print('当前指令不存在')
|
|
904
|
+
continue
|
|
905
|
+
print('【info指令运行结果】')
|
|
906
|
+
cmd_dict[cmd]()
|
|
907
|
+
print('\n\r')
|
|
908
|
+
|
|
909
|
+
def show_connect_list():
|
|
910
|
+
for id_code in dict_client:
|
|
911
|
+
print(dict_client[id_code])
|
|
912
|
+
|
|
913
|
+
|
|
914
|
+
def close_connet_single():
|
|
915
|
+
|
|
916
|
+
show_dict_connect_info()
|
|
917
|
+
id_code = int(input('请选择要断开的id_code'))
|
|
918
|
+
if id_code in dict_connect_info:
|
|
919
|
+
con = dict_connect_info[id_code]['con']
|
|
920
|
+
client = con
|
|
921
|
+
close_con(client)
|
|
922
|
+
try:
|
|
923
|
+
del dict_connect_info[id_code]
|
|
924
|
+
# print(1)
|
|
925
|
+
del dict_client_que[client]
|
|
926
|
+
# print(2)
|
|
927
|
+
del dict_client_push[id_code]
|
|
928
|
+
if 'who' in dict_connect_info[id_code]:
|
|
929
|
+
# print(3)
|
|
930
|
+
who = dict_connect_info[id_code]['who']
|
|
931
|
+
dict_client_push_group[who]['group'].remove(client)
|
|
932
|
+
# print(4)
|
|
933
|
+
except Exception as e:
|
|
934
|
+
pass
|
|
935
|
+
print('删除连接出错',e,id_code)
|
|
936
|
+
else:
|
|
937
|
+
print('当前id_code不存在')
|
|
938
|
+
|
|
939
|
+
def connect_mange():
|
|
940
|
+
while True:
|
|
941
|
+
print('#'*20,'连接管理','#'*20)
|
|
942
|
+
choice_dict = {'1----查看当前所有连接':show_connect_list,
|
|
943
|
+
'2----断开单个连接':close_connet_single,
|
|
944
|
+
'99----返回上一级':'',
|
|
945
|
+
}
|
|
946
|
+
for i in choice_dict:
|
|
947
|
+
print(i)
|
|
948
|
+
cmd_dict = {}
|
|
949
|
+
for i in choice_dict:
|
|
950
|
+
cmd_dict[i.split('-')[0]] = choice_dict[i]
|
|
951
|
+
cmd = input('输入指令:')
|
|
952
|
+
if cmd == '99':
|
|
953
|
+
break
|
|
954
|
+
if cmd not in cmd_dict:
|
|
955
|
+
print('当前指令不存在')
|
|
956
|
+
continue
|
|
957
|
+
print('【info指令运行结果】')
|
|
958
|
+
cmd_dict[cmd]()
|
|
959
|
+
print('\n\r')
|
|
960
|
+
|
|
961
|
+
def log_mange():
|
|
962
|
+
print('#'*20,'日志管理','#'*20)
|
|
963
|
+
|
|
964
|
+
def reback():
|
|
965
|
+
pass
|
|
966
|
+
|
|
967
|
+
|
|
968
|
+
def show_system_log():
|
|
969
|
+
for i in log_list:
|
|
970
|
+
print(i)
|
|
971
|
+
|
|
972
|
+
|
|
973
|
+
def show_dict_connect_info():
|
|
974
|
+
for i in dict_connect_info:
|
|
975
|
+
tem_dict = {}
|
|
976
|
+
if dict_connect_info[i]['con_type'] in ['push_mode','plus_mode']:
|
|
977
|
+
id_code = i
|
|
978
|
+
tem_dict['待推送数据量'] = dict_client_que_id_code[id_code].qsize()
|
|
979
|
+
tem_dict.update(dict_connect_info[i])
|
|
980
|
+
print(i,':',tem_dict)
|
|
981
|
+
|
|
982
|
+
def query_id_code_info():
|
|
983
|
+
while True:
|
|
984
|
+
print('当前有的id_code:',list(dict_connect_info.keys()))
|
|
985
|
+
id_code = int(input('请输入id_code(0-----返回):'))
|
|
986
|
+
if id_code in dict_connect_info:
|
|
987
|
+
i = id_code
|
|
988
|
+
tem_dict = {}
|
|
989
|
+
if dict_connect_info[i]['con_type'] in ['push_mode','plus_mode']:
|
|
990
|
+
id_code = i
|
|
991
|
+
tem_dict['待推送数据量'] = dict_client_que_id_code[id_code].qsize()
|
|
992
|
+
tem_dict.update(dict_connect_info[i])
|
|
993
|
+
print(i,':',tem_dict)
|
|
994
|
+
elif id_code == 0:
|
|
995
|
+
break
|
|
996
|
+
else:
|
|
997
|
+
print('%sid_code不存在'%(id_code))
|
|
998
|
+
|
|
999
|
+
def index():
|
|
1000
|
+
while True:
|
|
1001
|
+
print('【LTtx系统管理面板首页】:')
|
|
1002
|
+
choice_dict = {'1----信道管理':channel_mange,
|
|
1003
|
+
'2----日志管理':log_mange,
|
|
1004
|
+
'3----连接管理':connect_mange,
|
|
1005
|
+
'4----查看系统最近日志':show_system_log,
|
|
1006
|
+
'5----查看所有连接信息':show_dict_connect_info,
|
|
1007
|
+
'6----按id_code查看连接信息':query_id_code_info,
|
|
1008
|
+
'99----返回':reback,
|
|
1009
|
+
}
|
|
1010
|
+
cmd_dict = {}
|
|
1011
|
+
for i in choice_dict:
|
|
1012
|
+
cmd_dict[i.split('-')[0]] = choice_dict[i]
|
|
1013
|
+
print(i)
|
|
1014
|
+
cmd = input('输入指令:')
|
|
1015
|
+
if cmd == '99':
|
|
1016
|
+
pass
|
|
1017
|
+
if cmd not in cmd_dict:
|
|
1018
|
+
print('当前指令不存在')
|
|
1019
|
+
continue
|
|
1020
|
+
print('【info指令运行结果】')
|
|
1021
|
+
cmd_dict[cmd]()
|
|
1022
|
+
print('\n\r')
|
|
1023
|
+
|
|
1024
|
+
|
|
1025
|
+
|
|
1026
|
+
|
|
1027
|
+
|
|
1028
|
+
def main_control():
|
|
1029
|
+
'''
|
|
1030
|
+
控制面板
|
|
1031
|
+
'''
|
|
1032
|
+
index()
|
|
1033
|
+
|
|
1034
|
+
|
|
1035
|
+
|
|
1036
|
+
def make_dir():
|
|
1037
|
+
flod_list = [_lttx_file_data_dir(), _lttx_dataframe_data_dir()]
|
|
1038
|
+
for i in flod_list:
|
|
1039
|
+
try:
|
|
1040
|
+
os.makedirs(i, exist_ok=True)
|
|
1041
|
+
except:
|
|
1042
|
+
pass
|
|
1043
|
+
|
|
1044
|
+
def main_save_dict_var():
|
|
1045
|
+
global dict_var_on, dict_var
|
|
1046
|
+
file_path = _lttx_dict_var_path()
|
|
1047
|
+
|
|
1048
|
+
while True:
|
|
1049
|
+
if dict_var_on:
|
|
1050
|
+
dict_var_on = False # 重置标志
|
|
1051
|
+
with open(file_path, 'w', encoding='utf-8') as f:
|
|
1052
|
+
json.dump(dict_var, f, ensure_ascii=False, indent=4)
|
|
1053
|
+
print(f"dict_var saved to {file_path}")
|
|
1054
|
+
time.sleep(60) # 每 60 秒检查一次
|
|
1055
|
+
|
|
1056
|
+
|
|
1057
|
+
def main_zmq_mode(zmq_xsub_port,zmq_xpub_port):
|
|
1058
|
+
import zmq
|
|
1059
|
+
context = zmq.Context()
|
|
1060
|
+
# 创建并绑定发布者套接字
|
|
1061
|
+
frontend = context.socket(zmq.XSUB)
|
|
1062
|
+
frontend.setsockopt(zmq.SNDHWM, 10000000)
|
|
1063
|
+
frontend.setsockopt(zmq.RCVHWM, 10000000)
|
|
1064
|
+
frontend.bind("tcp://*:%s"%(zmq_xsub_port))
|
|
1065
|
+
|
|
1066
|
+
# 创建并绑定订阅者套接字
|
|
1067
|
+
backend = context.socket(zmq.XPUB)
|
|
1068
|
+
backend.setsockopt(zmq.SNDHWM, 10000000)
|
|
1069
|
+
backend.setsockopt(zmq.RCVHWM, 10000000)
|
|
1070
|
+
backend.bind("tcp://*:%s"%(zmq_xpub_port))
|
|
1071
|
+
print("tcp://*:%s"%(zmq_xpub_port))
|
|
1072
|
+
print('zmq启动成功')
|
|
1073
|
+
# 启动代理
|
|
1074
|
+
zmq.proxy(frontend, backend)
|
|
1075
|
+
|
|
1076
|
+
def load_config():
|
|
1077
|
+
tem_dict = {}
|
|
1078
|
+
config_path = os.path.join(os.path.dirname(__file__), 'Config.txt')
|
|
1079
|
+
with open(config_path, 'r', encoding='utf-8') as f:
|
|
1080
|
+
data = f.read().split('\n')
|
|
1081
|
+
for i in data:
|
|
1082
|
+
if len(i) > 0 and ' ' in i and ':' in i:
|
|
1083
|
+
key = i.split(' ')[0].split(':')[0]
|
|
1084
|
+
value = i.split(' ')[0].split(':')[1]
|
|
1085
|
+
tem_dict[key] = value
|
|
1086
|
+
return tem_dict
|
|
1087
|
+
|
|
1088
|
+
def tcp_port_open(host, port, timeout=0.3):
|
|
1089
|
+
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
1090
|
+
sock.settimeout(timeout)
|
|
1091
|
+
try:
|
|
1092
|
+
return sock.connect_ex((host, int(port))) == 0
|
|
1093
|
+
except Exception:
|
|
1094
|
+
return False
|
|
1095
|
+
finally:
|
|
1096
|
+
try:
|
|
1097
|
+
sock.close()
|
|
1098
|
+
except Exception:
|
|
1099
|
+
pass
|
|
1100
|
+
|
|
1101
|
+
def main():
|
|
1102
|
+
global tocken,ip,port,self_version
|
|
1103
|
+
self_version = 'V7.2.7'
|
|
1104
|
+
update_time = '2025-09-04'
|
|
1105
|
+
import multiprocessing
|
|
1106
|
+
config = load_config()
|
|
1107
|
+
port = int(config['port'])
|
|
1108
|
+
tocken = config['token']
|
|
1109
|
+
ip = get_local_ip()
|
|
1110
|
+
if tcp_port_open('127.0.0.1', port):
|
|
1111
|
+
print('LTtx server port %s already listening, skip duplicate start.' % port)
|
|
1112
|
+
return
|
|
1113
|
+
#ZMQ的订阅端口
|
|
1114
|
+
zmq_xsub_port = config['zmq_port1']
|
|
1115
|
+
#ZMQ的发布端口
|
|
1116
|
+
zmq_xpub_port = config['zmq_port2']
|
|
1117
|
+
zmq_mode = config['zmq_mode']
|
|
1118
|
+
set_cpu = config['set_cpu']
|
|
1119
|
+
if zmq_mode == "True":
|
|
1120
|
+
p0 = multiprocessing.Process(target=main_zmq_mode,args=(zmq_xsub_port,zmq_xpub_port))
|
|
1121
|
+
p0.daemon = True
|
|
1122
|
+
p0.start()
|
|
1123
|
+
|
|
1124
|
+
if set_cpu:
|
|
1125
|
+
# 创建一个Process对象,代表当前进程
|
|
1126
|
+
p = psutil.Process(os.getpid())
|
|
1127
|
+
|
|
1128
|
+
# 将当前进程的CPU亲和性设置为只运行最后一颗CPU上
|
|
1129
|
+
p.cpu_affinity([p.cpu_affinity()[-1]])
|
|
1130
|
+
|
|
1131
|
+
recv_buffer_size = 1024*1024*100
|
|
1132
|
+
server = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
|
|
1133
|
+
server.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, recv_buffer_size,)
|
|
1134
|
+
server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
|
1135
|
+
server.settimeout(5)
|
|
1136
|
+
server.bind(('0.0.0.0',port))
|
|
1137
|
+
server.listen(5)
|
|
1138
|
+
print('\n\r')
|
|
1139
|
+
print('#'*20,'服务器信息%s'%(self_version),'#'*20)
|
|
1140
|
+
print('#'*20,'更新日期%s'%(update_time),'#'*20)
|
|
1141
|
+
print('ip:%s'%(ip),' '*10,'port:%s'%(port),' '*10,'tocken:%s'%(tocken))
|
|
1142
|
+
# threading.Thread(target=main_show).start()
|
|
1143
|
+
threading.Thread(target = main_heartbeat).start()
|
|
1144
|
+
threading.Thread(target = main_save_dict_var).start()
|
|
1145
|
+
threading.Thread(target = main_handle_put).start()
|
|
1146
|
+
wait_accept(server)
|
|
1147
|
+
|
|
1148
|
+
#配置
|
|
1149
|
+
connect_show_list = []
|
|
1150
|
+
dict_var_on = False
|
|
1151
|
+
make_dir()
|
|
1152
|
+
dict_client_que = {}
|
|
1153
|
+
dict_client_push = {}
|
|
1154
|
+
dict_client_push_group = {}
|
|
1155
|
+
dict_client = {}
|
|
1156
|
+
dict_client_status = {}
|
|
1157
|
+
dict_var = load_dict_var()#保存云变量
|
|
1158
|
+
|
|
1159
|
+
if __name__ == '__main__':
|
|
1160
|
+
threading.Thread(target = main_control).start()
|
|
1161
|
+
main()
|
|
1162
|
+
# threading.Thread(target = main).start()
|
|
1163
|
+
|
|
1164
|
+
|
|
1165
|
+
|
|
1166
|
+
|
|
1167
|
+
|
|
1168
|
+
|
|
1169
|
+
|
|
1170
|
+
|
|
1171
|
+
|
|
1172
|
+
|
|
1173
|
+
|
|
1174
|
+
|
|
1175
|
+
|
|
1176
|
+
|
|
1177
|
+
|