rocket-welder-sdk 1.1.43__py3-none-any.whl → 1.1.45__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 (31) hide show
  1. rocket_welder_sdk/__init__.py +44 -22
  2. rocket_welder_sdk/binary_frame_reader.py +222 -0
  3. rocket_welder_sdk/binary_frame_writer.py +213 -0
  4. rocket_welder_sdk/confidence.py +206 -0
  5. rocket_welder_sdk/delta_frame.py +150 -0
  6. rocket_welder_sdk/graphics/__init__.py +42 -0
  7. rocket_welder_sdk/graphics/layer_canvas.py +157 -0
  8. rocket_welder_sdk/graphics/protocol.py +72 -0
  9. rocket_welder_sdk/graphics/rgb_color.py +109 -0
  10. rocket_welder_sdk/graphics/stage.py +494 -0
  11. rocket_welder_sdk/graphics/vector_graphics_encoder.py +575 -0
  12. rocket_welder_sdk/high_level/__init__.py +8 -1
  13. rocket_welder_sdk/high_level/client.py +114 -3
  14. rocket_welder_sdk/high_level/connection_strings.py +88 -15
  15. rocket_welder_sdk/high_level/frame_sink_factory.py +2 -15
  16. rocket_welder_sdk/high_level/transport_protocol.py +4 -130
  17. rocket_welder_sdk/keypoints_protocol.py +520 -55
  18. rocket_welder_sdk/rocket_welder_client.py +210 -89
  19. rocket_welder_sdk/segmentation_result.py +387 -2
  20. rocket_welder_sdk/session_id.py +7 -182
  21. rocket_welder_sdk/transport/__init__.py +10 -3
  22. rocket_welder_sdk/transport/frame_sink.py +3 -3
  23. rocket_welder_sdk/transport/frame_source.py +2 -2
  24. rocket_welder_sdk/transport/websocket_transport.py +316 -0
  25. rocket_welder_sdk/varint.py +213 -0
  26. {rocket_welder_sdk-1.1.43.dist-info → rocket_welder_sdk-1.1.45.dist-info}/METADATA +1 -4
  27. rocket_welder_sdk-1.1.45.dist-info/RECORD +51 -0
  28. {rocket_welder_sdk-1.1.43.dist-info → rocket_welder_sdk-1.1.45.dist-info}/WHEEL +1 -1
  29. rocket_welder_sdk/transport/nng_transport.py +0 -197
  30. rocket_welder_sdk-1.1.43.dist-info/RECORD +0 -40
  31. {rocket_welder_sdk-1.1.43.dist-info → rocket_welder_sdk-1.1.45.dist-info}/top_level.txt +0 -0
@@ -2,18 +2,24 @@
2
2
  RocketWelderClient - High-level API matching C# RocketWelder.SDK.
3
3
 
4
4
  Usage:
5
- with RocketWelderClient.from_environment() as client:
5
+ with RocketWelderClientFactory.from_environment() as client:
6
6
  # Define schema
7
7
  nose = client.keypoints.define_point("nose")
8
8
  person = client.segmentation.define_class(1, "person")
9
9
 
10
10
  # Start processing
11
11
  client.start(process_frame)
12
+
13
+ Alternatively:
14
+ # Using class methods directly
15
+ with RocketWelderClient.from_environment() as client:
16
+ ...
12
17
  """
13
18
 
14
19
  from __future__ import annotations
15
20
 
16
21
  import logging
22
+ from abc import ABC, abstractmethod
17
23
  from dataclasses import dataclass, field
18
24
  from typing import TYPE_CHECKING, Any, Callable, Optional
19
25
 
@@ -50,6 +56,73 @@ Mat: TypeAlias = npt.NDArray[np.uint8]
50
56
  logger = logging.getLogger(__name__)
51
57
 
52
58
 
59
+ class IRocketWelderClient(ABC):
60
+ """
61
+ Main entry point for RocketWelder SDK high-level API.
62
+
63
+ Provides schema definitions and frame processing loop.
64
+ Matches C# IRocketWelderClient interface.
65
+ """
66
+
67
+ @property
68
+ @abstractmethod
69
+ def keypoints(self) -> IKeyPointsSchema:
70
+ """Schema for defining keypoints."""
71
+ pass
72
+
73
+ @property
74
+ @abstractmethod
75
+ def segmentation(self) -> ISegmentationSchema:
76
+ """Schema for defining segmentation classes."""
77
+ pass
78
+
79
+ @abstractmethod
80
+ def start(
81
+ self,
82
+ process_frame: Callable[[Mat, ISegmentationDataContext, IKeyPointsDataContext, Mat], None],
83
+ ) -> None:
84
+ """
85
+ Start the processing loop with full context (keypoints + segmentation).
86
+
87
+ Args:
88
+ process_frame: Callback for each frame with:
89
+ - input_frame: Source video frame (Mat)
90
+ - segmentation: Segmentation data context
91
+ - keypoints: KeyPoints data context
92
+ - output_frame: Output frame for visualization (Mat)
93
+ """
94
+ pass
95
+
96
+ @abstractmethod
97
+ def start_keypoints(
98
+ self,
99
+ process_frame: Callable[[Mat, IKeyPointsDataContext, Mat], None],
100
+ ) -> None:
101
+ """Start the processing loop (keypoints only)."""
102
+ pass
103
+
104
+ @abstractmethod
105
+ def start_segmentation(
106
+ self,
107
+ process_frame: Callable[[Mat, ISegmentationDataContext, Mat], None],
108
+ ) -> None:
109
+ """Start the processing loop (segmentation only)."""
110
+ pass
111
+
112
+ @abstractmethod
113
+ def close(self) -> None:
114
+ """Release resources."""
115
+ pass
116
+
117
+ def __enter__(self) -> IRocketWelderClient:
118
+ """Context manager entry."""
119
+ return self
120
+
121
+ def __exit__(self, *args: object) -> None:
122
+ """Context manager exit."""
123
+ self.close()
124
+
125
+
53
126
  @dataclass
54
127
  class RocketWelderClientOptions:
55
128
  """Configuration options for RocketWelderClient."""
@@ -72,11 +145,12 @@ class RocketWelderClientOptions:
72
145
  )
73
146
 
74
147
 
75
- class RocketWelderClient:
148
+ class RocketWelderClient(IRocketWelderClient):
76
149
  """
77
150
  High-level client for RocketWelder SDK.
78
151
 
79
- Mirrors C# RocketWelder.SDK.IRocketWelderClient interface.
152
+ Implements IRocketWelderClient interface.
153
+ Mirrors C# RocketWelder.SDK.RocketWelderClientImpl.
80
154
  """
81
155
 
82
156
  def __init__(self, options: RocketWelderClientOptions) -> None:
@@ -233,3 +307,40 @@ class RocketWelderClient:
233
307
 
234
308
  def __exit__(self, *args: object) -> None:
235
309
  self.close()
310
+
311
+
312
+ class RocketWelderClientFactory:
313
+ """
314
+ Factory for creating RocketWelderClient instances.
315
+
316
+ Matches C# RocketWelderClientFactory static class.
317
+ """
318
+
319
+ @staticmethod
320
+ def from_environment() -> IRocketWelderClient:
321
+ """
322
+ Creates a client configured from environment variables.
323
+
324
+ Environment variables:
325
+ - VIDEO_SOURCE or CONNECTION_STRING: Video input
326
+ - KEYPOINTS_CONNECTION_STRING: KeyPoints output
327
+ - SEGMENTATION_CONNECTION_STRING: Segmentation output
328
+
329
+ Returns:
330
+ IRocketWelderClient configured from environment.
331
+ """
332
+ options = RocketWelderClientOptions.from_environment()
333
+ return RocketWelderClient(options)
334
+
335
+ @staticmethod
336
+ def create(options: Optional[RocketWelderClientOptions] = None) -> IRocketWelderClient:
337
+ """
338
+ Creates a client with explicit configuration.
339
+
340
+ Args:
341
+ options: Configuration options. If None, uses defaults.
342
+
343
+ Returns:
344
+ IRocketWelderClient with the specified configuration.
345
+ """
346
+ return RocketWelderClient(options or RocketWelderClientOptions())
@@ -4,10 +4,8 @@ Strongly-typed connection strings with parsing support.
4
4
  Connection string format: protocol://path?param1=value1&param2=value2
5
5
 
6
6
  Examples:
7
- nng+push+ipc://tmp/keypoints?masterFrameInterval=300
8
- nng+pub+tcp://localhost:5555
9
7
  file:///path/to/output.bin
10
- socket:///tmp/my.sock
8
+ socket:///tmp/my.sock?masterFrameInterval=300
11
9
  """
12
10
 
13
11
  from __future__ import annotations
@@ -148,8 +146,6 @@ class KeyPointsConnectionString:
148
146
  Supported protocols:
149
147
  - file:///path/to/file.bin - File output (absolute path)
150
148
  - socket:///tmp/socket.sock - Unix domain socket
151
- - nng+push+ipc://tmp/keypoints - NNG Push over IPC
152
- - nng+push+tcp://host:port - NNG Push over TCP
153
149
 
154
150
  Supported parameters:
155
151
  - masterFrameInterval: Interval between master frames (default: 300)
@@ -164,7 +160,7 @@ class KeyPointsConnectionString:
164
160
  @classmethod
165
161
  def default(cls) -> KeyPointsConnectionString:
166
162
  """Default connection string for KeyPoints."""
167
- return cls.parse("nng+push+ipc://tmp/rocket-welder-keypoints?masterFrameInterval=300")
163
+ return cls.parse("socket:///tmp/rocket-welder-keypoints.sock?masterFrameInterval=300")
168
164
 
169
165
  @classmethod
170
166
  def from_environment(
@@ -217,9 +213,6 @@ class KeyPointsConnectionString:
217
213
  elif protocol.is_socket:
218
214
  # socket:///tmp/sock -> /tmp/sock
219
215
  address = path_part if path_part.startswith("/") else "/" + path_part
220
- elif protocol.is_nng:
221
- # NNG protocols need proper address format
222
- address = protocol.create_nng_address(path_part)
223
216
  else:
224
217
  return None
225
218
 
@@ -249,8 +242,6 @@ class SegmentationConnectionString:
249
242
  Supported protocols:
250
243
  - file:///path/to/file.bin - File output (absolute path)
251
244
  - socket:///tmp/socket.sock - Unix domain socket
252
- - nng+push+ipc://tmp/segmentation - NNG Push over IPC
253
- - nng+push+tcp://host:port - NNG Push over TCP
254
245
  """
255
246
 
256
247
  value: str
@@ -261,7 +252,7 @@ class SegmentationConnectionString:
261
252
  @classmethod
262
253
  def default(cls) -> SegmentationConnectionString:
263
254
  """Default connection string for Segmentation."""
264
- return cls.parse("nng+push+ipc://tmp/rocket-welder-segmentation")
255
+ return cls.parse("socket:///tmp/rocket-welder-segmentation.sock")
265
256
 
266
257
  @classmethod
267
258
  def from_environment(
@@ -314,9 +305,91 @@ class SegmentationConnectionString:
314
305
  elif protocol.is_socket:
315
306
  # socket:///tmp/sock -> /tmp/sock
316
307
  address = path_part if path_part.startswith("/") else "/" + path_part
317
- elif protocol.is_nng:
318
- # NNG protocols need proper address format
319
- address = protocol.create_nng_address(path_part)
308
+ else:
309
+ return None
310
+
311
+ return cls(
312
+ value=s,
313
+ protocol=protocol,
314
+ address=address,
315
+ parameters=parameters,
316
+ )
317
+
318
+ def __str__(self) -> str:
319
+ return self.value
320
+
321
+
322
+ @dataclass(frozen=True)
323
+ class GraphicsConnectionString:
324
+ """
325
+ Strongly-typed connection string for Stage (graphics) output.
326
+
327
+ Supported protocols:
328
+ - file:///path/to/file.bin - File output (absolute path)
329
+ - socket:///tmp/socket.sock - Unix domain socket
330
+ """
331
+
332
+ value: str
333
+ protocol: TransportProtocol
334
+ address: str
335
+ parameters: Dict[str, str] = field(default_factory=dict)
336
+
337
+ @classmethod
338
+ def default(cls) -> GraphicsConnectionString:
339
+ """Default connection string for Stage."""
340
+ return cls.parse("socket:///tmp/rocket-welder-stage.sock")
341
+
342
+ @classmethod
343
+ def from_environment(
344
+ cls, variable_name: str = "STAGE_CONNECTION_STRING"
345
+ ) -> GraphicsConnectionString:
346
+ """Create from environment variable or use default."""
347
+ value = os.environ.get(variable_name)
348
+ return cls.parse(value) if value else cls.default()
349
+
350
+ @classmethod
351
+ def parse(cls, s: str) -> GraphicsConnectionString:
352
+ """Parse a connection string."""
353
+ result = cls.try_parse(s)
354
+ if result is None:
355
+ raise ValueError(f"Invalid Graphics connection string: {s}")
356
+ return result
357
+
358
+ @classmethod
359
+ def try_parse(cls, s: str) -> Optional[GraphicsConnectionString]:
360
+ """Try to parse a connection string."""
361
+ if not s or not s.strip():
362
+ return None
363
+
364
+ s = s.strip()
365
+ parameters: Dict[str, str] = {}
366
+
367
+ # Extract query parameters
368
+ endpoint_part = s
369
+ if "?" in s:
370
+ endpoint_part, query = s.split("?", 1)
371
+ for key, values in parse_qs(query).items():
372
+ parameters[key.lower()] = values[0] if values else ""
373
+
374
+ # Parse protocol and address
375
+ scheme_end = endpoint_part.find("://")
376
+ if scheme_end <= 0:
377
+ return None
378
+
379
+ schema_str = endpoint_part[:scheme_end]
380
+ path_part = endpoint_part[scheme_end + 3 :] # skip "://"
381
+
382
+ protocol = TransportProtocol.try_parse(schema_str)
383
+ if protocol is None:
384
+ return None
385
+
386
+ # Build address based on protocol type
387
+ if protocol.is_file:
388
+ # file:///absolute/path -> /absolute/path
389
+ address = path_part if path_part.startswith("/") else "/" + path_part
390
+ elif protocol.is_socket:
391
+ # socket:///tmp/sock -> /tmp/sock
392
+ address = path_part if path_part.startswith("/") else "/" + path_part
320
393
  else:
321
394
  return None
322
395
 
@@ -51,7 +51,7 @@ class FrameSinkFactory:
51
51
 
52
52
  Args:
53
53
  protocol: The transport protocol (from ConnectionString.protocol), or None
54
- address: The address (file path, socket path, or NNG address)
54
+ address: The address (file path or socket path)
55
55
  logger_instance: Optional logger for diagnostics
56
56
 
57
57
  Returns:
@@ -64,7 +64,7 @@ class FrameSinkFactory:
64
64
  cs = SegmentationConnectionString.parse("socket:///tmp/seg.sock")
65
65
  sink = FrameSinkFactory.create(cs.protocol, cs.address)
66
66
  """
67
- from rocket_welder_sdk.transport import NngFrameSink, NullFrameSink
67
+ from rocket_welder_sdk.transport import NullFrameSink
68
68
  from rocket_welder_sdk.transport.stream_transport import StreamFrameSink
69
69
  from rocket_welder_sdk.transport.unix_socket_transport import UnixSocketFrameSink
70
70
 
@@ -87,19 +87,6 @@ class FrameSinkFactory:
87
87
  log.info("Creating Unix socket frame sink (server/bind) at: %s", address)
88
88
  return UnixSocketFrameSink.bind(address)
89
89
 
90
- if protocol.is_nng:
91
- log.info("Creating NNG frame sink (%s) at: %s", protocol.schema, address)
92
-
93
- if protocol.is_pub:
94
- return NngFrameSink.create_publisher(address)
95
- if protocol.is_push:
96
- return NngFrameSink.create_pusher(address)
97
-
98
- raise ValueError(
99
- f"NNG protocol '{protocol.schema}' is not supported for sinks "
100
- "(only pub and push are supported)"
101
- )
102
-
103
90
  raise ValueError(f"Transport protocol '{protocol.schema}' is not supported for frame sinks")
104
91
 
105
92
  @staticmethod
@@ -1,13 +1,11 @@
1
1
  """
2
2
  Unified transport protocol as a value type.
3
3
 
4
- Supports: file://, socket://, nng+push+ipc://, nng+push+tcp://, etc.
4
+ Supports: file://, socket://
5
5
 
6
6
  Examples:
7
7
  file:///home/user/output.bin - absolute file path
8
8
  socket:///tmp/my.sock - Unix domain socket
9
- nng+push+ipc://tmp/keypoints - NNG Push over IPC
10
- nng+push+tcp://host:5555 - NNG Push over TCP
11
9
  """
12
10
 
13
11
  from __future__ import annotations
@@ -25,61 +23,21 @@ class TransportKind(Enum):
25
23
  SOCKET = auto()
26
24
  """Unix domain socket (direct, no messaging library)."""
27
25
 
28
- NNG_PUSH_IPC = auto()
29
- """NNG Push over IPC."""
30
-
31
- NNG_PUSH_TCP = auto()
32
- """NNG Push over TCP."""
33
-
34
- NNG_PULL_IPC = auto()
35
- """NNG Pull over IPC."""
36
-
37
- NNG_PULL_TCP = auto()
38
- """NNG Pull over TCP."""
39
-
40
- NNG_PUB_IPC = auto()
41
- """NNG Pub over IPC."""
42
-
43
- NNG_PUB_TCP = auto()
44
- """NNG Pub over TCP."""
45
-
46
- NNG_SUB_IPC = auto()
47
- """NNG Sub over IPC."""
48
-
49
- NNG_SUB_TCP = auto()
50
- """NNG Sub over TCP."""
51
-
52
26
 
53
27
  class TransportProtocol:
54
28
  """
55
29
  Unified transport protocol specification as a value type.
56
30
 
57
- Supports: file://, socket://, nng+push+ipc://, nng+push+tcp://, etc.
31
+ Supports: file://, socket://
58
32
  """
59
33
 
60
34
  # Predefined protocols
61
35
  File: TransportProtocol
62
36
  Socket: TransportProtocol
63
- NngPushIpc: TransportProtocol
64
- NngPushTcp: TransportProtocol
65
- NngPullIpc: TransportProtocol
66
- NngPullTcp: TransportProtocol
67
- NngPubIpc: TransportProtocol
68
- NngPubTcp: TransportProtocol
69
- NngSubIpc: TransportProtocol
70
- NngSubTcp: TransportProtocol
71
37
 
72
38
  _SCHEMA_MAP: ClassVar[Dict[str, TransportKind]] = {
73
39
  "file": TransportKind.FILE,
74
40
  "socket": TransportKind.SOCKET,
75
- "nng+push+ipc": TransportKind.NNG_PUSH_IPC,
76
- "nng+push+tcp": TransportKind.NNG_PUSH_TCP,
77
- "nng+pull+ipc": TransportKind.NNG_PULL_IPC,
78
- "nng+pull+tcp": TransportKind.NNG_PULL_TCP,
79
- "nng+pub+ipc": TransportKind.NNG_PUB_IPC,
80
- "nng+pub+tcp": TransportKind.NNG_PUB_TCP,
81
- "nng+sub+ipc": TransportKind.NNG_SUB_IPC,
82
- "nng+sub+tcp": TransportKind.NNG_SUB_TCP,
83
41
  }
84
42
 
85
43
  _KIND_TO_SCHEMA: ClassVar[Dict[TransportKind, str]] = {}
@@ -95,7 +53,7 @@ class TransportProtocol:
95
53
 
96
54
  @property
97
55
  def schema(self) -> str:
98
- """The schema string (e.g., 'file', 'socket', 'nng+push+ipc')."""
56
+ """The schema string (e.g., 'file', 'socket')."""
99
57
  return self._schema
100
58
 
101
59
  # Classification properties
@@ -110,82 +68,6 @@ class TransportProtocol:
110
68
  """True if this is a Unix socket transport."""
111
69
  return self._kind == TransportKind.SOCKET
112
70
 
113
- @property
114
- def is_nng(self) -> bool:
115
- """True if this is any NNG-based transport."""
116
- return self._kind in {
117
- TransportKind.NNG_PUSH_IPC,
118
- TransportKind.NNG_PUSH_TCP,
119
- TransportKind.NNG_PULL_IPC,
120
- TransportKind.NNG_PULL_TCP,
121
- TransportKind.NNG_PUB_IPC,
122
- TransportKind.NNG_PUB_TCP,
123
- TransportKind.NNG_SUB_IPC,
124
- TransportKind.NNG_SUB_TCP,
125
- }
126
-
127
- @property
128
- def is_push(self) -> bool:
129
- """True if this is a Push pattern."""
130
- return self._kind in {TransportKind.NNG_PUSH_IPC, TransportKind.NNG_PUSH_TCP}
131
-
132
- @property
133
- def is_pull(self) -> bool:
134
- """True if this is a Pull pattern."""
135
- return self._kind in {TransportKind.NNG_PULL_IPC, TransportKind.NNG_PULL_TCP}
136
-
137
- @property
138
- def is_pub(self) -> bool:
139
- """True if this is a Pub pattern."""
140
- return self._kind in {TransportKind.NNG_PUB_IPC, TransportKind.NNG_PUB_TCP}
141
-
142
- @property
143
- def is_sub(self) -> bool:
144
- """True if this is a Sub pattern."""
145
- return self._kind in {TransportKind.NNG_SUB_IPC, TransportKind.NNG_SUB_TCP}
146
-
147
- @property
148
- def is_ipc(self) -> bool:
149
- """True if this uses IPC layer."""
150
- return self._kind in {
151
- TransportKind.NNG_PUSH_IPC,
152
- TransportKind.NNG_PULL_IPC,
153
- TransportKind.NNG_PUB_IPC,
154
- TransportKind.NNG_SUB_IPC,
155
- }
156
-
157
- @property
158
- def is_tcp(self) -> bool:
159
- """True if this uses TCP layer."""
160
- return self._kind in {
161
- TransportKind.NNG_PUSH_TCP,
162
- TransportKind.NNG_PULL_TCP,
163
- TransportKind.NNG_PUB_TCP,
164
- TransportKind.NNG_SUB_TCP,
165
- }
166
-
167
- def create_nng_address(self, path_or_host: str) -> str:
168
- """
169
- Create the NNG address from a path/host.
170
-
171
- For IPC: ipc:///path
172
- For TCP: tcp://host:port
173
-
174
- Raises:
175
- ValueError: If this is not an NNG protocol.
176
- """
177
- if not self.is_nng:
178
- raise ValueError(f"Cannot create NNG address for {self._kind} transport")
179
-
180
- if self.is_ipc:
181
- # IPC paths need leading "/" for absolute paths
182
- if not path_or_host.startswith("/"):
183
- return f"ipc:///{path_or_host}"
184
- return f"ipc://{path_or_host}"
185
-
186
- # TCP
187
- return f"tcp://{path_or_host}"
188
-
189
71
  def __str__(self) -> str:
190
72
  return self._schema
191
73
 
@@ -202,7 +84,7 @@ class TransportProtocol:
202
84
 
203
85
  @classmethod
204
86
  def parse(cls, s: str) -> TransportProtocol:
205
- """Parse a protocol string (e.g., 'nng+push+ipc')."""
87
+ """Parse a protocol string (e.g., 'file', 'socket')."""
206
88
  result = cls.try_parse(s)
207
89
  if result is None:
208
90
  raise ValueError(f"Invalid transport protocol: {s}")
@@ -225,14 +107,6 @@ class TransportProtocol:
225
107
  # Initialize predefined protocols
226
108
  TransportProtocol.File = TransportProtocol(TransportKind.FILE, "file")
227
109
  TransportProtocol.Socket = TransportProtocol(TransportKind.SOCKET, "socket")
228
- TransportProtocol.NngPushIpc = TransportProtocol(TransportKind.NNG_PUSH_IPC, "nng+push+ipc")
229
- TransportProtocol.NngPushTcp = TransportProtocol(TransportKind.NNG_PUSH_TCP, "nng+push+tcp")
230
- TransportProtocol.NngPullIpc = TransportProtocol(TransportKind.NNG_PULL_IPC, "nng+pull+ipc")
231
- TransportProtocol.NngPullTcp = TransportProtocol(TransportKind.NNG_PULL_TCP, "nng+pull+tcp")
232
- TransportProtocol.NngPubIpc = TransportProtocol(TransportKind.NNG_PUB_IPC, "nng+pub+ipc")
233
- TransportProtocol.NngPubTcp = TransportProtocol(TransportKind.NNG_PUB_TCP, "nng+pub+tcp")
234
- TransportProtocol.NngSubIpc = TransportProtocol(TransportKind.NNG_SUB_IPC, "nng+sub+ipc")
235
- TransportProtocol.NngSubTcp = TransportProtocol(TransportKind.NNG_SUB_TCP, "nng+sub+tcp")
236
110
 
237
111
  # Initialize reverse lookup map
238
112
  TransportProtocol._KIND_TO_SCHEMA = {v: k for k, v in TransportProtocol._SCHEMA_MAP.items()}