From d31495c98979813a25ad18e8d41d16a6ca7a1d85 Mon Sep 17 00:00:00 2001 From: denis Date: Sun, 5 Feb 2023 18:49:42 +0000 Subject: [PATCH] =?UTF-8?q?=D0=9E=D0=B1=D1=80=D0=B0=D0=B1=D0=BE=D1=82?= =?UTF-8?q?=D0=BA=D0=B0=20=D0=BE=D1=88=D0=B8=D0=B1=D0=BE=D0=BA=20=D0=BF?= =?UTF-8?q?=D0=BE=D0=B4=D0=BA=D0=BB=D1=8E=D1=87=D0=B5=D0=BD=D0=B8=D1=8F=20?= =?UTF-8?q?=D0=BA=20QUIK?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 1 + QuikPy.py | 63 ++++++++++++++++++++++++++++++++++++++++++------------ 2 files changed, 50 insertions(+), 14 deletions(-) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..bee8a64 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +__pycache__ diff --git a/QuikPy.py b/QuikPy.py index 9b9aec5..346e3eb 100644 --- a/QuikPy.py +++ b/QuikPy.py @@ -37,10 +37,14 @@ class QuikPy(metaclass=Singleton): # Singleton класс fragments = [] # Будем получать ответ в виде списка фрагментов. Они могут быть разной длины. Ответ может состоять из нескольких фрагментов while getattr(currentThread, 'process', True): # Пока поток нужен while True: # Пока есть что-то в буфере ответов - fragment = socketCallbacks.recv(self.bufferSize) # Читаем фрагмент из буфера - fragments.append(fragment.decode('cp1251')) # Переводим фрагмент в Windows кодировку 1251, добавляем в список - if len(fragment) < self.bufferSize: # Если в принятом фрагменте данных меньше чем размер буфера - break # то, возможно, это был последний фрагмент, выходим из чтения буфера + try: + fragment = socketCallbacks.recv(self.bufferSize) # Читаем фрагмент из буфера + except ConnectionResetError: + print("Соединение сброшено") + else: + fragments.append(fragment.decode('cp1251')) # Переводим фрагмент в Windows кодировку 1251, добавляем в список + if len(fragment) < self.bufferSize: # Если в принятом фрагменте данных меньше чем размер буфера + break # то, возможно, это был последний фрагмент, выходим из чтения буфера data = ''.join(fragments) # Собираем список фрагментов в строку dataList = data.split('\n') # Одновременно могут прийти несколько функций обратного вызова, разбираем их по одной fragments = [] # Сбрасываем фрагменты. Если последнюю строку не сможем разобрать, то занесем ее сюда @@ -111,10 +115,32 @@ class QuikPy(metaclass=Singleton): # Singleton класс def ProcessRequest(self, Request): """Отправляем запрос в QUIK, получаем ответ из QUIK""" rawData = json.dumps(Request) # Переводим запрос в формат JSON - self.socketRequests.sendall(f'{rawData}\r\n'.encode()) # Отправляем запрос в QUIK + # self.socketRequests.settimeout(1) + # print(self.socketRequests.timeout) + # self.socketRequests.sendall(f'{rawData}\r\n'.encode()) # Отправляем запрос в QUIK + try: + self.socketRequests.sendall(f'{rawData}\r\n'.encode()) # Отправляем запрос в QUIK + except Exception: + # raise TimeoutError("Истёк таймаут подключения к QUIK") + print("Истёк таймаут подключения к QUIK") + data = {"data": "error"} + return data fragments = [] # Гораздо быстрее получать ответ в виде списка фрагментов + while True: # Пока фрагменты есть в буфере - fragment = self.socketRequests.recv(self.bufferSize) # Читаем фрагмент из буфера + # print(fragments) + try: + fragment = self.socketRequests.recv(self.bufferSize) # Читаем фрагмент из буфера + assert len(fragment) > 0 + except (TimeoutError, AssertionError): + print("Истёк таймаут подключения к QUIK") + data = {"data": "error"} + return data + except ConnectionResetError: + print("Соединение сброшено") + data = {"data": "error"} + return data + fragments.append(fragment.decode('cp1251')) # Переводим фрагмент в Windows кодировку 1251, добавляем в список if len(fragment) < self.bufferSize: # Если в принятом фрагменте данных меньше чем размер буфера data = ''.join(fragments) # Собираем список фрагментов в строку @@ -125,7 +151,7 @@ class QuikPy(metaclass=Singleton): # Singleton класс # Инициализация и вход - def __init__(self, Host='127.0.0.1', RequestsPort=34130, CallbacksPort=34131): + def __init__(self, Host='127.0.0.1', RequestsPort=34130, CallbacksPort=34131, connection_timeout=None): """Инициализация""" # 2.2. Функции обратного вызова self.OnFirm = self.DefaultHandler # 1. Новая фирма @@ -162,17 +188,25 @@ class QuikPy(metaclass=Singleton): # Singleton класс self.RequestsPort = RequestsPort # Порт для отправки запросов и получения ответов self.CallbacksPort = CallbacksPort # Порт для функций обратного вызова self.socketRequests = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # Создаем соединение для запросов - self.socketRequests.connect((self.Host, self.RequestsPort)) # Открываем соединение для запросов + if connection_timeout is not None: + self.socketRequests.settimeout(connection_timeout) + self.callbackThread = threading.Thread(target=self.CallbackHandler, + name='CallbackThread') # Создаем поток обработки функций обратного вызова + + try: + self.socketRequests.connect((self.Host, self.RequestsPort)) # Открываем соединение для запросов + except TimeoutError: + raise ConnectionError("Истёк таймаут подключения к QUIK") + else: + self.callbackThread.start() # Запускаем поток - self.callbackThread = threading.Thread(target=self.CallbackHandler, name='CallbackThread') # Создаем поток обработки функций обратного вызова - self.callbackThread.start() # Запускаем поток def __enter__(self): """Вход в класс, например, с with""" return self # Фукнции связи с QuikSharp - + def Ping(self, TransId=0): """Проверка соединения. Отправка ping. Получение pong""" return self.ProcessRequest({'data': 'Ping', 'id': TransId, 'cmd': 'ping', 't': ''}) @@ -221,7 +255,7 @@ class QuikPy(metaclass=Singleton): # Singleton класс # isDarkTheme - 9. Тема оформления. true - тёмная, false - светлая # Сервисные функции QuikSharp - + def MessageInfo(self, Message, TransId=0): # В QUIK LUA message icon_type=1 """Отправка информационного сообщения в терминал QUIK""" return self.ProcessRequest({'data': Message, 'id': TransId, 'cmd': 'message', 't': ''}) @@ -250,7 +284,7 @@ class QuikPy(metaclass=Singleton): # Singleton класс def GetTradeAccount(self, ClassCode, TransId=0): """Торговый счет для запрашиваемого кода класса""" return self.ProcessRequest({'data': ClassCode, 'id': TransId, 'cmd': 'getTradeAccount', 't': ''}) - + def GetAllOrders(self, TransId=0): """Таблица заявок (вся)""" return self.ProcessRequest({'data': f'', 'id': TransId, 'cmd': 'get_orders', 't': ''}) @@ -332,7 +366,7 @@ class QuikPy(metaclass=Singleton): # Singleton класс def GetClassSecurities(self, ClassCode, TransId=0): # 3 """Список инструментов класса""" return self.ProcessRequest({'data': ClassCode, 'id': TransId, 'cmd': 'getClassSecurities', 't': ''}) - + # Функции для обращения к спискам доступных параметров QuikSharp def GetOptionBoard(self, ClassCode, SecCode, TransId=0): @@ -590,3 +624,4 @@ class QuikPy(metaclass=Singleton): # Singleton класс def __del__(self): self.CloseConnectionAndThread() # Закрываем соединение для запросов и поток обработки функций обратного вызова +