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.
Files changed (90) hide show
  1. LTtx/__init__.py +2 -0
  2. LTtx/tx/Config.txt +8 -0
  3. LTtx/tx/LTtx_server.py +1177 -0
  4. LTtx/tx/__init__.py +5 -0
  5. LTtx/tx/tx.py +1935 -0
  6. cfquant/__init__.py +26 -0
  7. cfquant/account_routing.py +119 -0
  8. cfquant/channels.py +76 -0
  9. cfquant/cli.py +266 -0
  10. cfquant/client.py +365 -0
  11. cfquant/config.py +86 -0
  12. cfquant/hub.py +304 -0
  13. cfquant/logging_i18n.py +348 -0
  14. cfquant/normal_bridge.py +676 -0
  15. cfquant/pipe_bridge.py +249 -0
  16. cfquant/pipe_client.py +223 -0
  17. cfquant/pipe_hub.py +602 -0
  18. cfquant/pipe_transport.py +493 -0
  19. cfquant/protocol.py +238 -0
  20. cfquant/qmt_bridge.py +1081 -0
  21. cfquant/runtime_report.py +334 -0
  22. cfquant/tx.py +1898 -0
  23. cfquant/tx_trade_bridge.py +1905 -0
  24. cfquant/version.py +5 -0
  25. cfquant/xtconstant.py +262 -0
  26. cfquant/xtdata.py +462 -0
  27. cfquant/xttrader.py +955 -0
  28. cfquant/xttype.py +586 -0
  29. cfquant-0.2.0.dist-info/LICENSE +21 -0
  30. cfquant-0.2.0.dist-info/METADATA +144 -0
  31. cfquant-0.2.0.dist-info/RECORD +90 -0
  32. cfquant-0.2.0.dist-info/WHEEL +5 -0
  33. cfquant-0.2.0.dist-info/entry_points.txt +4 -0
  34. cfquant-0.2.0.dist-info/top_level.txt +6 -0
  35. cfquant_pipe_hub.py +45 -0
  36. cfquant_web_server.py +12137 -0
  37. qmt_scripts/CFQUANT.py +507 -0
  38. qmt_scripts/CFQUANT_CTYPE_ALL_LOWLAT.py +830 -0
  39. qmt_scripts/CFQUANT_LITE.py +4928 -0
  40. qmt_scripts/CFQUANT_TRADE_LOWLAT.py +382 -0
  41. qmt_scripts/__init__.py +1 -0
  42. 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
  43. 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
  44. 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
  45. 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
  46. 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
  47. 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
  48. 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
  49. web_dashboard/__init__.py +1 -0
  50. web_dashboard/app.js +9847 -0
  51. web_dashboard/avatars/amber-pulse.svg +15 -0
  52. web_dashboard/avatars/copper-grid.svg +15 -0
  53. web_dashboard/avatars/market-blue.svg +15 -0
  54. web_dashboard/avatars/rose-circuit.svg +15 -0
  55. web_dashboard/avatars/signal-green.svg +15 -0
  56. web_dashboard/avatars/slate-wave.svg +15 -0
  57. web_dashboard/avatars/teal-orbit.svg +16 -0
  58. web_dashboard/avatars/violet-node.svg +15 -0
  59. web_dashboard/favicon.svg +5 -0
  60. web_dashboard/image/qmt_deploy/image-10.png +0 -0
  61. web_dashboard/image/qmt_deploy/image-11.png +0 -0
  62. web_dashboard/image/qmt_deploy/image-12.png +0 -0
  63. web_dashboard/image/qmt_deploy/image-13.png +0 -0
  64. web_dashboard/image/qmt_deploy/image-14.png +0 -0
  65. web_dashboard/image/qmt_deploy/image-15.png +0 -0
  66. web_dashboard/image/qmt_deploy/image-16.png +0 -0
  67. web_dashboard/image/qmt_deploy/image-17.png +0 -0
  68. web_dashboard/image/qmt_deploy/image-18.png +0 -0
  69. web_dashboard/image/qmt_deploy/image-19.png +0 -0
  70. web_dashboard/image/qmt_deploy/image-20.png +0 -0
  71. web_dashboard/image/qmt_deploy/image-21.png +0 -0
  72. web_dashboard/image/qmt_deploy/image-4.png +0 -0
  73. web_dashboard/image/qmt_deploy/image-5.png +0 -0
  74. web_dashboard/image/qmt_deploy/image-6.png +0 -0
  75. web_dashboard/image/qmt_deploy/image-7.png +0 -0
  76. web_dashboard/image/qmt_deploy/image-8.png +0 -0
  77. web_dashboard/image/qmt_deploy/image-9.png +0 -0
  78. 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
  79. 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
  80. 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
  81. 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
  82. 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
  83. 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
  84. 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
  85. 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
  86. 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
  87. 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
  88. web_dashboard/index.html +2317 -0
  89. web_dashboard/styles.css +7817 -0
  90. 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
+