2020-12-26 14:12:05 -08:00
|
|
|
// Copyright (c) FIRST and other WPILib contributors.
|
|
|
|
|
// Open Source Software; you can modify and/or share it under the terms of
|
|
|
|
|
// the WPILib BSD license file in the root directory of this project.
|
2016-11-22 21:51:47 -08:00
|
|
|
|
2018-07-20 00:03:45 -07:00
|
|
|
#include "hal/cpp/SerialHelper.h"
|
2016-11-22 21:51:47 -08:00
|
|
|
|
|
|
|
|
#include <algorithm>
|
|
|
|
|
#include <cstdio>
|
|
|
|
|
#include <cstring>
|
2021-06-06 16:13:58 -07:00
|
|
|
#include <string_view>
|
2016-11-22 21:51:47 -08:00
|
|
|
|
2021-06-06 16:13:58 -07:00
|
|
|
#include <wpi/StringExtras.h>
|
2021-06-01 21:50:35 -07:00
|
|
|
#include <wpi/fs.h>
|
2017-08-27 00:11:52 -07:00
|
|
|
|
2018-07-20 00:03:45 -07:00
|
|
|
#include "hal/Errors.h"
|
2019-09-07 21:01:27 -07:00
|
|
|
#include "visa/visa.h"
|
2016-11-22 21:51:47 -08:00
|
|
|
|
2016-11-24 21:53:04 -08:00
|
|
|
constexpr const char* OnboardResourceVISA = "ASRL1::INSTR";
|
|
|
|
|
constexpr const char* MxpResourceVISA = "ASRL2::INSTR";
|
|
|
|
|
|
2016-12-20 22:16:05 -08:00
|
|
|
constexpr const char* OnboardResourceOS = "/dev/ttyS0";
|
|
|
|
|
constexpr const char* MxpResourceOS = "/dev/ttyS1";
|
2016-11-22 21:51:47 -08:00
|
|
|
|
|
|
|
|
namespace hal {
|
2017-08-23 22:07:46 -07:00
|
|
|
|
2016-11-22 21:51:47 -08:00
|
|
|
std::string SerialHelper::m_usbNames[2]{"", ""};
|
|
|
|
|
|
2017-11-13 09:51:48 -08:00
|
|
|
wpi::mutex SerialHelper::m_nameMutex;
|
2016-11-22 21:51:47 -08:00
|
|
|
|
2016-12-20 22:16:05 -08:00
|
|
|
SerialHelper::SerialHelper() {
|
|
|
|
|
viOpenDefaultRM(reinterpret_cast<ViSession*>(&m_resourceHandle));
|
|
|
|
|
}
|
2016-11-22 21:51:47 -08:00
|
|
|
|
2016-11-24 21:53:04 -08:00
|
|
|
std::string SerialHelper::GetVISASerialPortName(HAL_SerialPort port,
|
|
|
|
|
int32_t* status) {
|
2016-11-22 21:51:47 -08:00
|
|
|
if (port == HAL_SerialPort::HAL_SerialPort_Onboard) {
|
2016-11-24 21:53:04 -08:00
|
|
|
return OnboardResourceVISA;
|
2016-11-22 21:51:47 -08:00
|
|
|
} else if (port == HAL_SerialPort::HAL_SerialPort_MXP) {
|
2016-11-24 21:53:04 -08:00
|
|
|
return MxpResourceVISA;
|
2016-11-22 21:51:47 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QueryHubPaths(status);
|
|
|
|
|
|
|
|
|
|
// If paths are empty or status error, return error
|
|
|
|
|
if (*status != 0 || m_visaResource.empty() || m_osResource.empty() ||
|
|
|
|
|
m_sortedHubPath.empty()) {
|
|
|
|
|
*status = HAL_SERIAL_PORT_NOT_FOUND;
|
|
|
|
|
return "";
|
|
|
|
|
}
|
|
|
|
|
|
2016-11-24 21:53:04 -08:00
|
|
|
int32_t visaIndex = GetIndexForPort(port, status);
|
|
|
|
|
if (visaIndex == -1) {
|
|
|
|
|
*status = HAL_SERIAL_PORT_NOT_FOUND;
|
|
|
|
|
return "";
|
|
|
|
|
// Error
|
|
|
|
|
} else {
|
2021-06-06 16:13:58 -07:00
|
|
|
return std::string{m_visaResource[visaIndex].str()};
|
2016-11-24 21:53:04 -08:00
|
|
|
}
|
|
|
|
|
}
|
2016-11-22 21:51:47 -08:00
|
|
|
|
2016-11-24 21:53:04 -08:00
|
|
|
std::string SerialHelper::GetOSSerialPortName(HAL_SerialPort port,
|
|
|
|
|
int32_t* status) {
|
|
|
|
|
if (port == HAL_SerialPort::HAL_SerialPort_Onboard) {
|
|
|
|
|
return OnboardResourceOS;
|
|
|
|
|
} else if (port == HAL_SerialPort::HAL_SerialPort_MXP) {
|
|
|
|
|
return MxpResourceOS;
|
|
|
|
|
}
|
2016-11-22 21:51:47 -08:00
|
|
|
|
2016-11-24 21:53:04 -08:00
|
|
|
QueryHubPaths(status);
|
2016-11-22 21:51:47 -08:00
|
|
|
|
2016-11-24 21:53:04 -08:00
|
|
|
// If paths are empty or status error, return error
|
|
|
|
|
if (*status != 0 || m_visaResource.empty() || m_osResource.empty() ||
|
|
|
|
|
m_sortedHubPath.empty()) {
|
|
|
|
|
*status = HAL_SERIAL_PORT_NOT_FOUND;
|
|
|
|
|
return "";
|
|
|
|
|
}
|
2016-11-22 21:51:47 -08:00
|
|
|
|
2016-11-24 21:53:04 -08:00
|
|
|
int32_t osIndex = GetIndexForPort(port, status);
|
|
|
|
|
if (osIndex == -1) {
|
|
|
|
|
*status = HAL_SERIAL_PORT_NOT_FOUND;
|
|
|
|
|
return "";
|
|
|
|
|
// Error
|
|
|
|
|
} else {
|
2021-06-06 16:13:58 -07:00
|
|
|
return std::string{m_osResource[osIndex].str()};
|
2016-11-24 21:53:04 -08:00
|
|
|
}
|
|
|
|
|
}
|
2016-11-22 21:51:47 -08:00
|
|
|
|
2016-11-24 21:53:04 -08:00
|
|
|
std::vector<std::string> SerialHelper::GetVISASerialPortList(int32_t* status) {
|
|
|
|
|
std::vector<std::string> retVec;
|
2016-11-22 21:51:47 -08:00
|
|
|
|
2016-11-24 21:53:04 -08:00
|
|
|
// Always add 2 onboard ports
|
|
|
|
|
retVec.emplace_back(OnboardResourceVISA);
|
|
|
|
|
retVec.emplace_back(MxpResourceVISA);
|
2016-11-22 21:51:47 -08:00
|
|
|
|
2016-11-24 21:53:04 -08:00
|
|
|
QueryHubPaths(status);
|
|
|
|
|
|
|
|
|
|
// If paths are empty or status error, return only onboard list
|
|
|
|
|
if (*status != 0 || m_visaResource.empty() || m_osResource.empty() ||
|
|
|
|
|
m_sortedHubPath.empty()) {
|
|
|
|
|
*status = 0;
|
|
|
|
|
return retVec;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (auto& i : m_visaResource) {
|
|
|
|
|
retVec.emplace_back(i.str());
|
2016-11-22 21:51:47 -08:00
|
|
|
}
|
|
|
|
|
|
2016-11-24 21:53:04 -08:00
|
|
|
return retVec;
|
|
|
|
|
}
|
2016-11-22 21:51:47 -08:00
|
|
|
|
2016-11-24 21:53:04 -08:00
|
|
|
std::vector<std::string> SerialHelper::GetOSSerialPortList(int32_t* status) {
|
|
|
|
|
std::vector<std::string> retVec;
|
|
|
|
|
|
|
|
|
|
// Always add 2 onboard ports
|
|
|
|
|
retVec.emplace_back(OnboardResourceOS);
|
|
|
|
|
retVec.emplace_back(MxpResourceOS);
|
|
|
|
|
|
|
|
|
|
QueryHubPaths(status);
|
|
|
|
|
|
|
|
|
|
// If paths are empty or status error, return only onboard list
|
|
|
|
|
if (*status != 0 || m_visaResource.empty() || m_osResource.empty() ||
|
|
|
|
|
m_sortedHubPath.empty()) {
|
|
|
|
|
*status = 0;
|
|
|
|
|
return retVec;
|
2016-11-22 21:51:47 -08:00
|
|
|
}
|
|
|
|
|
|
2016-11-24 21:53:04 -08:00
|
|
|
for (auto& i : m_osResource) {
|
|
|
|
|
retVec.emplace_back(i.str());
|
2016-11-22 21:51:47 -08:00
|
|
|
}
|
2016-11-24 21:53:04 -08:00
|
|
|
|
|
|
|
|
return retVec;
|
2016-11-22 21:51:47 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SerialHelper::SortHubPathVector() {
|
|
|
|
|
m_sortedHubPath.clear();
|
|
|
|
|
m_sortedHubPath = m_unsortedHubPath;
|
|
|
|
|
std::sort(m_sortedHubPath.begin(), m_sortedHubPath.end(),
|
2018-04-29 23:33:19 -07:00
|
|
|
[](const wpi::SmallVectorImpl<char>& lhs,
|
|
|
|
|
const wpi::SmallVectorImpl<char>& rhs) -> int {
|
2021-06-06 16:13:58 -07:00
|
|
|
std::string_view lhsRef(lhs.begin(), lhs.size());
|
|
|
|
|
std::string_view rhsRef(rhs.begin(), rhs.size());
|
2016-11-22 21:51:47 -08:00
|
|
|
return lhsRef.compare(rhsRef);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SerialHelper::CoiteratedSort(
|
2018-04-29 23:33:19 -07:00
|
|
|
wpi::SmallVectorImpl<wpi::SmallString<16>>& vec) {
|
|
|
|
|
wpi::SmallVector<wpi::SmallString<16>, 4> sortedVec;
|
2016-11-22 21:51:47 -08:00
|
|
|
for (auto& str : m_sortedHubPath) {
|
|
|
|
|
for (size_t i = 0; i < m_unsortedHubPath.size(); i++) {
|
2021-06-06 16:13:58 -07:00
|
|
|
if (wpi::equals(std::string_view{m_unsortedHubPath[i].begin(),
|
|
|
|
|
m_unsortedHubPath[i].size()},
|
|
|
|
|
std::string_view{str.begin(), str.size()})) {
|
2016-11-22 21:51:47 -08:00
|
|
|
sortedVec.push_back(vec[i]);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-01-01 10:27:49 -08:00
|
|
|
vec.swap(sortedVec);
|
2016-11-22 21:51:47 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SerialHelper::QueryHubPaths(int32_t* status) {
|
|
|
|
|
// VISA resource matching string
|
|
|
|
|
const char* str = "?*";
|
|
|
|
|
// Items needed for VISA
|
|
|
|
|
ViUInt32 retCnt = 0;
|
|
|
|
|
ViFindList viList = 0;
|
|
|
|
|
ViChar desc[VI_FIND_BUFLEN];
|
|
|
|
|
*status = viFindRsrc(m_resourceHandle, const_cast<char*>(str), &viList,
|
|
|
|
|
&retCnt, desc);
|
|
|
|
|
|
|
|
|
|
if (*status < 0) {
|
|
|
|
|
// Handle the bad status elsewhere
|
|
|
|
|
// Note let positive statii (warnings) continue
|
2016-11-25 15:52:21 -08:00
|
|
|
goto done;
|
2016-11-22 21:51:47 -08:00
|
|
|
}
|
|
|
|
|
// Status might be positive, so reset it to 0
|
|
|
|
|
*status = 0;
|
|
|
|
|
|
2017-11-03 12:30:56 -07:00
|
|
|
// Storage buffer for Visa call
|
2016-11-22 21:51:47 -08:00
|
|
|
char osName[256];
|
|
|
|
|
|
|
|
|
|
// Loop through all returned VISA objects.
|
|
|
|
|
// Increment the internal VISA ptr every loop
|
|
|
|
|
for (size_t i = 0; i < retCnt; i++, viFindNext(viList, desc)) {
|
|
|
|
|
// Ignore any matches to the 2 onboard ports
|
2016-11-24 21:53:04 -08:00
|
|
|
if (std::strcmp(OnboardResourceVISA, desc) == 0 ||
|
|
|
|
|
std::strcmp(MxpResourceVISA, desc) == 0) {
|
2016-11-22 21:51:47 -08:00
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Open the resource, grab its interface name, and close it.
|
|
|
|
|
ViSession vSession;
|
|
|
|
|
*status = viOpen(m_resourceHandle, desc, VI_NULL, VI_NULL, &vSession);
|
2020-12-28 12:58:06 -08:00
|
|
|
if (*status < 0)
|
|
|
|
|
goto done;
|
2016-11-22 21:51:47 -08:00
|
|
|
*status = 0;
|
|
|
|
|
|
|
|
|
|
*status = viGetAttribute(vSession, VI_ATTR_INTF_INST_NAME, &osName);
|
|
|
|
|
// Ignore an error here, as we want to close the session on an error
|
2020-08-31 00:33:11 -07:00
|
|
|
// Use a separate close variable so we can check
|
2016-11-22 21:51:47 -08:00
|
|
|
ViStatus closeStatus = viClose(vSession);
|
2020-12-28 12:58:06 -08:00
|
|
|
if (*status < 0)
|
|
|
|
|
goto done;
|
|
|
|
|
if (closeStatus < 0)
|
|
|
|
|
goto done;
|
2016-11-22 21:51:47 -08:00
|
|
|
*status = 0;
|
|
|
|
|
|
2016-11-24 21:53:04 -08:00
|
|
|
// split until (/dev/
|
2021-06-06 16:13:58 -07:00
|
|
|
std::string_view devNameRef = wpi::split(osName, "(/dev/").second;
|
2016-11-24 21:53:04 -08:00
|
|
|
// String not found, continue
|
2021-06-06 16:13:58 -07:00
|
|
|
if (wpi::equals(devNameRef, ""))
|
2020-12-28 12:58:06 -08:00
|
|
|
continue;
|
2016-11-22 21:51:47 -08:00
|
|
|
|
2016-11-24 21:53:04 -08:00
|
|
|
// Split at )
|
2021-06-06 16:13:58 -07:00
|
|
|
std::string_view matchString = wpi::split(devNameRef, ')').first;
|
|
|
|
|
if (wpi::equals(matchString, devNameRef))
|
2020-12-28 12:58:06 -08:00
|
|
|
continue;
|
2016-11-22 21:51:47 -08:00
|
|
|
|
2017-11-03 12:30:56 -07:00
|
|
|
// Search directories to get a list of system accessors
|
2018-12-19 14:40:01 -08:00
|
|
|
// The directories we need are not symbolic, so we can safely
|
|
|
|
|
// disable symbolic links.
|
2017-11-03 12:30:56 -07:00
|
|
|
std::error_code ec;
|
2021-06-01 21:50:35 -07:00
|
|
|
for (auto& p : fs::recursive_directory_iterator("/sys/devices/soc0", ec)) {
|
2020-12-28 12:58:06 -08:00
|
|
|
if (ec)
|
|
|
|
|
break;
|
2021-06-06 16:13:58 -07:00
|
|
|
std::string path = p.path();
|
2021-06-01 21:50:35 -07:00
|
|
|
if (path.find("amba") == std::string::npos)
|
2020-12-28 12:58:06 -08:00
|
|
|
continue;
|
2021-06-01 21:50:35 -07:00
|
|
|
if (path.find("usb") == std::string::npos)
|
2020-12-28 12:58:06 -08:00
|
|
|
continue;
|
2021-06-01 21:50:35 -07:00
|
|
|
if (path.find(matchString) == std::string::npos)
|
2020-12-28 12:58:06 -08:00
|
|
|
continue;
|
2016-11-22 21:51:47 -08:00
|
|
|
|
2021-06-06 16:13:58 -07:00
|
|
|
wpi::SmallVector<std::string_view, 16> pathSplitVec;
|
2017-11-03 12:30:56 -07:00
|
|
|
// Split path into individual directories
|
2021-06-06 16:13:58 -07:00
|
|
|
wpi::split(path, pathSplitVec, '/', -1, false);
|
2016-11-22 21:51:47 -08:00
|
|
|
|
2016-11-24 21:53:04 -08:00
|
|
|
// Find each individual item index
|
|
|
|
|
int findusb = -1;
|
|
|
|
|
int findtty = -1;
|
|
|
|
|
int findregex = -1;
|
|
|
|
|
for (size_t i = 0; i < pathSplitVec.size(); i++) {
|
2021-06-06 16:13:58 -07:00
|
|
|
if (findusb == -1 && wpi::equals(pathSplitVec[i], "usb1")) {
|
2016-11-24 21:53:04 -08:00
|
|
|
findusb = i;
|
|
|
|
|
}
|
2021-06-06 16:13:58 -07:00
|
|
|
if (findtty == -1 && wpi::equals(pathSplitVec[i], "tty")) {
|
2016-11-24 21:53:04 -08:00
|
|
|
findtty = i;
|
|
|
|
|
}
|
2021-06-06 16:13:58 -07:00
|
|
|
if (findregex == -1 && wpi::equals(pathSplitVec[i], matchString)) {
|
2016-11-24 21:53:04 -08:00
|
|
|
findregex = i;
|
2016-11-22 21:51:47 -08:00
|
|
|
}
|
2016-11-24 21:53:04 -08:00
|
|
|
}
|
2016-11-22 21:51:47 -08:00
|
|
|
|
2016-11-24 21:53:04 -08:00
|
|
|
// Get the index for our device
|
|
|
|
|
int hubIndex = findtty;
|
2020-12-28 12:58:06 -08:00
|
|
|
if (findtty == -1)
|
|
|
|
|
hubIndex = findregex;
|
2016-11-22 21:51:47 -08:00
|
|
|
|
2016-11-24 21:53:04 -08:00
|
|
|
int devStart = findusb + 1;
|
2016-11-22 21:51:47 -08:00
|
|
|
|
2020-12-28 12:58:06 -08:00
|
|
|
if (hubIndex < devStart)
|
|
|
|
|
continue;
|
2016-11-22 21:51:47 -08:00
|
|
|
|
2016-11-24 21:53:04 -08:00
|
|
|
// Add our devices to our list
|
|
|
|
|
m_unsortedHubPath.emplace_back(
|
2021-06-06 16:13:58 -07:00
|
|
|
std::string_view{pathSplitVec[hubIndex - 2]});
|
2016-11-24 21:53:04 -08:00
|
|
|
m_visaResource.emplace_back(desc);
|
2016-12-20 22:16:05 -08:00
|
|
|
m_osResource.emplace_back(
|
2021-06-06 16:13:58 -07:00
|
|
|
wpi::split(wpi::split(osName, "(").second, ")").first);
|
2017-11-03 12:30:56 -07:00
|
|
|
break;
|
2016-11-22 21:51:47 -08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SortHubPathVector();
|
|
|
|
|
|
|
|
|
|
CoiteratedSort(m_visaResource);
|
|
|
|
|
CoiteratedSort(m_osResource);
|
2016-11-25 15:52:21 -08:00
|
|
|
done:
|
|
|
|
|
viClose(viList);
|
2016-11-22 21:51:47 -08:00
|
|
|
}
|
2016-11-24 21:53:04 -08:00
|
|
|
|
|
|
|
|
int32_t SerialHelper::GetIndexForPort(HAL_SerialPort port, int32_t* status) {
|
|
|
|
|
// Hold lock whenever we're using the names array
|
2019-07-08 22:58:39 -07:00
|
|
|
std::scoped_lock lock(m_nameMutex);
|
2016-11-24 21:53:04 -08:00
|
|
|
|
|
|
|
|
std::string portString = m_usbNames[port - 2];
|
|
|
|
|
|
2018-04-29 23:33:19 -07:00
|
|
|
wpi::SmallVector<int32_t, 4> indices;
|
2016-11-24 21:53:04 -08:00
|
|
|
|
|
|
|
|
// If port has not been assigned, find the one to assign
|
|
|
|
|
if (portString.empty()) {
|
|
|
|
|
for (size_t i = 0; i < 2; i++) {
|
|
|
|
|
// Remove all used ports
|
2021-06-06 16:13:58 -07:00
|
|
|
auto idx = std::find_if(
|
|
|
|
|
m_sortedHubPath.begin(), m_sortedHubPath.end(),
|
|
|
|
|
[&](const auto& s) { return wpi::equals(s, m_usbNames[i]); });
|
2016-11-24 21:53:04 -08:00
|
|
|
if (idx != m_sortedHubPath.end()) {
|
|
|
|
|
// found
|
|
|
|
|
m_sortedHubPath.erase(idx);
|
|
|
|
|
}
|
|
|
|
|
if (m_usbNames[i] == "") {
|
|
|
|
|
indices.push_back(i);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int32_t idx = -1;
|
|
|
|
|
for (size_t i = 0; i < indices.size(); i++) {
|
|
|
|
|
if (indices[i] == port - 2) {
|
|
|
|
|
idx = i;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (idx == -1) {
|
|
|
|
|
*status = HAL_SERIAL_PORT_NOT_FOUND;
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (idx >= static_cast<int32_t>(m_sortedHubPath.size())) {
|
|
|
|
|
*status = HAL_SERIAL_PORT_NOT_FOUND;
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
portString = m_sortedHubPath[idx].str();
|
|
|
|
|
m_usbNames[port - 2] = portString;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int retIndex = -1;
|
|
|
|
|
|
|
|
|
|
for (size_t i = 0; i < m_sortedHubPath.size(); i++) {
|
2021-06-06 16:13:58 -07:00
|
|
|
if (wpi::equals(m_sortedHubPath[i], portString)) {
|
2016-11-24 21:53:04 -08:00
|
|
|
retIndex = i;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return retIndex;
|
|
|
|
|
}
|
|
|
|
|
|
2016-11-22 21:51:47 -08:00
|
|
|
} // namespace hal
|