STAR
Cal STAR — firmware, DAQ server, and engine design
Loading...
Searching...
No Matches
LjmT7Device.cpp
Go to the documentation of this file.
1// T7Device over LabJack's LJM library (libLabJackM.so, LabJackM.h).
2//
3// Compiled only when CMake finds LJM (see CMakeLists.txt, T7_HAVE_LJM). LJM is closed source and
4// installed separately on the DAQ host: https://labjack.com/support/software/installers/ljm
5
6#include "LjmT7Device.hpp"
7
8#include <LabJackM.h>
9
10#include <iostream>
11#include <vector>
12
13namespace fsw {
14namespace t7 {
15
16namespace {
17
18std::string ljm_message(int err) {
19 char buf[LJM_MAX_NAME_SIZE] = {0};
20 LJM_ErrorToString(err, buf);
21 return std::string(buf) + " (" + std::to_string(err) + ")";
22}
23
24// LJM reserves 200-399 for warnings: the call did its job, with a caveat worth a log line.
25bool is_warning(int err) {
26 return err >= LJME_WARNINGS_BEGIN && err <= LJME_WARNINGS_END;
27}
28
29// A warning is still worth seeing: 203 LJME_USING_DEFAULT_CALIBRATION means LJM could not read
30// the unit's own calibration and every volt is on nominal constants.
31void warn(int err, const std::string& what) {
32 std::cerr << ("t7_bridge: LJM warning from " + what + ": " + ljm_message(err) + "\n");
33}
34
35void check(int err, const std::string& what) {
36 if (err == LJME_NOERROR)
37 return;
38 if (is_warning(err))
39 return warn(err, what);
40 throw T7Error(what + ": " + ljm_message(err), err);
41}
42
43// LJM library settings, once per process. LJM_AUTO_IPS (default on) caches discovered IPs in
44// /usr/local/share/LabJack/LJM/ljm_auto_ips.json for "ANY" opens; we always open by explicit IP,
45// and LabJack suggests turning it off in a sandboxed or read-only deployment ("What permissions
46// does LJM need?"). Failing to set it is logged, not fatal.
48 static const bool done = [] {
49 const int err = LJM_WriteLibraryConfigS(LJM_AUTO_IPS, 0);
50 if (err != LJME_NOERROR)
51 warn(err, "LJM_WriteLibraryConfigS(LJM_AUTO_IPS, 0)");
52 return true;
53 }();
54 (void)done;
55}
56
57} // namespace
58
62
63void LjmT7Device::open(const std::string& ip) {
64 close();
65 configure_library_once();
66 // Open by explicit IP over wired Ethernet only: "ANY" would happily open some other T7 on the
67 // network (or one on USB), and "TCP" would also accept WiFi.
68 check(LJM_OpenS("T7", "ETHERNET", ip.c_str(), &handle_), "LJM_OpenS(" + ip + ")");
69}
70
72 double v = 0;
73 check(LJM_eReadName(handle_, "SERIAL_NUMBER", &v), "read SERIAL_NUMBER");
74 return static_cast<int64_t>(v);
75}
76
77void LjmT7Device::write_names(const std::vector<std::string>& names,
78 const std::vector<double>& values) {
79 if (names.empty())
80 return;
81 std::vector<const char*> cnames;
82 for (const auto& n : names)
83 cnames.push_back(n.c_str());
84 int error_address = -1;
85 const int err = LJM_eWriteNames(handle_, static_cast<int>(cnames.size()), cnames.data(),
86 values.data(), &error_address);
87 if (is_warning(err))
88 warn(err, "LJM_eWriteNames");
89 if (err != LJME_NOERROR && !is_warning(err)) {
90 // ErrorAddress is the Modbus address of the frame that failed; name it if we can.
91 std::string which = "address " + std::to_string(error_address);
92 std::vector<int> addrs(names.size()), types(names.size());
93 if (LJM_NamesToAddresses(static_cast<int>(cnames.size()), cnames.data(), addrs.data(),
94 types.data()) == LJME_NOERROR)
95 for (size_t i = 0; i < names.size(); ++i)
96 if (addrs[i] == error_address)
97 which = names[i];
98 throw T7Error("writing " + which + ": " + ljm_message(err), err);
99 }
100}
101
102std::vector<double> LjmT7Device::read_names(const std::vector<std::string>& names) {
103 std::vector<const char*> cnames;
104 for (const auto& n : names)
105 cnames.push_back(n.c_str());
106 std::vector<double> values(names.size(), 0.0);
107 int error_address = -1;
108 check(LJM_eReadNames(handle_, static_cast<int>(cnames.size()), cnames.data(), values.data(),
109 &error_address),
110 "reading " + (names.empty() ? std::string() : names.front()) + "...");
111 return values;
112}
113
114double LjmT7Device::stream_start(const std::vector<std::string>& scan_names, int scans_per_read,
115 double scan_rate_hz) {
116 std::vector<const char*> cnames;
117 for (const auto& n : scan_names)
118 cnames.push_back(n.c_str());
119 std::vector<int> addrs(scan_names.size()), types(scan_names.size());
120 check(LJM_NamesToAddresses(static_cast<int>(cnames.size()), cnames.data(), addrs.data(),
121 types.data()),
122 "LJM_NamesToAddresses");
123 double rate = scan_rate_hz;
124 check(LJM_eStreamStart(handle_, scans_per_read, static_cast<int>(addrs.size()), addrs.data(),
125 &rate),
126 "LJM_eStreamStart");
127 num_addresses_ = static_cast<int>(addrs.size());
128 scans_per_read_ = scans_per_read;
129 return rate;
130}
131
132T7Device::Backlog LjmT7Device::stream_read(std::vector<double>& data) {
133 data.resize(static_cast<size_t>(scans_per_read_) * num_addresses_);
134 Backlog b;
135 check(LJM_eStreamRead(handle_, data.data(), &b.device_scans, &b.ljm_scans), "LJM_eStreamRead");
136 return b;
137}
138
140 check(LJM_eStreamStop(handle_), "LJM_eStreamStop");
141}
142
144 if (handle_ >= 0) {
145 LJM_Close(handle_);
146 handle_ = -1;
147 }
148}
149
150} // namespace t7
151} // namespace fsw
int64_t serial_number() override
void close() override
double stream_start(const std::vector< std::string > &scan_names, int scans_per_read, double scan_rate_hz) override
Starts the stream; returns the scan rate the device will actually run at.
void open(const std::string &ip) override
void write_names(const std::vector< std::string > &names, const std::vector< double > &values) override
void stream_stop() override
std::vector< double > read_names(const std::vector< std::string > &names) override
Command-response reads; only valid while no stream is running.
Backlog stream_read(std::vector< double > &data) override
Fills data with scans_per_read scans, channels interleaved.
A device failure.
Definition T7Stream.hpp:62
*brief create JSON pointer *sa reference_tokens const string_t const string_t & b
Definition json.hpp:13878
void warn(int err, const std::string &what)
Motorized ball valve position: raw ADC counts -> % open.
*brief user defined string literal for JSON values *sa std::size_t n
Definition json.hpp:24474
static void check(bool ok, const std::string &what)