fixcore-engine 0.1.4__tar.gz → 0.1.6__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {fixcore_engine-0.1.4/fixcore_engine.egg-info → fixcore_engine-0.1.6}/PKG-INFO +1 -1
- {fixcore_engine-0.1.4 → fixcore_engine-0.1.6}/fixcore/session/session.py +16 -0
- {fixcore_engine-0.1.4 → fixcore_engine-0.1.6}/fixcore/transport/initiator.py +17 -3
- {fixcore_engine-0.1.4 → fixcore_engine-0.1.6/fixcore_engine.egg-info}/PKG-INFO +1 -1
- {fixcore_engine-0.1.4 → fixcore_engine-0.1.6}/pyproject.toml +1 -1
- {fixcore_engine-0.1.4 → fixcore_engine-0.1.6}/LICENSE +0 -0
- {fixcore_engine-0.1.4 → fixcore_engine-0.1.6}/README.md +0 -0
- {fixcore_engine-0.1.4 → fixcore_engine-0.1.6}/fixcore/__init__.py +0 -0
- {fixcore_engine-0.1.4 → fixcore_engine-0.1.6}/fixcore/application.py +0 -0
- {fixcore_engine-0.1.4 → fixcore_engine-0.1.6}/fixcore/gui.py +0 -0
- {fixcore_engine-0.1.4 → fixcore_engine-0.1.6}/fixcore/gui_ui/app.js +0 -0
- {fixcore_engine-0.1.4 → fixcore_engine-0.1.6}/fixcore/gui_ui/fixcore_logo.svg +0 -0
- {fixcore_engine-0.1.4 → fixcore_engine-0.1.6}/fixcore/gui_ui/index.html +0 -0
- {fixcore_engine-0.1.4 → fixcore_engine-0.1.6}/fixcore/gui_ui/style.css +0 -0
- {fixcore_engine-0.1.4 → fixcore_engine-0.1.6}/fixcore/log/__init__.py +0 -0
- {fixcore_engine-0.1.4 → fixcore_engine-0.1.6}/fixcore/log/base.py +0 -0
- {fixcore_engine-0.1.4 → fixcore_engine-0.1.6}/fixcore/log/factory.py +0 -0
- {fixcore_engine-0.1.4 → fixcore_engine-0.1.6}/fixcore/log/file_log.py +0 -0
- {fixcore_engine-0.1.4 → fixcore_engine-0.1.6}/fixcore/log/screen.py +0 -0
- {fixcore_engine-0.1.4 → fixcore_engine-0.1.6}/fixcore/message/__init__.py +0 -0
- {fixcore_engine-0.1.4 → fixcore_engine-0.1.6}/fixcore/message/cracker.py +0 -0
- {fixcore_engine-0.1.4 → fixcore_engine-0.1.6}/fixcore/message/data_dictionary.py +0 -0
- {fixcore_engine-0.1.4 → fixcore_engine-0.1.6}/fixcore/message/exceptions.py +0 -0
- {fixcore_engine-0.1.4 → fixcore_engine-0.1.6}/fixcore/message/field.py +0 -0
- {fixcore_engine-0.1.4 → fixcore_engine-0.1.6}/fixcore/message/message.py +0 -0
- {fixcore_engine-0.1.4 → fixcore_engine-0.1.6}/fixcore/session/__init__.py +0 -0
- {fixcore_engine-0.1.4 → fixcore_engine-0.1.6}/fixcore/session/session_id.py +0 -0
- {fixcore_engine-0.1.4 → fixcore_engine-0.1.6}/fixcore/session/session_settings.py +0 -0
- {fixcore_engine-0.1.4 → fixcore_engine-0.1.6}/fixcore/session/state.py +0 -0
- {fixcore_engine-0.1.4 → fixcore_engine-0.1.6}/fixcore/store/__init__.py +0 -0
- {fixcore_engine-0.1.4 → fixcore_engine-0.1.6}/fixcore/store/base.py +0 -0
- {fixcore_engine-0.1.4 → fixcore_engine-0.1.6}/fixcore/store/factory.py +0 -0
- {fixcore_engine-0.1.4 → fixcore_engine-0.1.6}/fixcore/store/file_store.py +0 -0
- {fixcore_engine-0.1.4 → fixcore_engine-0.1.6}/fixcore/store/memory.py +0 -0
- {fixcore_engine-0.1.4 → fixcore_engine-0.1.6}/fixcore/transport/__init__.py +0 -0
- {fixcore_engine-0.1.4 → fixcore_engine-0.1.6}/fixcore/transport/acceptor.py +0 -0
- {fixcore_engine-0.1.4 → fixcore_engine-0.1.6}/fixcore/transport/framer.py +0 -0
- {fixcore_engine-0.1.4 → fixcore_engine-0.1.6}/fixcore_engine.egg-info/SOURCES.txt +0 -0
- {fixcore_engine-0.1.4 → fixcore_engine-0.1.6}/fixcore_engine.egg-info/dependency_links.txt +0 -0
- {fixcore_engine-0.1.4 → fixcore_engine-0.1.6}/fixcore_engine.egg-info/entry_points.txt +0 -0
- {fixcore_engine-0.1.4 → fixcore_engine-0.1.6}/fixcore_engine.egg-info/requires.txt +0 -0
- {fixcore_engine-0.1.4 → fixcore_engine-0.1.6}/fixcore_engine.egg-info/top_level.txt +0 -0
- {fixcore_engine-0.1.4 → fixcore_engine-0.1.6}/setup.cfg +0 -0
- {fixcore_engine-0.1.4 → fixcore_engine-0.1.6}/tests/test_cracker.py +0 -0
- {fixcore_engine-0.1.4 → fixcore_engine-0.1.6}/tests/test_data_dictionary.py +0 -0
- {fixcore_engine-0.1.4 → fixcore_engine-0.1.6}/tests/test_file_log.py +0 -0
- {fixcore_engine-0.1.4 → fixcore_engine-0.1.6}/tests/test_file_store.py +0 -0
- {fixcore_engine-0.1.4 → fixcore_engine-0.1.6}/tests/test_framer.py +0 -0
- {fixcore_engine-0.1.4 → fixcore_engine-0.1.6}/tests/test_integration.py +0 -0
- {fixcore_engine-0.1.4 → fixcore_engine-0.1.6}/tests/test_message.py +0 -0
- {fixcore_engine-0.1.4 → fixcore_engine-0.1.6}/tests/test_session.py +0 -0
- {fixcore_engine-0.1.4 → fixcore_engine-0.1.6}/tests/test_session_id.py +0 -0
- {fixcore_engine-0.1.4 → fixcore_engine-0.1.6}/tests/test_store.py +0 -0
- {fixcore_engine-0.1.4 → fixcore_engine-0.1.6}/tests/test_transport.py +0 -0
|
@@ -3,10 +3,13 @@
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
5
|
import asyncio
|
|
6
|
+
import logging
|
|
6
7
|
import time
|
|
7
8
|
from datetime import datetime, timezone
|
|
8
9
|
from typing import Awaitable, Callable
|
|
9
10
|
|
|
11
|
+
logger = logging.getLogger(__name__)
|
|
12
|
+
|
|
10
13
|
from fixcore.application import Application
|
|
11
14
|
from fixcore.log.base import Log
|
|
12
15
|
from fixcore.message.message import Message
|
|
@@ -111,17 +114,22 @@ class Session:
|
|
|
111
114
|
|
|
112
115
|
async def on_connect(self, send_fn: SendFn) -> None:
|
|
113
116
|
"""Called by transport when connection is established."""
|
|
117
|
+
logger.debug("[%s] on_connect: setting send_fn", self._id)
|
|
114
118
|
self._send_fn = send_fn
|
|
115
119
|
self._last_recv_time = time.monotonic()
|
|
116
120
|
|
|
117
121
|
if self._reset_on_logon:
|
|
122
|
+
logger.debug("[%s] on_connect: resetting store", self._id)
|
|
118
123
|
self._store.reset()
|
|
119
124
|
|
|
120
125
|
self._state = SessionState.LOGON_TIMEOUT
|
|
126
|
+
logger.debug("[%s] on_connect: starting timer loop", self._id)
|
|
121
127
|
self._heartbeat_task = asyncio.create_task(self._timer_loop())
|
|
122
128
|
|
|
123
129
|
if self._is_initiator:
|
|
130
|
+
logger.debug("[%s] on_connect: sending Logon", self._id)
|
|
124
131
|
await self._send_logon()
|
|
132
|
+
logger.debug("[%s] on_connect: Logon sent", self._id)
|
|
125
133
|
|
|
126
134
|
async def on_disconnect(self) -> None:
|
|
127
135
|
"""Called by transport when connection is lost."""
|
|
@@ -304,13 +312,16 @@ class Session:
|
|
|
304
312
|
# ------------------------------------------------------------------
|
|
305
313
|
|
|
306
314
|
async def _send_logon(self) -> None:
|
|
315
|
+
logger.debug("[%s] _send_logon: building Logon message", self._id)
|
|
307
316
|
msg = Message()
|
|
308
317
|
msg.header.set(TAG_MSG_TYPE, MSG_LOGON)
|
|
309
318
|
msg.set_field(TAG_ENCRYPT_METHOD, "0")
|
|
310
319
|
msg.set_field(TAG_HEART_BT_INT, str(self._heartbt_int))
|
|
311
320
|
self._app.to_admin(msg, self._id)
|
|
321
|
+
logger.debug("[%s] _send_logon: calling _send_message", self._id)
|
|
312
322
|
await self._send_message(msg, is_admin=True)
|
|
313
323
|
self._logon_sent_at = time.monotonic()
|
|
324
|
+
logger.debug("[%s] _send_logon: complete", self._id)
|
|
314
325
|
|
|
315
326
|
async def _send_logout(self, text: str = "") -> None:
|
|
316
327
|
msg = Message()
|
|
@@ -435,6 +446,7 @@ class Session:
|
|
|
435
446
|
msg.header.set(TAG_SENDING_TIME, _utc_timestamp())
|
|
436
447
|
|
|
437
448
|
raw = msg.encode()
|
|
449
|
+
logger.debug("[%s] _send_message: seq=%s len=%d", self._id, seq_num, len(raw))
|
|
438
450
|
self._store.set(seq_num, raw)
|
|
439
451
|
self._store.incr_next_sender_msg_seq_num()
|
|
440
452
|
self._last_send_time = time.monotonic()
|
|
@@ -442,7 +454,11 @@ class Session:
|
|
|
442
454
|
self._log.on_outgoing(raw.replace(b"\x01", b"|").decode("latin-1"))
|
|
443
455
|
|
|
444
456
|
if self._send_fn is not None:
|
|
457
|
+
logger.debug("[%s] _send_message: calling send_fn", self._id)
|
|
445
458
|
await self._send_fn(raw)
|
|
459
|
+
logger.debug("[%s] _send_message: send_fn returned", self._id)
|
|
460
|
+
else:
|
|
461
|
+
logger.warning("[%s] _send_message: send_fn is None — message not sent", self._id)
|
|
446
462
|
|
|
447
463
|
# ------------------------------------------------------------------
|
|
448
464
|
# Sequence number validation
|
|
@@ -3,8 +3,11 @@
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
5
|
import asyncio
|
|
6
|
+
import logging
|
|
6
7
|
|
|
7
8
|
from fixcore.application import Application
|
|
9
|
+
|
|
10
|
+
logger = logging.getLogger(__name__)
|
|
8
11
|
from fixcore.log.base import LogFactory
|
|
9
12
|
from fixcore.session.session import Session
|
|
10
13
|
from fixcore.session.session_id import SessionID
|
|
@@ -61,12 +64,14 @@ class SocketInitiator:
|
|
|
61
64
|
"""Launch a connect-loop task for each configured initiator session."""
|
|
62
65
|
self._stop_event.clear()
|
|
63
66
|
self._no_reconnect.clear()
|
|
67
|
+
logger.info("SocketInitiator starting %d session(s)", len(self._sessions))
|
|
64
68
|
for sid, session in self._sessions.items():
|
|
65
69
|
task = asyncio.create_task(
|
|
66
70
|
self._connect_loop(sid, session),
|
|
67
71
|
name=f"initiator-{sid}",
|
|
68
72
|
)
|
|
69
73
|
self._tasks.append(task)
|
|
74
|
+
logger.debug("Connect-loop task created for %s", sid)
|
|
70
75
|
|
|
71
76
|
async def stop(self) -> None:
|
|
72
77
|
"""Signal all connect loops to exit and wait for them to finish."""
|
|
@@ -108,14 +113,17 @@ class SocketInitiator:
|
|
|
108
113
|
port = int(self._settings.get(sid, "SocketConnectPort"))
|
|
109
114
|
reconnect = self._settings.get_int(sid, "ReconnectInterval", 10)
|
|
110
115
|
|
|
116
|
+
logger.info("[%s] Connect loop started — target %s:%s", sid, host, port)
|
|
111
117
|
while not self._stop_event.is_set():
|
|
118
|
+
logger.debug("[%s] Attempting connection to %s:%s", sid, host, port)
|
|
112
119
|
try:
|
|
113
120
|
reader, writer = await asyncio.open_connection(host, port)
|
|
114
|
-
except OSError:
|
|
115
|
-
|
|
121
|
+
except OSError as exc:
|
|
122
|
+
logger.warning("[%s] Connection failed: %s — retrying in %ss", sid, exc, reconnect)
|
|
116
123
|
await self._interruptible_sleep(reconnect)
|
|
117
124
|
continue
|
|
118
125
|
|
|
126
|
+
logger.info("[%s] TCP connected to %s:%s", sid, host, port)
|
|
119
127
|
framer = MessageFramer()
|
|
120
128
|
|
|
121
129
|
async def send_fn(data: bytes, _w: asyncio.StreamWriter = writer) -> None:
|
|
@@ -123,21 +131,26 @@ class SocketInitiator:
|
|
|
123
131
|
await _w.drain()
|
|
124
132
|
|
|
125
133
|
await session.on_connect(send_fn)
|
|
134
|
+
logger.debug("[%s] on_connect complete — Logon sent", sid)
|
|
126
135
|
|
|
127
136
|
try:
|
|
128
137
|
while not self._stop_event.is_set():
|
|
129
138
|
try:
|
|
130
139
|
data = await reader.read(4096)
|
|
131
140
|
except (ConnectionResetError, asyncio.IncompleteReadError,
|
|
132
|
-
asyncio.CancelledError):
|
|
141
|
+
asyncio.CancelledError) as exc:
|
|
142
|
+
logger.info("[%s] Read interrupted: %s", sid, type(exc).__name__)
|
|
133
143
|
break
|
|
134
144
|
if not data:
|
|
145
|
+
logger.info("[%s] Connection closed by peer", sid)
|
|
135
146
|
break
|
|
136
147
|
|
|
148
|
+
logger.debug("[%s] Received %d bytes", sid, len(data))
|
|
137
149
|
for raw_msg in framer.feed(data):
|
|
138
150
|
await session.on_data(raw_msg)
|
|
139
151
|
|
|
140
152
|
finally:
|
|
153
|
+
logger.info("[%s] Disconnecting", sid)
|
|
141
154
|
await session.on_disconnect()
|
|
142
155
|
try:
|
|
143
156
|
writer.close()
|
|
@@ -147,6 +160,7 @@ class SocketInitiator:
|
|
|
147
160
|
framer.reset()
|
|
148
161
|
|
|
149
162
|
if not self._stop_event.is_set() and not self._no_reconnect.is_set():
|
|
163
|
+
logger.info("[%s] Waiting %ss before reconnect", sid, reconnect)
|
|
150
164
|
await self._interruptible_sleep(reconnect)
|
|
151
165
|
|
|
152
166
|
async def _interruptible_sleep(self, seconds: float) -> None:
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|