chiark / gitweb /
Add absolute motion
[legofocuser.git] / legofocuser.py
1 #!/usr/bin/env python3
2 import sys
3 from pathlib import Path
4 import random
5 sys.path.insert(0, str(Path.cwd().parent))
6 from pyindi.device import device
7
8 from buildhat import Motor
9 from buildhat.motors import MotorRunmode
10
11 """
12 This file uses a skeleton xml file to initialize and
13 define properties. Similar to this example at indilib
14 https://www.indilib.org/developers/driver-howto.html#h2-properties
15 """
16
17
18 class LegoFocuser(device):
19     
20     def ISGetProperties(self, device=None):
21         """Property Definiations are generated
22         by initProperties and buildSkeleton. No
23         need to do it here. """
24         pass
25
26     def initProperties(self):
27         """Build the vector properties from
28         the skeleton file."""
29         self.buildSkeleton("legofocuser.xml")
30
31     def ISNewText(self, device, name, names, values):
32         """A new text vector has been updated from 
33         the client. In this case we update the text
34         vector with the IUUpdate function. In a real
35         device driver you would probably want to do 
36         something more than that. 
37
38         This function is always called by the 
39         mainloop
40         """
41
42         # BUILD_HAT_PORT - the port on the Build HAT to which the
43         # motor is connected.
44         
45         if name == "BUILD_HAT_PORT":
46             try:
47                 port = self.IUUpdate(device, name, names, values)
48                 port.state='Ok'
49                 port["PORT"].value = port["PORT"].value.strip()
50                 self.IDSet(port)
51             except Exception as error:
52                 self.IDMessage(f"BUILD_HAT_PORT error: {error}")
53         
54         self.IUUpdate(device, name, names, values, Set=True)
55
56     def ISNewNumber(self, device, name, names, values):
57
58         """A number vector has been updated from the client.
59         """
60
61         # FOCUS_SPEED - the absolute speed at which the focuser travels
62         
63         if name == "FOCUS_SPEED":
64
65             try:
66                 # update our idea of speed to what the client said
67                 
68                 focus = self.IUUpdate(device, name, names, values)
69
70                 self.update_speed_direction()
71
72                 # No exceptions, so set the status to OK
73
74                 focus.state='Ok'
75                 self.IDSet(focus)
76                 
77             except Exception as error:
78                 self.IDMessage(f"FOCUS_SPEED error: {error}")
79
80                 # Try to set the status to Alert (red)
81
82                 focus.state='Alert'
83                 self.IDSet(focus)
84
85                 raise
86         
87         # REL_FOCUS_POSITION - request a focuser move by
88         # FOCUS_RELATIVE_POSITION ticks
89             
90         if name == "REL_FOCUS_POSITION":
91             try:
92
93                 # Update our idea of the request
94
95                 focpos = self.IUUpdate(device, name, names, values)
96                 focposval = focpos["FOCUS_RELATIVE_POSITION"].value
97
98                 # Ticks are degrees for us.  Speed is the motor's
99                 # default
100                 
101                 self.motor.run_for_degrees(focposval, blocking=False)
102
103                 # We now need to set the state to Busy until the motor
104                 # stops running.
105
106                 focpos.state='Busy'
107                 self.IDSet(focpos)
108
109                 # And set a callback to return to OK when the motor
110                 # has finished
111
112                 self.IEAddTimer(100, self.checkmotoridle)
113                 
114             except Exception as error:
115                 self.IDMessage(f"IUUpdate error: {error}")
116                 raise
117
118         # ABS_FOCUS_POSITION - request a focuser move to
119         # FOCUS_ABSOLUTE_POSITION ticks
120
121         if name == "ABS_FOCUS_POSITION":
122             try:
123
124                 # Update our idea of the request
125
126                 focpos = self.IUUpdate(device, name, names, values)
127                 focposval = focpos["FOCUS_ABSOLUTE_POSITION"].value
128
129                 # Assuming 0 is the bottom of the focuser travel
130                 # we move to the new position
131
132                 curpos = self.motor.get_position()
133                 self.motor.run_for_degrees(focposval-curpos, blocking=False)
134                 
135                 # We now need to set the state to Busy until the motor
136                 # stops running.
137
138                 focpos.state='Busy'
139                 self.IDSet(focpos)
140
141                 # And set a callback to return to OK when the motor
142                 # has finished
143
144                 self.IEAddTimer(100, self.checkmotoridle)
145
146             except Exception as error:
147                 self.IDMessage(f"IUUpdate error: {error}")
148                 raise
149
150         if name == "FOCUS_TIMER":
151
152             try:
153                 focus = self.IUUpdate(device, name, names, values)
154                 if focus["FOCUS_TIMER_VALUE"].value != 0:
155                     motor.run_for_seconds(1)
156                     # focus["FOCUS_TIMER_VALUE"].value / 1000)
157             except Exception as error:
158                 self.IDMessage(f"IUUpdate error: {error}")
159                 raise
160                                           
161     
162     def ISNewSwitch(self, device, name, names, values):
163
164         """A numer switch has been updated from the client.
165         This function handles when a new switch
166         
167         This function is always called by the 
168         mainloop
169         """
170
171         if name == "CONNECTION":
172
173             try:
174                 conn = self.IUUpdate(device, name, names, values)
175                 if conn["CONNECT"].value == 'Off':
176                     conn.state = "Idle"
177                     self._is_connected = False
178                 else:
179                     conn.state = "Busy"
180                     self.IDSet(conn)
181                     prt = self.IUFind("BUILD_HAT_PORT")
182                     portval = prt["PORT"].value.strip()
183                     self.motor = Motor(portval)
184                     conn.state = "Ok"
185                     self._is_connected = True
186                     
187                 self.IDSet(conn)
188
189             except Exception as error:
190                 self.IDMessage(f"IUUpdate error: {error}")
191                 raise
192             
193         if name == "FOCUS_MOTION":
194             try:
195                 focus = self.IUUpdate(device, name, names, values, Set=True)
196
197                 self.update_speed_direction()
198
199                 # No exceptions, so set the status to OK
200
201                 focus.state='Ok'
202                 self.IDSet(focus)
203                 
204             except Exception as error:
205                 self.IDMessage(f"FOCUS_MOTION error: {error}")
206
207                 # Try to set the status to Alert (red)
208
209                 focus.state='Alert'
210                 self.IDSet(focus)
211
212         if name == "FOCUS_ABORT_MOTION":
213             try:
214                 self.motor.stop()
215                 fam = self.IUFind("FOCUS_ABORT_MOTION")
216                 fam["ABORT"].value = "Off"
217                 fam.state = "Ok"
218                 self.IDSet(fam)
219             except Exception as error:
220                 self.IDMessage(f"FOCUS_ABORT_MOTION error: {error}")
221
222                 fam.state='Alert'
223                 self.IDSet(fam)
224                 
225     def checkmotoridle(self):
226         if self.motor._runmode == MotorRunmode.NONE:
227             focpos = self.IUFind("REL_FOCUS_POSITION")
228             focpos.state = "Ok"
229             self.IDSet(focpos)
230             focpos = self.IUFind("ABS_FOCUS_POSITION")
231             focpos.state = "Ok"
232             self.IDSet(focpos)
233         else:
234             self.IEAddTimer(100, self.checkmotoridle)
235
236     def run_until_resistance(self):
237
238         # run motor until it appears it encounters resistance
239         # returns number of degrees travelled
240
241         # Initial absolute position of motor
242
243         ipos = self.motor.get_aposition()
244         pos = ipos
245         revs = 0
246         
247         while abs(pos-ipos) < 15:
248             self.motor.run_for_degrees(360)
249             self.motor.run_to_position(ipos)
250             pos = self.motor.get_position()
251             revs += 1
252             
253         return revs
254             
255     def update_speed_direction(self):
256
257         # Absolute speed
258
259         speed = int(self.IUFind("FOCUS_SPEED")["FOCUS_SPEED_VALUE"].value)
260
261         # since the Build HAT API uses positive/negative speed
262         # for direction, pull out the direction of focus motion
263                 
264         direction = 1 if (self.IUFind("FOCUS_MOTION"))["FOCUS_INWARD"].value == 'On' else -1
265
266         # And flip if the INDI focus reverse switch is set
267
268         if (self.IUFind("FOCUS_REVERSE_MOTION"))["ENABLED"].value == 'On':
269             direction *= -1
270
271         # And send the result to the HAT.
272         
273         self.motor.set_default_speed(speed * direction)
274
275
276 lf = LegoFocuser()
277 lf.start()