11import functools
22import inspect
3+ from typing import Generic , Optional , TypeVar
34
45from networktables import NetworkTables
56from ntcore .value import Value
67
7- # Only used as a marker
8- class _TunableProperty (property ):
9- pass
8+ V = TypeVar ("V" )
109
1110
12- class _AutosendProperty (_TunableProperty ):
13- pass
14-
15-
16- def tunable (default , * , writeDefault = True , subtable = None , doc = None ):
11+ class tunable (Generic [V ]):
1712 """
1813 This allows you to define simple properties that allow you to easily
1914 communicate with other programs via NetworkTables.
@@ -23,18 +18,17 @@ def tunable(default, *, writeDefault=True, subtable=None, doc=None):
2318
2419 class MyRobot(magicbot.MagicRobot):
2520
26- my_component = MyComponent
21+ my_component: MyComponent
2722
28- ...
23+ ...
2924
3025 from magicbot import tunable
3126
3227 class MyComponent:
3328
3429 # define the tunable property
3530 foo = tunable(True)
36-
37-
31+
3832 def execute(self):
3933
4034 # set the variable
@@ -65,33 +59,48 @@ def execute(self):
6559 # the name of the key is related to the name of the variable name in the
6660 # robot class
6761
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" ):
62+ __slots__ = (
63+ "_ntdefault" ,
64+ "_ntsubtable" ,
65+ "_ntwritedefault" ,
66+ # "__doc__",
67+ "_mkv" ,
68+ "_nt" ,
69+ )
70+
71+ def __init__ (
72+ self ,
73+ default : V ,
74+ * ,
75+ writeDefault : bool = True ,
76+ subtable : Optional [str ] = None
77+ # , doc: Optional[str] = None
78+ ) -> None :
79+ self ._ntdefault = default
80+ self ._ntsubtable = subtable
81+ self ._ntwritedefault = writeDefault
82+ # self.__doc__ = doc
83+
84+ self ._mkv = Value .getFactory (default )
85+ self ._nt = NetworkTables
86+
87+ def __get__ (self , instance , owner ) -> V :
88+ if instance is not None :
89+ return instance ._tunables [self ].value
90+ return self
91+
92+ def __set__ (self , instance , value ) -> None :
93+ v = instance ._tunables [self ]
94+ self ._nt ._api .setEntryValueById (v ._local_id , self ._mkv (value ))
95+
96+
97+ def setup_tunables (component , cname : str , prefix : Optional [str ] = "components" ) -> None :
8798 """
8899 Connects the tunables on an object to NetworkTables.
89100
90101 :param component: Component object
91102 :param cname: Name of component
92- :type cname: str
93103 :param prefix: Prefix to use, or no prefix if None
94- :type prefix: str
95104
96105 .. note:: This is not needed in normal use, only useful
97106 for testing
@@ -104,27 +113,27 @@ def setup_tunables(component, cname, prefix="components"):
104113 else :
105114 prefix = "/%s/%s" % (prefix , cname )
106115
116+ tunables = {}
117+
107118 for n in dir (cls ):
108119 if n .startswith ("_" ):
109120 continue
110121
111122 prop = getattr (cls , n )
112- if not isinstance (prop , _TunableProperty ):
123+ if not isinstance (prop , tunable ):
113124 continue
114125
115126 if prop ._ntsubtable :
116127 key = "%s/%s/%s" % (prefix , prop ._ntsubtable , n )
117128 else :
118129 key = "%s/%s" % (prefix , n )
119130
120- ntattr = "_Tunable__%s" % n
121-
122131 ntvalue = NetworkTables .getGlobalAutoUpdateValue (
123132 key , prop ._ntdefault , prop ._ntwritedefault
124133 )
125- # double indirection
126- setattr ( component , ntattr , ntvalue )
127- prop . _ntattr = ntattr
134+ tunables [ prop ] = ntvalue
135+
136+ component . _tunables = tunables
128137
129138
130139def feedback (f = None , * , key : str = None ):
0 commit comments