conson-xp 0.11.21__py3-none-any.whl → 1.0.1__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.
- {conson_xp-0.11.21.dist-info → conson_xp-1.0.1.dist-info}/METADATA +1 -1
- {conson_xp-0.11.21.dist-info → conson_xp-1.0.1.dist-info}/RECORD +20 -21
- xp/__init__.py +1 -1
- xp/cli/commands/conbus/conbus_config_commands.py +3 -12
- xp/cli/commands/conbus/conbus_linknumber_commands.py +24 -11
- xp/cli/commands/conbus/conbus_output_commands.py +44 -18
- xp/models/conbus/conbus.py +12 -11
- xp/models/conbus/conbus_linknumber.py +1 -1
- xp/services/conbus/conbus_autoreport_get_service.py +29 -80
- xp/services/conbus/conbus_datapoint_service.py +6 -1
- xp/services/conbus/conbus_lightlevel_get_service.py +36 -84
- xp/services/conbus/conbus_linknumber_get_service.py +86 -0
- xp/services/conbus/conbus_linknumber_set_service.py +155 -0
- xp/services/conbus/conbus_output_service.py +129 -92
- xp/services/conbus/conbus_scan_service.py +94 -98
- xp/services/protocol/conbus_protocol.py +1 -0
- xp/utils/dependencies.py +21 -44
- xp/services/conbus/conbus_connection_pool.py +0 -148
- xp/services/conbus/conbus_linknumber_service.py +0 -197
- xp/services/conbus/conbus_service.py +0 -306
- {conson_xp-0.11.21.dist-info → conson_xp-1.0.1.dist-info}/WHEEL +0 -0
- {conson_xp-0.11.21.dist-info → conson_xp-1.0.1.dist-info}/entry_points.txt +0 -0
- {conson_xp-0.11.21.dist-info → conson_xp-1.0.1.dist-info}/licenses/LICENSE +0 -0
|
@@ -5,124 +5,120 @@ various types of telegrams including discover, version, and sensor data requests
|
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
7
|
import logging
|
|
8
|
-
import
|
|
9
|
-
from typing import
|
|
8
|
+
from datetime import datetime
|
|
9
|
+
from typing import Callable, Optional
|
|
10
|
+
|
|
11
|
+
from twisted.internet.posixbase import PosixReactorBase
|
|
10
12
|
|
|
11
13
|
from xp.models import (
|
|
12
|
-
|
|
14
|
+
ConbusClientConfig,
|
|
13
15
|
ConbusResponse,
|
|
14
16
|
)
|
|
15
|
-
from xp.
|
|
16
|
-
from xp.services.
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
class ConbusScanError(Exception):
|
|
20
|
-
"""Raised when Conbus client send operations fail"""
|
|
17
|
+
from xp.models.protocol.conbus_protocol import TelegramReceivedEvent
|
|
18
|
+
from xp.services.protocol import ConbusProtocol
|
|
21
19
|
|
|
22
|
-
pass
|
|
23
20
|
|
|
24
|
-
|
|
25
|
-
class ConbusScanService:
|
|
21
|
+
class ConbusScanService(ConbusProtocol):
|
|
26
22
|
"""
|
|
27
|
-
|
|
23
|
+
Service for querying datapoints from Conbus modules.
|
|
28
24
|
|
|
29
|
-
|
|
30
|
-
and
|
|
25
|
+
Uses ConbusProtocol to provide datapoint query functionality
|
|
26
|
+
for reading sensor data and module information.
|
|
31
27
|
"""
|
|
32
28
|
|
|
33
29
|
def __init__(
|
|
34
30
|
self,
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
):
|
|
38
|
-
"""Initialize the Conbus
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
31
|
+
cli_config: ConbusClientConfig,
|
|
32
|
+
reactor: PosixReactorBase,
|
|
33
|
+
) -> None:
|
|
34
|
+
"""Initialize the Conbus datapoint service"""
|
|
35
|
+
super().__init__(cli_config, reactor)
|
|
36
|
+
self.serial_number: str = ""
|
|
37
|
+
self.function_code: str = ""
|
|
38
|
+
self.datapoint_value: int = -1
|
|
39
|
+
self.progress_callback: Optional[Callable[[str], None]] = None
|
|
40
|
+
self.finish_callback: Optional[Callable[[ConbusResponse], None]] = None
|
|
41
|
+
self.service_response: ConbusResponse = ConbusResponse(
|
|
42
|
+
success=False,
|
|
43
|
+
serial_number=self.serial_number,
|
|
44
|
+
sent_telegrams=[],
|
|
45
|
+
received_telegrams=[],
|
|
46
|
+
timestamp=datetime.now(),
|
|
47
|
+
)
|
|
49
48
|
# Set up logging
|
|
50
49
|
self.logger = logging.getLogger(__name__)
|
|
51
50
|
|
|
52
|
-
def
|
|
53
|
-
self,
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
# Call progress callback with error response
|
|
95
|
-
if progress_callback:
|
|
96
|
-
progress_callback(error_response, count, total_combinations)
|
|
97
|
-
|
|
98
|
-
return results
|
|
51
|
+
def connection_established(self) -> None:
|
|
52
|
+
self.logger.debug("Connection established, starting scan")
|
|
53
|
+
self.scan_next_datacode()
|
|
54
|
+
|
|
55
|
+
def scan_next_datacode(self) -> bool:
|
|
56
|
+
self.datapoint_value += 1
|
|
57
|
+
if self.datapoint_value >= 100:
|
|
58
|
+
if self.finish_callback:
|
|
59
|
+
self.finish_callback(self.service_response)
|
|
60
|
+
return False
|
|
61
|
+
|
|
62
|
+
self.logger.debug(f"Scanning next datacode: {self.datapoint_value:02d}")
|
|
63
|
+
data = f"{self.datapoint_value:02d}"
|
|
64
|
+
telegram_body = f"S{self.serial_number}F{self.function_code}D{data}"
|
|
65
|
+
self.sendFrame(telegram_body.encode())
|
|
66
|
+
return True
|
|
67
|
+
|
|
68
|
+
def telegram_sent(self, telegram_sent: str) -> None:
|
|
69
|
+
self.service_response.success = True
|
|
70
|
+
self.service_response.sent_telegrams.append(telegram_sent)
|
|
71
|
+
|
|
72
|
+
def telegram_received(self, telegram_received: TelegramReceivedEvent) -> None:
|
|
73
|
+
self.logger.debug(f"Telegram received: {telegram_received}")
|
|
74
|
+
if not self.service_response.received_telegrams:
|
|
75
|
+
self.service_response.received_telegrams = []
|
|
76
|
+
self.service_response.received_telegrams.append(telegram_received.frame)
|
|
77
|
+
|
|
78
|
+
if self.progress_callback:
|
|
79
|
+
self.progress_callback(telegram_received.frame)
|
|
80
|
+
|
|
81
|
+
def timeout(self) -> bool:
|
|
82
|
+
self.logger.debug(f"Timeout: {self.timeout_seconds}s")
|
|
83
|
+
continue_scan = self.scan_next_datacode()
|
|
84
|
+
return continue_scan
|
|
85
|
+
|
|
86
|
+
def failed(self, message: str) -> None:
|
|
87
|
+
self.logger.debug(f"Failed with message: {message}")
|
|
88
|
+
self.service_response.success = False
|
|
89
|
+
self.service_response.timestamp = datetime.now()
|
|
90
|
+
self.service_response.error = message
|
|
91
|
+
if self.finish_callback:
|
|
92
|
+
self.finish_callback(self.service_response)
|
|
99
93
|
|
|
100
94
|
def scan_module_background(
|
|
101
95
|
self,
|
|
102
96
|
serial_number: str,
|
|
103
97
|
function_code: str,
|
|
104
|
-
progress_callback:
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
return self.scan_module(serial_number, function_code, progress_callback)
|
|
111
|
-
|
|
112
|
-
# Start background thread
|
|
113
|
-
scan_thread = threading.Thread(target=background_scan, daemon=True)
|
|
114
|
-
scan_thread.start()
|
|
98
|
+
progress_callback: Callable[[str], None],
|
|
99
|
+
finish_callback: Callable[[ConbusResponse], None],
|
|
100
|
+
timeout_seconds: Optional[float] = None,
|
|
101
|
+
) -> None:
|
|
102
|
+
"""
|
|
103
|
+
Query a specific datapoint from a module.
|
|
115
104
|
|
|
116
|
-
|
|
105
|
+
Args:
|
|
106
|
+
serial_number: 10-digit module serial number
|
|
107
|
+
function_code: the function code to scan
|
|
108
|
+
progress_callback: callback to handle progress
|
|
109
|
+
finish_callback: callback function to call when the datapoint is received
|
|
110
|
+
timeout_seconds: timeout in seconds
|
|
111
|
+
|
|
112
|
+
Returns:
|
|
113
|
+
ConbusDatapointResponse with operation result and datapoint value
|
|
114
|
+
"""
|
|
117
115
|
|
|
118
|
-
|
|
119
|
-
|
|
116
|
+
self.logger.info("Starting query_datapoint")
|
|
117
|
+
if timeout_seconds:
|
|
118
|
+
self.timeout_seconds = timeout_seconds
|
|
120
119
|
|
|
121
|
-
|
|
122
|
-
self
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
) -> None:
|
|
127
|
-
# Cleanup logic if needed
|
|
128
|
-
pass
|
|
120
|
+
self.serial_number = serial_number
|
|
121
|
+
self.function_code = function_code
|
|
122
|
+
self.progress_callback = progress_callback
|
|
123
|
+
self.finish_callback = finish_callback
|
|
124
|
+
self.start_reactor()
|
|
@@ -145,6 +145,7 @@ class ConbusProtocol(protocol.Protocol, protocol.ClientFactory):
|
|
|
145
145
|
self._stop_reactor()
|
|
146
146
|
|
|
147
147
|
def timeout(self) -> bool:
|
|
148
|
+
"""Timeout callback, return True to continue waiting for next timeout, False to stop"""
|
|
148
149
|
self.logger.info("Timeout after: %ss", self.timeout_seconds)
|
|
149
150
|
self.failed(f"Timeout after: {self.timeout_seconds}s")
|
|
150
151
|
return False
|
xp/utils/dependencies.py
CHANGED
|
@@ -25,10 +25,6 @@ from xp.services.conbus.conbus_autoreport_get_service import ConbusAutoreportGet
|
|
|
25
25
|
from xp.services.conbus.conbus_autoreport_set_service import ConbusAutoreportSetService
|
|
26
26
|
from xp.services.conbus.conbus_blink_all_service import ConbusBlinkAllService
|
|
27
27
|
from xp.services.conbus.conbus_blink_service import ConbusBlinkService
|
|
28
|
-
from xp.services.conbus.conbus_connection_pool import (
|
|
29
|
-
ConbusConnectionPool,
|
|
30
|
-
ConbusSocketConnectionManager,
|
|
31
|
-
)
|
|
32
28
|
from xp.services.conbus.conbus_custom_service import ConbusCustomService
|
|
33
29
|
from xp.services.conbus.conbus_datapoint_queryall_service import (
|
|
34
30
|
ConbusDatapointQueryAllService,
|
|
@@ -38,12 +34,12 @@ from xp.services.conbus.conbus_datapoint_service import (
|
|
|
38
34
|
)
|
|
39
35
|
from xp.services.conbus.conbus_discover_service import ConbusDiscoverService
|
|
40
36
|
from xp.services.conbus.conbus_lightlevel_set_service import ConbusLightlevelSetService
|
|
41
|
-
from xp.services.conbus.
|
|
37
|
+
from xp.services.conbus.conbus_linknumber_get_service import ConbusLinknumberGetService
|
|
38
|
+
from xp.services.conbus.conbus_linknumber_set_service import ConbusLinknumberSetService
|
|
42
39
|
from xp.services.conbus.conbus_output_service import ConbusOutputService
|
|
43
40
|
from xp.services.conbus.conbus_raw_service import ConbusRawService
|
|
44
41
|
from xp.services.conbus.conbus_receive_service import ConbusReceiveService
|
|
45
42
|
from xp.services.conbus.conbus_scan_service import ConbusScanService
|
|
46
|
-
from xp.services.conbus.conbus_service import ConbusService
|
|
47
43
|
from xp.services.homekit.homekit_cache_service import HomeKitCacheService
|
|
48
44
|
from xp.services.homekit.homekit_conbus_service import HomeKitConbusService
|
|
49
45
|
from xp.services.homekit.homekit_dimminglight_service import HomeKitDimmingLightService
|
|
@@ -106,30 +102,13 @@ class ServiceContainer:
|
|
|
106
102
|
def _register_services(self) -> None:
|
|
107
103
|
"""Register all services in the container based on dependency graph."""
|
|
108
104
|
|
|
109
|
-
# ConbusClientConfig
|
|
105
|
+
# ConbusClientConfig
|
|
110
106
|
self.container.register(
|
|
111
107
|
ConbusClientConfig,
|
|
112
108
|
factory=lambda: ConbusClientConfig.from_yaml(self._config_path),
|
|
113
109
|
scope=punq.Scope.singleton,
|
|
114
110
|
)
|
|
115
111
|
|
|
116
|
-
# Core infrastructure layer - ConbusConnectionPool (singleton)
|
|
117
|
-
self.container.register(
|
|
118
|
-
ConbusSocketConnectionManager,
|
|
119
|
-
factory=lambda: ConbusSocketConnectionManager(
|
|
120
|
-
cli_config=self.container.resolve(ConbusClientConfig)
|
|
121
|
-
),
|
|
122
|
-
scope=punq.Scope.singleton,
|
|
123
|
-
)
|
|
124
|
-
|
|
125
|
-
self.container.register(
|
|
126
|
-
ConbusConnectionPool,
|
|
127
|
-
factory=lambda: ConbusConnectionPool(
|
|
128
|
-
connection_manager=self.container.resolve(ConbusSocketConnectionManager)
|
|
129
|
-
),
|
|
130
|
-
scope=punq.Scope.singleton,
|
|
131
|
-
)
|
|
132
|
-
|
|
133
112
|
# Telegram services layer
|
|
134
113
|
self.container.register(TelegramService, scope=punq.Scope.singleton)
|
|
135
114
|
self.container.register(
|
|
@@ -143,16 +122,6 @@ class ServiceContainer:
|
|
|
143
122
|
self.container.register(TelegramBlinkService, scope=punq.Scope.singleton)
|
|
144
123
|
self.container.register(LinkNumberService, scope=punq.Scope.singleton)
|
|
145
124
|
|
|
146
|
-
# ConbusService - depends on ConbusConnectionPool
|
|
147
|
-
self.container.register(
|
|
148
|
-
ConbusService,
|
|
149
|
-
factory=lambda: ConbusService(
|
|
150
|
-
client_config=self.container.resolve(ConbusClientConfig),
|
|
151
|
-
connection_pool=self.container.resolve(ConbusConnectionPool),
|
|
152
|
-
),
|
|
153
|
-
scope=punq.Scope.singleton,
|
|
154
|
-
)
|
|
155
|
-
|
|
156
125
|
# Conbus services layer
|
|
157
126
|
self.container.register(
|
|
158
127
|
ConbusDatapointService,
|
|
@@ -177,8 +146,8 @@ class ServiceContainer:
|
|
|
177
146
|
self.container.register(
|
|
178
147
|
ConbusScanService,
|
|
179
148
|
factory=lambda: ConbusScanService(
|
|
180
|
-
|
|
181
|
-
|
|
149
|
+
cli_config=self.container.resolve(ConbusClientConfig),
|
|
150
|
+
reactor=self.container.resolve(PosixReactorBase),
|
|
182
151
|
),
|
|
183
152
|
scope=punq.Scope.singleton,
|
|
184
153
|
)
|
|
@@ -215,10 +184,9 @@ class ServiceContainer:
|
|
|
215
184
|
self.container.register(
|
|
216
185
|
ConbusOutputService,
|
|
217
186
|
factory=lambda: ConbusOutputService(
|
|
218
|
-
telegram_service=self.container.resolve(TelegramService),
|
|
219
187
|
telegram_output_service=self.container.resolve(TelegramOutputService),
|
|
220
|
-
|
|
221
|
-
|
|
188
|
+
cli_config=self.container.resolve(ConbusClientConfig),
|
|
189
|
+
reactor=self.container.resolve(PosixReactorBase),
|
|
222
190
|
),
|
|
223
191
|
scope=punq.Scope.singleton,
|
|
224
192
|
)
|
|
@@ -300,12 +268,21 @@ class ServiceContainer:
|
|
|
300
268
|
)
|
|
301
269
|
|
|
302
270
|
self.container.register(
|
|
303
|
-
|
|
304
|
-
factory=lambda:
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
271
|
+
ConbusLinknumberGetService,
|
|
272
|
+
factory=lambda: ConbusLinknumberGetService(
|
|
273
|
+
telegram_service=self.container.resolve(TelegramService),
|
|
274
|
+
cli_config=self.container.resolve(ConbusClientConfig),
|
|
275
|
+
reactor=self.container.resolve(PosixReactorBase),
|
|
276
|
+
),
|
|
277
|
+
scope=punq.Scope.singleton,
|
|
278
|
+
)
|
|
279
|
+
|
|
280
|
+
self.container.register(
|
|
281
|
+
ConbusLinknumberSetService,
|
|
282
|
+
factory=lambda: ConbusLinknumberSetService(
|
|
308
283
|
telegram_service=self.container.resolve(TelegramService),
|
|
284
|
+
cli_config=self.container.resolve(ConbusClientConfig),
|
|
285
|
+
reactor=self.container.resolve(PosixReactorBase),
|
|
309
286
|
),
|
|
310
287
|
scope=punq.Scope.singleton,
|
|
311
288
|
)
|
|
@@ -1,148 +0,0 @@
|
|
|
1
|
-
"""Connection pooling implementation for Conbus TCP connections.
|
|
2
|
-
|
|
3
|
-
This module provides a singleton connection pool for managing TCP socket connections
|
|
4
|
-
to Conbus servers with automatic lifecycle management, health checking, and reconnection.
|
|
5
|
-
"""
|
|
6
|
-
|
|
7
|
-
import logging
|
|
8
|
-
import socket
|
|
9
|
-
import threading
|
|
10
|
-
import time
|
|
11
|
-
from typing import Any, Optional
|
|
12
|
-
|
|
13
|
-
from xp.models import ConbusClientConfig
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
class ConbusSocketConnectionManager:
|
|
17
|
-
"""Connection manager for TCP socket connections to Conbus servers"""
|
|
18
|
-
|
|
19
|
-
def __init__(self, cli_config: ConbusClientConfig):
|
|
20
|
-
self.config = cli_config.conbus
|
|
21
|
-
self.logger = logging.getLogger(__name__)
|
|
22
|
-
|
|
23
|
-
def create(self) -> socket.socket:
|
|
24
|
-
"""Create and configure a new TCP socket connection"""
|
|
25
|
-
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
26
|
-
sock.settimeout(self.config.timeout)
|
|
27
|
-
sock.connect((self.config.ip, self.config.port))
|
|
28
|
-
self.logger.info(
|
|
29
|
-
f"Created new connection to {self.config.ip}:{self.config.port} (timeout: {self.config.timeout})"
|
|
30
|
-
)
|
|
31
|
-
return sock
|
|
32
|
-
|
|
33
|
-
def dispose(self, connection: socket.socket) -> None:
|
|
34
|
-
"""Close and cleanup socket connection"""
|
|
35
|
-
try:
|
|
36
|
-
connection.close()
|
|
37
|
-
self.logger.info("Disposed socket connection")
|
|
38
|
-
except Exception as e:
|
|
39
|
-
self.logger.warning(f"Error disposing connection: {e}")
|
|
40
|
-
|
|
41
|
-
@staticmethod
|
|
42
|
-
def check_aliveness(connection: socket.socket) -> bool:
|
|
43
|
-
"""Verify if connection is still alive"""
|
|
44
|
-
try:
|
|
45
|
-
# Use socket error checking rather than sending empty data
|
|
46
|
-
# to avoid potential protocol issues
|
|
47
|
-
error = connection.getsockopt(socket.SOL_SOCKET, socket.SO_ERROR)
|
|
48
|
-
return error == 0
|
|
49
|
-
except (socket.error, OSError):
|
|
50
|
-
return False
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
class ConbusConnectionPool:
|
|
54
|
-
"""Singleton connection pool for Conbus TCP connections"""
|
|
55
|
-
|
|
56
|
-
_lock = threading.Lock()
|
|
57
|
-
|
|
58
|
-
def __init__(self, connection_manager: ConbusSocketConnectionManager) -> None:
|
|
59
|
-
if hasattr(self, "_initialized"):
|
|
60
|
-
return
|
|
61
|
-
|
|
62
|
-
self._connection_manager = connection_manager
|
|
63
|
-
self._connection: Optional[socket.socket] = None
|
|
64
|
-
self._current_connection: Optional[socket.socket] = None
|
|
65
|
-
self._connection_created_at: Optional[float] = None
|
|
66
|
-
self._lock = threading.Lock()
|
|
67
|
-
self.logger = logging.getLogger(__name__)
|
|
68
|
-
|
|
69
|
-
# Configuration
|
|
70
|
-
self.idle_timeout = 21600 # 6 hours
|
|
71
|
-
self.max_lifetime = 21600 # 6 hours
|
|
72
|
-
self._initialized = True
|
|
73
|
-
|
|
74
|
-
def _is_connection_expired(self) -> bool:
|
|
75
|
-
"""Check if the current connection has expired"""
|
|
76
|
-
if self._connection_created_at is None:
|
|
77
|
-
return True
|
|
78
|
-
|
|
79
|
-
age = time.time() - self._connection_created_at
|
|
80
|
-
return age > self.max_lifetime
|
|
81
|
-
|
|
82
|
-
def _is_connection_alive(self) -> bool:
|
|
83
|
-
"""Check if connection is still alive"""
|
|
84
|
-
if self._connection is None or self._connection_manager is None:
|
|
85
|
-
return False
|
|
86
|
-
|
|
87
|
-
return self._connection_manager.check_aliveness(self._connection)
|
|
88
|
-
|
|
89
|
-
def acquire_connection(self) -> socket.socket:
|
|
90
|
-
"""Acquire a connection from the pool"""
|
|
91
|
-
if self._connection_manager is None:
|
|
92
|
-
raise RuntimeError("Connection pool not initialized")
|
|
93
|
-
|
|
94
|
-
with self._lock:
|
|
95
|
-
# Check if we need a new connection
|
|
96
|
-
if (
|
|
97
|
-
self._connection is None
|
|
98
|
-
or self._is_connection_expired()
|
|
99
|
-
or not self._is_connection_alive()
|
|
100
|
-
):
|
|
101
|
-
|
|
102
|
-
# Close existing connection if any
|
|
103
|
-
if self._connection:
|
|
104
|
-
self._connection_manager.dispose(self._connection)
|
|
105
|
-
self._connection = None
|
|
106
|
-
|
|
107
|
-
# Create new connection
|
|
108
|
-
self._connection = self._connection_manager.create()
|
|
109
|
-
self._connection_created_at = time.time()
|
|
110
|
-
self.logger.debug("Created new connection")
|
|
111
|
-
|
|
112
|
-
self.logger.debug("Acquired connection from pool")
|
|
113
|
-
return self._connection
|
|
114
|
-
|
|
115
|
-
# noinspection PyUnusedLocal
|
|
116
|
-
def release_connection(self, connection: socket.socket) -> None:
|
|
117
|
-
"""Release a connection back to the pool (no-op for single connection pool)"""
|
|
118
|
-
self.logger.debug("Released connection back to pool")
|
|
119
|
-
# For single connection pool, we just log but don't actually close the connection
|
|
120
|
-
|
|
121
|
-
def __enter__(self) -> socket.socket:
|
|
122
|
-
"""Context manager entry - acquire connection"""
|
|
123
|
-
self._current_connection = self.acquire_connection()
|
|
124
|
-
return self._current_connection
|
|
125
|
-
|
|
126
|
-
def __exit__(
|
|
127
|
-
self,
|
|
128
|
-
_exc_type: Optional[type],
|
|
129
|
-
_exc_val: Optional[Exception],
|
|
130
|
-
_exc_tb: Optional[Any],
|
|
131
|
-
) -> None:
|
|
132
|
-
"""Context manager exit - release connection"""
|
|
133
|
-
if hasattr(self, "_current_connection") and self._current_connection:
|
|
134
|
-
self.release_connection(self._current_connection)
|
|
135
|
-
self._current_connection = None
|
|
136
|
-
|
|
137
|
-
def close(self) -> None:
|
|
138
|
-
"""Close the connection pool and cleanup resources"""
|
|
139
|
-
with self._lock:
|
|
140
|
-
if self._connection and self._connection_manager is not None:
|
|
141
|
-
try:
|
|
142
|
-
self._connection_manager.dispose(self._connection)
|
|
143
|
-
self.logger.info("Connection pool closed")
|
|
144
|
-
except Exception as e:
|
|
145
|
-
self.logger.error(f"Error closing connection pool: {e}")
|
|
146
|
-
finally:
|
|
147
|
-
self._connection = None
|
|
148
|
-
self._connection_created_at = None
|