9769cad6 |
1 | """ |
2 | Simple test for the USB relay DLL |
3 | Just plain calling the C library, no fancy OOP stuff |
4 | Uses CTYPES |
5 | |
6 | For python 2.7, 3 |
7 | """ |
8 | import sys, os, time |
9 | import ctypes |
10 | |
11 | print("Running on Python v." + str(sys.version)) |
12 | print("%d-bit mode" % ({4:32, 8:64}[ctypes.sizeof(ctypes.c_void_p)]) ) |
13 | |
14 | # Fix the path below if the library is not in current dir. |
15 | libpath = "." |
16 | |
17 | if sys.version_info.major >= 3: |
18 | def charpToString(charp): |
19 | return str(ctypes.string_at(charp), 'ascii') |
20 | def stringToCharp(s) : |
21 | return bytes(s, "ascii") |
22 | else: |
23 | def charpToString(charp) : |
24 | return str(ctypes.string_at(charp)) |
25 | def stringToCharp(s) : |
26 | return bytes(s) #bytes(s, "ascii") |
27 | |
28 | libfile = {'nt': "usb_relay_device.dll", |
29 | 'posix': "usb_relay_device.so", |
30 | 'darwin':"usb_relay_device.dylib", |
31 | } [os.name] |
32 | |
33 | #?? MAC => os.name == "posix" and sys.platform == "darwin" |
34 | |
35 | devids = [] |
36 | hdev = None |
37 | |
38 | def exc(msg): return Exception(msg) |
39 | |
40 | def fail(msg) : raise exc(msg) |
41 | |
42 | class L: pass # Global object for the DLL |
43 | setattr(L, "dll", None) |
44 | |
45 | def loadLib(): |
46 | # Load the C DLL ... |
47 | if not L.dll : |
48 | print("Loading DLL: %s" % ('/'.join([libpath, libfile]))) |
49 | try: |
50 | L.dll = ctypes.CDLL( '/'.join([libpath, libfile]) ) |
51 | except OSError: |
52 | fail("Failed load lib") |
53 | else: |
54 | print("lib already open") |
55 | #print(L.dll) |
56 | |
57 | usb_relay_lib_funcs = [ |
58 | # TYpes: h=handle (pointer sized), p=pointer, i=int, e=error num (int), s=string |
59 | ("usb_relay_device_enumerate", 'h', None), |
60 | ("usb_relay_device_close", 'e', 'h'), |
61 | ("usb_relay_device_open_with_serial_number", 'h', 'si'), |
62 | ("usb_relay_device_get_num_relays", 'i', 'h'), |
63 | ("usb_relay_device_get_id_string", 's', 'h'), |
64 | ("usb_relay_device_next_dev", 'h', 'h'), |
65 | ("usb_relay_device_get_status_bitmap", 'i', 'h'), |
66 | ("usb_relay_device_open_one_relay_channel", 'e', 'hi'), |
67 | ("usb_relay_device_close_one_relay_channel", 'e', 'hi'), |
68 | ("usb_relay_device_close_all_relay_channel", 'e', None) |
69 | ] |
70 | |
71 | |
72 | def getLibFunctions(): |
73 | """ Get needed functions and configure types; call lib. init. |
74 | """ |
75 | assert L.dll |
76 | |
77 | #Get lib version (my extension, not in the original dll) |
78 | libver = L.dll.usb_relay_device_lib_version() |
79 | print("%s version: 0x%X" % (libfile,libver)) |
80 | |
81 | ret = L.dll.usb_relay_init() |
82 | if ret != 0 : fail("Failed lib init!") |
83 | |
84 | """ |
85 | Tweak imported C functions |
86 | This is required in 64-bit mode. Optional for 32-bit (pointer size=int size) |
87 | Functions that return and receive ints or void work without specifying types. |
88 | """ |
89 | ctypemap = { 'e': ctypes.c_int, 'h':ctypes.c_void_p, 'p': ctypes.c_void_p, |
90 | 'i': ctypes.c_int, 's': ctypes.c_char_p} |
91 | for x in usb_relay_lib_funcs : |
92 | fname, ret, param = x |
93 | try: |
94 | f = getattr(L.dll, fname) |
95 | except Exception: |
96 | fail("Missing lib export:" + fname) |
97 | |
98 | ps = [] |
99 | if param : |
100 | for p in param : |
101 | ps.append( ctypemap[p] ) |
102 | f.restype = ctypemap[ret] |
103 | f.argtypes = ps |
104 | setattr(L, fname, f) |
105 | |
106 | def openDevById(idstr): |
107 | #Open by known ID: |
108 | print("Opening " + idstr) |
109 | h = L.usb_relay_device_open_with_serial_number(stringToCharp(idstr), 5) |
110 | if not h: fail("Cannot open device with id="+idstr) |
111 | global numch |
112 | numch = L.usb_relay_device_get_num_relays(h) |
113 | if numch <= 0 or numch > 8 : fail("Bad number of channels, can be 1-8") |
114 | global hdev |
115 | hdev = h |
116 | print("Number of relays on device with ID=%s: %d" % (idstr, numch)) |
117 | |
118 | def closeDev(): |
119 | global hdev |
120 | L.usb_relay_device_close(hdev) |
121 | hdev = None |
122 | |
123 | def enumDevs(): |
124 | global devids |
125 | devids = [] |
126 | enuminfo = L.usb_relay_device_enumerate() |
127 | while enuminfo : |
128 | idstrp = L.usb_relay_device_get_id_string(enuminfo) |
129 | idstr = charpToString(idstrp) |
130 | print(idstr) |
131 | assert len(idstr) == 5 |
132 | if not idstr in devids : devids.append(idstr) |
133 | else : print("Warning! found duplicate ID=" + idstr) |
134 | enuminfo = L.usb_relay_device_next_dev(enuminfo) |
135 | |
136 | print("Found devices: %d" % len(devids)) |
137 | |
138 | def unloadLib(): |
139 | global hdev, L |
140 | if hdev: closeDev() |
141 | L.dll.usb_relay_exit() |
142 | L.dll = None |
143 | print("Lib closed") |
144 | |
145 | def testR2(): |
146 | """ Test one device with handle hdev, 1 or 2 channels """ |
147 | global numch, hdev |
148 | if numch <=0 or numch > 8: |
149 | fail("Bad number of channels on relay device!") |
150 | |
151 | ret = L.usb_relay_device_close_all_relay_channel(hdev) |
152 | if ret != 0: |
153 | fail("Failed OFF all!") |
154 | |
155 | st = L.usb_relay_device_get_status_bitmap(hdev) |
156 | if st < 0: fail("Bad status bitmask") |
157 | print("Relay num ch=%d state=%x" % (numch, st)) |
158 | |
159 | ret = L.usb_relay_device_close_one_relay_channel(hdev, numch+1) |
160 | if ret == 0: fail("Succeeded with bad channel num!") |
161 | ret = L.usb_relay_device_close_one_relay_channel(hdev, 0) |
162 | if ret == 0: fail("Succeeded with bad channel num!") |
163 | |
164 | # Play on/off |
165 | mask=0 |
166 | ret = L.usb_relay_device_open_one_relay_channel(hdev,1) |
167 | if ret != 0: fail("Failed R1 on!") |
168 | mask |= 0x1 |
169 | |
170 | if numch > 1: |
171 | time.sleep(1) |
172 | ret = L.usb_relay_device_open_one_relay_channel(hdev,2) |
173 | if ret != 0: failed("Failed R2 on!") |
174 | mask |= 0x2 |
175 | |
176 | #Note: Checking relay state after successful open/close is redundant, because the library does it. |
177 | # We check state here as a sanity test. |
178 | st = L.usb_relay_device_get_status_bitmap(hdev) |
179 | if st != mask: |
180 | fail("Bad state after relays on!") |
181 | |
182 | # Delays here are only for test, not really needed |
183 | time.sleep(1) |
184 | |
185 | ret = L.usb_relay_device_close_all_relay_channel(hdev) |
186 | if ret != 0: |
187 | fail("Failed OFF all!") |
188 | |
189 | st = L.usb_relay_device_get_status_bitmap(hdev) |
190 | if st != 0: |
191 | fail("Bad state after all off!") |
192 | |
193 | print("*** test R2 PASS ***") |
194 | |
195 | # main |
196 | def main(): |
197 | print("Test 2-ch relay") |
198 | loadLib() |
199 | getLibFunctions() |
200 | try: |
201 | print("Searching for compatible devices") |
202 | enumDevs() |
203 | if len(devids) != 0 : |
204 | # Test any 1st found dev . |
205 | print("Testing relay with ID=" + devids[0]) |
206 | openDevById(devids[0]) |
207 | testR2() |
208 | closeDev() |
209 | finally: |
210 | unloadLib() |
211 | |
212 | if __name__ == "__main__" : |
213 | main() |