Skip to content

Commit 7cea5e0

Browse files
committed
move around code, fix some circular imports, change some base agents to extend off of AgentBase instead of Agent and some other small fixes
1 parent 09b1b0c commit 7cea5e0

8 files changed

Lines changed: 130 additions & 118 deletions

File tree

Alt-Cameras/src/Alt/Cameras/Agents/CameraUsingAgentBase.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@
33
import cv2
44
import numpy as np
55

6-
from Alt.Core.Agents import Agent, BindableAgent
6+
from Alt.Core.Agents import AgentBase, BindableAgent
77
from Alt.Core.Constants.AgentConstants import ProxyType
8-
from Alt.Core.Operators.StreamOperator import StreamProxy
8+
from Alt.Core.Operators.StreamProxy import StreamProxy
99

1010
from ..Captures.OpenCVCapture import OpenCVCapture
1111
from ..Captures.Capture import Capture, CaptureWIntrinsics, ConfigurableCapture
@@ -15,7 +15,7 @@
1515
from ..Calibration.CalibrationUtil import CalibrationUtil
1616

1717

18-
class CameraUsingAgentBase(Agent, BindableAgent):
18+
class CameraUsingAgentBase(AgentBase, BindableAgent):
1919
"""
2020
Main superclass for any agent that needs to use a camera. It is reccomended to extend this rather than create a separate agent class.
2121
Adds calibration support, automatic mjpeg streaming, camera status checking, and depth camera support

Alt-Core/src/Alt/Core/Agents/Agent.py

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,16 @@
77
from JXTABLES.XTablesClient import XTablesClient
88

99
from ..Utils.network import DEVICEIP
10+
from ..Operators.LogStreamOperator import LOGPATH
11+
from ..Operators.StreamProxy import StreamProxy
12+
1013

1114
if TYPE_CHECKING:
1215
from ..Constants.AgentConstants import Proxy, ProxyType
1316
from ..Constants.Teams import TEAM
1417
from ..Operators.ConfigOperator import ConfigOperator
15-
from ..Operators.LogStreamOperator import LogStreamOperator
1618
from ..Operators.PropertyOperator import PropertyOperator
1719
from ..Operators.ShareOperator import ShareOperator
18-
from ..Operators.StreamOperator import StreamProxy
1920
from ..Operators.TimeOperator import TimeOperator, Timer
2021
from ..Operators.UpdateOperator import UpdateOperator
2122

@@ -173,6 +174,9 @@ def _updateNetworkProxyInfo(self):
173174
if isinstance(proxy, StreamProxy):
174175
streamPaths.append(f"{proxyName}|{proxy.getStreamPath()}")
175176

177+
print(f"STREAM PROXY PATH: {proxy.getStreamPath()}")
178+
179+
176180
self.propertyOperator.createCustomReadOnlyProperty(
177181
"streamPaths", streamPaths, addBasePrefix=True, addOperatorPrefix=True
178182
)
@@ -210,12 +214,14 @@ def getTeam(self) -> Optional[TEAM]:
210214
def _runOwnCreate(self):
211215
"""The agent wants to do its own stuff too... okay."""
212216

213-
logIp = f"http://{DEVICEIP}:5000/{self.agentName}/{LogStreamOperator.LOGPATH}"
217+
logIp = f"http://{DEVICEIP}:5000/{self.agentName}/{LOGPATH}"
214218

215219
self.propertyOperator.createCustomReadOnlyProperty(
216220
"logIP", logIp, addBasePrefix=True, addOperatorPrefix=True
217221
)
218222

223+
print(f"LOGIP: {logIp}")
224+
219225
self.__ensureProxies()
220226
self._updateNetworkProxyInfo()
221227

@@ -272,12 +278,10 @@ def onClose(self) -> None:
272278

273279
# ----- proxy methods -----
274280
def __ensureProxies(self) -> None:
275-
for proxyName, proxyType in self._getProxyRequests().items():
281+
for proxyName, _ in self._getProxyRequests().items():
276282
if (
277283
proxyName not in self.__proxies
278-
or type(self.__proxies[proxyName]) is not proxyType
279284
):
280-
print(type(self.__proxies[proxyName]) is proxyType)
281285
raise RuntimeError(
282286
f"Agent proxies are not correcty initialized!\n{self._getProxyRequests()=}\n{self.__proxies.items()=}"
283287
)

Alt-Core/src/Alt/Core/Constants/AgentConstants.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,11 @@
33
from abc import abstractmethod
44
from enum import Enum
55
from functools import partial
6-
from typing import Any, Dict, cast
6+
from typing import Any, Dict, cast, TYPE_CHECKING
77

8-
from Alt.Core.Agents import Agent
8+
9+
if TYPE_CHECKING:
10+
from Alt.Core.Agents import Agent
911

1012

1113
class ProxyType(Enum):
@@ -28,6 +30,7 @@ def getProxyRequests(agentClass) -> Dict[str, "ProxyType"]:
2830
def __getPartialProxyRequests(
2931
agentClass: partial[type[Agent]],
3032
) -> Dict[str, "ProxyType"]:
33+
from Alt.Core.Agents import Agent
3134
agentClassType = cast(Agent, agentClass.func)
3235
agentClassType.requestProxies()
3336
return agentClassType._getProxyRequests()
@@ -46,3 +49,4 @@ def put(self, value: Any):
4649
@abstractmethod
4750
def get(self) -> Any:
4851
pass
52+

Alt-Core/src/Alt/Core/Constants/field.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,3 +32,28 @@ def getHeight(self, units: Types.Length = Types.Length.CM) -> float:
3232
self.height, fromType=self.units, toType=units
3333
)
3434
return cast(float, result)
35+
36+
def invertY(y: Conversions.NumericType, lengthType: Types.Length = Types.Length.CM) -> float:
37+
"""
38+
Invert the Y coordinate relative to field height
39+
40+
Args:
41+
yCM: Y coordinate in units specified, or by default CM
42+
43+
Returns:
44+
Inverted Y coordinate (field height - Y)
45+
"""
46+
return Field.getInstance().getHeight(lengthType) - y
47+
48+
49+
def invertX(x: Conversions.NumericType, lengthType: Types.Length = Types.Length.CM) -> float:
50+
"""
51+
Invert the X coordinate relative to field width
52+
53+
Args:
54+
xCM: X coordinate in the units specified, or by default CM
55+
56+
Returns:
57+
Inverted X coordinate (field width - X)
58+
"""
59+
return Field.getInstance().getWidth(lengthType) - x

Alt-Core/src/Alt/Core/Operators/LogStreamOperator.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
Sentinel = getChildLogger("Log_Stream_Operator")
2020

21+
LOGPATH = "logs"
2122

2223
class LogStreamOperator:
2324
"""
@@ -31,8 +32,6 @@ class LogStreamOperator:
3132
running (bool): Indicates if the log stream operator is running.
3233
"""
3334

34-
LOGPATH = "logs"
35-
3635
def __init__(self, app: Flask, manager: multiprocessing.managers.SyncManager):
3736
"""
3837
Initializes the LogStreamOperator.
@@ -77,7 +76,7 @@ def generate_logs(queue=log_queue):
7776
continue
7877

7978
self.app.add_url_rule(
80-
f"/{name}/{self.LOGPATH}",
79+
f"/{name}/{LOGPATH}",
8180
view_func=self._create_log_view_func(generate_logs, name),
8281
)
8382

Alt-Core/src/Alt/Core/Operators/StreamOperator.py

Lines changed: 2 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,10 @@ class serves as a wrapper for accessing and manipulating the stream data between
1212
import multiprocessing
1313
import time
1414
import cv2
15-
import numpy as np
16-
from multiprocessing import managers
17-
from typing import Dict, Optional
15+
from typing import Dict
1816
from flask import Flask, Response, stream_with_context
19-
from werkzeug.serving import make_server
2017
from .LogOperator import getChildLogger
21-
from ..Constants.AgentConstants import Proxy
18+
from .StreamProxy import StreamProxy
2219
from ..Utils.network import DEVICEIP
2320

2421
Sentinel = getChildLogger("Stream_Operator")
@@ -153,75 +150,3 @@ def close_stream(self, name: str):
153150
Sentinel.info(f"Closed stream: {name}")
154151

155152

156-
class StreamProxy(Proxy):
157-
"""Wrapper for accessing and manipulating a stream from another process.
158-
159-
This class extends the DictProxy used to transfer data
160-
between processes, specifically for MJPEG video streaming.
161-
162-
Attributes:
163-
__streamDict (managers.DictProxy): The underlying dictionary proxy
164-
holding stream data.
165-
"""
166-
167-
def __init__(self, streamDict: managers.DictProxy, streamPath: str):
168-
"""Initializes a StreamProxy instance.
169-
170-
Args:
171-
streamDict (managers.DictProxy): Proxy for accessing shared data.
172-
streamPath (str): URL path of the video stream.
173-
"""
174-
self.__streamDict = streamDict
175-
self.__streamDict["stream_path"] = streamPath
176-
self.__streamDict["frame_count"] = 0
177-
178-
def put(self, frame: np.ndarray) -> None:
179-
"""Stores a new video frame in the proxy.
180-
181-
Args:
182-
frame (np.ndarray): The video frame to store.
183-
"""
184-
self.__streamDict["frame"] = frame
185-
self.__streamDict["frame_count"] = self.__streamDict.get("frame_count", 0) + 1
186-
187-
def get(self) -> Optional[np.ndarray]:
188-
"""Retrieves the current video frame from the proxy.
189-
190-
Returns:
191-
Optional[np.ndarray]: The latest frame, or None if no frame is available.
192-
"""
193-
return self.__streamDict.get("frame")
194-
195-
def getFrameCount(self) -> int:
196-
"""Gets the current count of frames stored in the proxy.
197-
198-
Returns:
199-
int: The total number of frames sent.
200-
"""
201-
return self.__streamDict["frame_count"]
202-
203-
def getStreamPath(self) -> str:
204-
"""Gets the URL path of the video stream.
205-
206-
Returns:
207-
str: The path where the stream can be accessed.
208-
"""
209-
return self.__streamDict["stream_path"]
210-
211-
def __getstate__(self):
212-
"""Returns the state of the StreamProxy for pickling.
213-
214-
This method enables the StreamProxy to be correctly serialized.
215-
216-
Returns:
217-
managers.DictProxy: The underlying stream dictionary proxy.
218-
"""
219-
return self.__streamDict
220-
221-
def __setstate__(self, state):
222-
"""Restores the state of the StreamProxy from a pickled state.
223-
224-
Args:
225-
state (dict): The state to restore from.
226-
"""
227-
self.__streamDict = state
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
from __future__ import annotations
2+
3+
import numpy as np
4+
from multiprocessing import managers
5+
from typing import Optional
6+
from .LogOperator import getChildLogger
7+
from ..Constants.AgentConstants import Proxy
8+
9+
Sentinel = getChildLogger("Stream_Operator")
10+
11+
class StreamProxy(Proxy):
12+
"""Wrapper for accessing and manipulating a stream from another process.
13+
14+
This class extends the DictProxy used to transfer data
15+
between processes, specifically for MJPEG video streaming.
16+
17+
Attributes:
18+
__streamDict (managers.DictProxy): The underlying dictionary proxy
19+
holding stream data.
20+
"""
21+
22+
def __init__(self, streamDict: managers.DictProxy, streamPath: str):
23+
"""Initializes a StreamProxy instance.
24+
25+
Args:
26+
streamDict (managers.DictProxy): Proxy for accessing shared data.
27+
streamPath (str): URL path of the video stream.
28+
"""
29+
self.__streamDict = streamDict
30+
self.__streamDict["stream_path"] = streamPath
31+
self.__streamDict["frame_count"] = 0
32+
33+
def put(self, frame: np.ndarray) -> None:
34+
"""Stores a new video frame in the proxy.
35+
36+
Args:
37+
frame (np.ndarray): The video frame to store.
38+
"""
39+
self.__streamDict["frame"] = frame
40+
self.__streamDict["frame_count"] = self.__streamDict.get("frame_count", 0) + 1
41+
42+
def get(self) -> Optional[np.ndarray]:
43+
"""Retrieves the current video frame from the proxy.
44+
45+
Returns:
46+
Optional[np.ndarray]: The latest frame, or None if no frame is available.
47+
"""
48+
return self.__streamDict.get("frame")
49+
50+
def getFrameCount(self) -> int:
51+
"""Gets the current count of frames stored in the proxy.
52+
53+
Returns:
54+
int: The total number of frames sent.
55+
"""
56+
return self.__streamDict["frame_count"]
57+
58+
def getStreamPath(self) -> str:
59+
"""Gets the URL path of the video stream.
60+
61+
Returns:
62+
str: The path where the stream can be accessed.
63+
"""
64+
return self.__streamDict["stream_path"]
65+
66+
def __getstate__(self):
67+
"""Returns the state of the StreamProxy for pickling.
68+
69+
This method enables the StreamProxy to be correctly serialized.
70+
71+
Returns:
72+
managers.DictProxy: The underlying stream dictionary proxy.
73+
"""
74+
return self.__streamDict
75+
76+
def __setstate__(self, state):
77+
"""Restores the state of the StreamProxy from a pickled state.
78+
79+
Args:
80+
state (dict): The state to restore from.
81+
"""
82+
self.__streamDict = state

Alt-Core/src/Alt/Core/Units/Conversions.py

Lines changed: 0 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
from collections.abc import Iterable
44
from typing import Any, Union, TypeVar, Callable, Tuple, cast
55
from . import Types, Measurements
6-
from ..Constants import Field
76

87
# Type variables for better typing
98
NumericType = Union[float, int]
@@ -66,32 +65,6 @@ def toint(
6665
return __convert(value, int)
6766

6867

69-
def invertY(y: NumericType, lengthType: Types.Length = Types.Length.CM) -> float:
70-
"""
71-
Invert the Y coordinate relative to field height
72-
73-
Args:
74-
yCM: Y coordinate in units specified, or by default CM
75-
76-
Returns:
77-
Inverted Y coordinate (field height - Y)
78-
"""
79-
return Field.getInstance().getHeight(lengthType) - y
80-
81-
82-
def invertX(x: NumericType, lengthType: Types.Length = Types.Length.CM) -> float:
83-
"""
84-
Invert the X coordinate relative to field width
85-
86-
Args:
87-
xCM: X coordinate in the units specified, or by default CM
88-
89-
Returns:
90-
Inverted X coordinate (field width - X)
91-
"""
92-
return Field.getInstance().getWidth(lengthType) - x
93-
94-
9568
def convertLength(
9669
value: Union[Iterable[NumericType], NumericType],
9770
fromType: Types.Length,

0 commit comments

Comments
 (0)