11import functools
22import inspect
3+ import warnings
4+ from typing import Generic , Optional , TypeVar
35
46from networktables import NetworkTables
57from ntcore .value import Value
68
7- # Only used as a marker
8- class _TunableProperty (property ):
9- pass
9+ V = TypeVar ("V" )
1010
1111
12- class _AutosendProperty (_TunableProperty ):
13- pass
14-
15-
16- def tunable (default , * , writeDefault = True , subtable = None , doc = None ):
12+ class tunable (Generic [V ]):
1713 """
1814 This allows you to define simple properties that allow you to easily
1915 communicate with other programs via NetworkTables.
@@ -23,18 +19,17 @@ def tunable(default, *, writeDefault=True, subtable=None, doc=None):
2319
2420 class MyRobot(magicbot.MagicRobot):
2521
26- my_component = MyComponent
22+ my_component: MyComponent
2723
28- ...
24+ ...
2925
3026 from magicbot import tunable
3127
3228 class MyComponent:
3329
3430 # define the tunable property
3531 foo = tunable(True)
36-
37-
32+
3833 def execute(self):
3934
4035 # set the variable
@@ -65,33 +60,51 @@ def execute(self):
6560 # the name of the key is related to the name of the variable name in the
6661 # robot class
6762
68- nt = NetworkTables
69- mkv = Value .getFactory (default )
70-
71- def _get (self ):
72- return getattr (self , prop ._ntattr ).value
73-
74- def _set (self , value ):
75- v = getattr (self , prop ._ntattr )
76- nt ._api .setEntryValueById (v ._local_id , mkv (value ))
77-
78- prop = _TunableProperty (fget = _get , fset = _set , doc = doc )
79- prop ._ntdefault = default
80- prop ._ntsubtable = subtable
81- prop ._ntwritedefault = writeDefault
82-
83- return prop
84-
85-
86- def setup_tunables (component , cname , prefix = "components" ):
63+ __slots__ = (
64+ "_ntdefault" ,
65+ "_ntsubtable" ,
66+ "_ntwritedefault" ,
67+ # "__doc__",
68+ "_mkv" ,
69+ "_nt" ,
70+ )
71+
72+ def __init__ (
73+ self ,
74+ default : V ,
75+ * ,
76+ writeDefault : bool = True ,
77+ subtable : Optional [str ] = None ,
78+ doc = None
79+ ) -> None :
80+ if doc is not None :
81+ warnings .warn ("tunable no longer uses the doc argument" , stacklevel = 2 )
82+
83+ self ._ntdefault = default
84+ self ._ntsubtable = subtable
85+ self ._ntwritedefault = writeDefault
86+ # self.__doc__ = doc
87+
88+ self ._mkv = Value .getFactory (default )
89+ self ._nt = NetworkTables
90+
91+ def __get__ (self , instance , owner ) -> V :
92+ if instance is not None :
93+ return instance ._tunables [self ].value
94+ return self
95+
96+ def __set__ (self , instance , value ) -> None :
97+ v = instance ._tunables [self ]
98+ self ._nt ._api .setEntryValueById (v ._local_id , self ._mkv (value ))
99+
100+
101+ def setup_tunables (component , cname : str , prefix : Optional [str ] = "components" ) -> None :
87102 """
88103 Connects the tunables on an object to NetworkTables.
89104
90105 :param component: Component object
91106 :param cname: Name of component
92- :type cname: str
93107 :param prefix: Prefix to use, or no prefix if None
94- :type prefix: str
95108
96109 .. note:: This is not needed in normal use, only useful
97110 for testing
@@ -104,27 +117,27 @@ def setup_tunables(component, cname, prefix="components"):
104117 else :
105118 prefix = "/%s/%s" % (prefix , cname )
106119
120+ tunables = {}
121+
107122 for n in dir (cls ):
108123 if n .startswith ("_" ):
109124 continue
110125
111126 prop = getattr (cls , n )
112- if not isinstance (prop , _TunableProperty ):
127+ if not isinstance (prop , tunable ):
113128 continue
114129
115130 if prop ._ntsubtable :
116131 key = "%s/%s/%s" % (prefix , prop ._ntsubtable , n )
117132 else :
118133 key = "%s/%s" % (prefix , n )
119134
120- ntattr = "_Tunable__%s" % n
121-
122135 ntvalue = NetworkTables .getGlobalAutoUpdateValue (
123136 key , prop ._ntdefault , prop ._ntwritedefault
124137 )
125- # double indirection
126- setattr ( component , ntattr , ntvalue )
127- prop . _ntattr = ntattr
138+ tunables [ prop ] = ntvalue
139+
140+ component . _tunables = tunables
128141
129142
130143def feedback (f = None , * , key : str = None ):
0 commit comments