blob: 5b7e8be9ff2f3c8328fb002159b20813587bf879 [file] [log] [blame]
Brian Silvermanf7f267a2017-02-04 16:16:08 -08001/*----------------------------------------------------------------------------*/
2/* Copyright (c) FIRST 2016-2017. All Rights Reserved. */
3/* Open Source Software - may be modified and shared by FRC teams. The code */
4/* must be accompanied by the FIRST BSD license file in the root directory of */
5/* the project. */
6/*----------------------------------------------------------------------------*/
7
8#include "HAL/cpp/SerialHelper.h"
9
10#include <algorithm>
11#include <cstdio>
12#include <cstring>
13
14#include "../visa/visa.h"
15#include "HAL/Errors.h"
16#include "llvm/StringRef.h"
17
18constexpr const char* OnboardResourceVISA = "ASRL1::INSTR";
19constexpr const char* MxpResourceVISA = "ASRL2::INSTR";
20
21constexpr const char* OnboardResourceOS = "/dev/ttyS0";
22constexpr const char* MxpResourceOS = "/dev/ttyS1";
23
24namespace hal {
25std::string SerialHelper::m_usbNames[2]{"", ""};
26
27priority_mutex SerialHelper::m_nameMutex;
28
29SerialHelper::SerialHelper() {
30 viOpenDefaultRM(reinterpret_cast<ViSession*>(&m_resourceHandle));
31}
32
33std::string SerialHelper::GetVISASerialPortName(HAL_SerialPort port,
34 int32_t* status) {
35 if (port == HAL_SerialPort::HAL_SerialPort_Onboard) {
36 return OnboardResourceVISA;
37 } else if (port == HAL_SerialPort::HAL_SerialPort_MXP) {
38 return MxpResourceVISA;
39 }
40
41 QueryHubPaths(status);
42
43 // If paths are empty or status error, return error
44 if (*status != 0 || m_visaResource.empty() || m_osResource.empty() ||
45 m_sortedHubPath.empty()) {
46 *status = HAL_SERIAL_PORT_NOT_FOUND;
47 return "";
48 }
49
50 int32_t visaIndex = GetIndexForPort(port, status);
51 if (visaIndex == -1) {
52 *status = HAL_SERIAL_PORT_NOT_FOUND;
53 return "";
54 // Error
55 } else {
56 return m_visaResource[visaIndex].str();
57 }
58}
59
60std::string SerialHelper::GetOSSerialPortName(HAL_SerialPort port,
61 int32_t* status) {
62 if (port == HAL_SerialPort::HAL_SerialPort_Onboard) {
63 return OnboardResourceOS;
64 } else if (port == HAL_SerialPort::HAL_SerialPort_MXP) {
65 return MxpResourceOS;
66 }
67
68 QueryHubPaths(status);
69
70 // If paths are empty or status error, return error
71 if (*status != 0 || m_visaResource.empty() || m_osResource.empty() ||
72 m_sortedHubPath.empty()) {
73 *status = HAL_SERIAL_PORT_NOT_FOUND;
74 return "";
75 }
76
77 int32_t osIndex = GetIndexForPort(port, status);
78 if (osIndex == -1) {
79 *status = HAL_SERIAL_PORT_NOT_FOUND;
80 return "";
81 // Error
82 } else {
83 return m_osResource[osIndex].str();
84 }
85}
86
87std::vector<std::string> SerialHelper::GetVISASerialPortList(int32_t* status) {
88 std::vector<std::string> retVec;
89
90 // Always add 2 onboard ports
91 retVec.emplace_back(OnboardResourceVISA);
92 retVec.emplace_back(MxpResourceVISA);
93
94 QueryHubPaths(status);
95
96 // If paths are empty or status error, return only onboard list
97 if (*status != 0 || m_visaResource.empty() || m_osResource.empty() ||
98 m_sortedHubPath.empty()) {
99 *status = 0;
100 return retVec;
101 }
102
103 for (auto& i : m_visaResource) {
104 retVec.emplace_back(i.str());
105 }
106
107 return retVec;
108}
109
110std::vector<std::string> SerialHelper::GetOSSerialPortList(int32_t* status) {
111 std::vector<std::string> retVec;
112
113 // Always add 2 onboard ports
114 retVec.emplace_back(OnboardResourceOS);
115 retVec.emplace_back(MxpResourceOS);
116
117 QueryHubPaths(status);
118
119 // If paths are empty or status error, return only onboard list
120 if (*status != 0 || m_visaResource.empty() || m_osResource.empty() ||
121 m_sortedHubPath.empty()) {
122 *status = 0;
123 return retVec;
124 }
125
126 for (auto& i : m_osResource) {
127 retVec.emplace_back(i.str());
128 }
129
130 return retVec;
131}
132
133void SerialHelper::SortHubPathVector() {
134 m_sortedHubPath.clear();
135 m_sortedHubPath = m_unsortedHubPath;
136 std::sort(m_sortedHubPath.begin(), m_sortedHubPath.end(),
137 [](const llvm::SmallVectorImpl<char>& lhs,
138 const llvm::SmallVectorImpl<char>& rhs) -> int {
139 llvm::StringRef lhsRef(lhs.begin(), lhs.size());
140 llvm::StringRef rhsRef(rhs.begin(), rhs.size());
141 return lhsRef.compare(rhsRef);
142 });
143}
144
145void SerialHelper::CoiteratedSort(
146 llvm::SmallVectorImpl<llvm::SmallString<16>>& vec) {
147 llvm::SmallVector<llvm::SmallString<16>, 4> sortedVec;
148 for (auto& str : m_sortedHubPath) {
149 for (size_t i = 0; i < m_unsortedHubPath.size(); i++) {
150 if (llvm::StringRef{m_unsortedHubPath[i].begin(),
151 m_unsortedHubPath[i].size()}
152 .equals(llvm::StringRef{str.begin(), str.size()})) {
153 sortedVec.push_back(vec[i]);
154 break;
155 }
156 }
157 }
158 vec = sortedVec;
159}
160
161void SerialHelper::QueryHubPaths(int32_t* status) {
162 // VISA resource matching string
163 const char* str = "?*";
164 // Items needed for VISA
165 ViUInt32 retCnt = 0;
166 ViFindList viList = 0;
167 ViChar desc[VI_FIND_BUFLEN];
168 *status = viFindRsrc(m_resourceHandle, const_cast<char*>(str), &viList,
169 &retCnt, desc);
170
171 if (*status < 0) {
172 // Handle the bad status elsewhere
173 // Note let positive statii (warnings) continue
174 goto done;
175 }
176 // Status might be positive, so reset it to 0
177 *status = 0;
178
179 // Storage buffers for Visa calls and system exec calls
180 char osName[256];
181 char execBuffer[128];
182
183 // Loop through all returned VISA objects.
184 // Increment the internal VISA ptr every loop
185 for (size_t i = 0; i < retCnt; i++, viFindNext(viList, desc)) {
186 // Ignore any matches to the 2 onboard ports
187 if (std::strcmp(OnboardResourceVISA, desc) == 0 ||
188 std::strcmp(MxpResourceVISA, desc) == 0) {
189 continue;
190 }
191
192 // Open the resource, grab its interface name, and close it.
193 ViSession vSession;
194 *status = viOpen(m_resourceHandle, desc, VI_NULL, VI_NULL, &vSession);
195 if (*status < 0) goto done;
196 *status = 0;
197
198 *status = viGetAttribute(vSession, VI_ATTR_INTF_INST_NAME, &osName);
199 // Ignore an error here, as we want to close the session on an error
200 // Use a seperate close variable so we can check
201 ViStatus closeStatus = viClose(vSession);
202 if (*status < 0) goto done;
203 if (closeStatus < 0) goto done;
204 *status = 0;
205
206 // split until (/dev/
207 llvm::StringRef devNameRef = llvm::StringRef{osName}.split("(/dev/").second;
208 // String not found, continue
209 if (devNameRef.equals("")) continue;
210
211 // Split at )
212 llvm::StringRef matchString = devNameRef.split(')').first;
213 if (matchString.equals(devNameRef)) continue;
214
215 // Run find using pipe to get a list of system accessors
216 llvm::SmallString<128> val(
217 "sh -c \"find /sys/devices/soc0 | grep amba | grep usb | grep ");
218 val += matchString;
219 val += "\"";
220
221 // Pipe code found on StackOverflow
222 // http://stackoverflow.com/questions/478898/how-to-execute-a-command-and-get-output-of-command-within-c-using-posix
223
224 // Using std::string because this is guarenteed to be large
225 std::string output = "";
226
227 std::shared_ptr<FILE> pipe(popen(val.c_str(), "r"), pclose);
228 // Just check the next item on a pipe failure
229 if (!pipe) continue;
230 while (!feof(pipe.get())) {
231 if (std::fgets(execBuffer, 128, pipe.get()) != 0) output += execBuffer;
232 }
233
234 if (!output.empty()) {
235 llvm::SmallVector<llvm::StringRef, 16> pathSplitVec;
236 // Split output by line, grab first line, and split it into
237 // individual directories
238 llvm::StringRef{output}.split('\n').first.split(pathSplitVec, '/', -1,
239 false);
240
241 // Find each individual item index
242
243 const char* usb1 = "usb1";
244 const char* tty = "tty";
245
246 int findusb = -1;
247 int findtty = -1;
248 int findregex = -1;
249 for (size_t i = 0; i < pathSplitVec.size(); i++) {
250 if (findusb == -1 && pathSplitVec[i].equals(usb1)) {
251 findusb = i;
252 }
253 if (findtty == -1 && pathSplitVec[i].equals(tty)) {
254 findtty = i;
255 }
256 if (findregex == -1 && pathSplitVec[i].equals(matchString)) {
257 findregex = i;
258 }
259 }
260
261 // Get the index for our device
262 int hubIndex = findtty;
263 if (findtty == -1) hubIndex = findregex;
264
265 int devStart = findusb + 1;
266
267 if (hubIndex < devStart) continue;
268
269 // Add our devices to our list
270 m_unsortedHubPath.emplace_back(
271 llvm::StringRef{pathSplitVec[hubIndex - 2]});
272 m_visaResource.emplace_back(desc);
273 m_osResource.emplace_back(
274 llvm::StringRef{osName}.split("(").second.split(")").first);
275 }
276 }
277
278 SortHubPathVector();
279
280 CoiteratedSort(m_visaResource);
281 CoiteratedSort(m_osResource);
282done:
283 viClose(viList);
284}
285
286int32_t SerialHelper::GetIndexForPort(HAL_SerialPort port, int32_t* status) {
287 // Hold lock whenever we're using the names array
288 std::lock_guard<priority_mutex> lock(m_nameMutex);
289
290 std::string portString = m_usbNames[port - 2];
291
292 llvm::SmallVector<int32_t, 4> indices;
293
294 // If port has not been assigned, find the one to assign
295 if (portString.empty()) {
296 for (size_t i = 0; i < 2; i++) {
297 // Remove all used ports
298 auto idx = std::find(m_sortedHubPath.begin(), m_sortedHubPath.end(),
299 m_usbNames[i]);
300 if (idx != m_sortedHubPath.end()) {
301 // found
302 m_sortedHubPath.erase(idx);
303 }
304 if (m_usbNames[i] == "") {
305 indices.push_back(i);
306 }
307 }
308
309 int32_t idx = -1;
310 for (size_t i = 0; i < indices.size(); i++) {
311 if (indices[i] == port - 2) {
312 idx = i;
313 break;
314 }
315 }
316
317 if (idx == -1) {
318 *status = HAL_SERIAL_PORT_NOT_FOUND;
319 return -1;
320 }
321
322 if (idx >= static_cast<int32_t>(m_sortedHubPath.size())) {
323 *status = HAL_SERIAL_PORT_NOT_FOUND;
324 return -1;
325 }
326
327 portString = m_sortedHubPath[idx].str();
328 m_usbNames[port - 2] = portString;
329 }
330
331 int retIndex = -1;
332
333 for (size_t i = 0; i < m_sortedHubPath.size(); i++) {
334 if (m_sortedHubPath[i].equals(portString)) {
335 retIndex = i;
336 break;
337 }
338 }
339
340 return retIndex;
341}
342
343} // namespace hal