1/// Example illustrating the use of the LabOne C API and the Impedance Module to
2/// perform a short-open user compensation with an MFIA device.
3#include <chrono>
4#include <iostream>
5#include <string>
6#include <thread>
7
8#include "ziAPI.h"
9#include "ziUtils.hpp"
10
11/// Translate C API result codes into C++ exceptions
12void handleError(ZIConnection conn, ZIResult_enum resultCode)
13{
14 if (resultCode == ZI_INFO_SUCCESS)
15 {
16 return;
17 }
18 char* errorDescription;
19 ziAPIGetError(resultCode, &errorDescription, nullptr);
20 constexpr size_t maxErrorLength = 1000;
21 char errorDetails[maxErrorLength];
22 errorDetails[0] = 0;
23 ziAPIGetLastError(conn, errorDetails, maxErrorLength);
24 auto message = std::string{"LabOne C API error "} +
25 std::to_string(resultCode) + ": " + errorDescription +
26 "\nDetails: " + errorDetails;
27 throw std::runtime_error(message);
28}
29
30int main()
31{
32 const char* serverHost = ziUtilsGetEnv("LABONE_SERVER", "localhost");
33 const char* deviceAddress = ziUtilsGetEnv("LABONE_DEVICE", "dev3123");
34 const char* deviceInterface = ziUtilsGetEnv("LABONE_INTERFACE", "1GbE");
35
36 // Instrument connection
37 std::cout << "-- Connecting to instrument ..." << std::endl;
38 ZIConnection conn = nullptr;
39 handleError(conn, ziAPIInit(&conn));
40
41 handleError(
42 conn, ziAPIConnectEx(conn, serverHost, 8004, ZI_API_VERSION_6, nullptr));
43 handleError(
44 conn, ziAPIConnectDevice(conn, deviceAddress, deviceInterface, nullptr));
45
46 ziApiServerVersionCheck(conn);
47
48 const auto pathBase = std::string{"/"} + deviceAddress + "/";
49
50 // Instrument settings
51 std::cout << "-- Setting up instrument ..." << std::endl;
52 {
53 // clang-format off
54 const auto impIndex = 0;
55 const auto pathBaseImp = pathBase + "imps/" + std::to_string(impIndex) + "/";
56 handleError(conn, ziAPISetValueI(conn, (pathBaseImp + "enable").c_str(), 1));
57 handleError(conn, ziAPISetValueI(conn, (pathBaseImp + "mode").c_str(), 0));
58 handleError(conn, ziAPISetValueI(conn, (pathBaseImp + "auto/output").c_str(), 1));
59 handleError(conn, ziAPISetValueI(conn, (pathBaseImp + "auto/bw").c_str(), 1));
60 handleError(conn, ziAPISetValueD(conn, (pathBaseImp + "freq").c_str(), 1000.0));
61 handleError(conn, ziAPISetValueI(conn, (pathBaseImp + "auto/inputrange").c_str(), 1));
62 handleError(conn, ziAPISetValueD(conn, (pathBaseImp + "output/amplitude").c_str(), 0.3));
63 handleError(conn, ziAPISetValueD(conn, (pathBaseImp + "output/range").c_str(), 1.0));
64 handleError(conn, ziAPISetValueI(conn, (pathBaseImp + "model").c_str(), 0));
65 handleError(conn, ziAPISync(conn));
66 // clang-format on
67 }
68
69 // User compensation setup
70 std::cout << "-- Setting up module for user compensation ..." << std::endl;
71 ZIModuleHandle impedance;
72 handleError(conn, ziAPIModCreate(conn, &impedance, "impedanceModule"));
73 handleError(conn, ziAPIModExecute(conn, impedance));
74 {
75 // clang-format off
76 handleError(conn, ziAPIModSetString(conn, impedance, "device", deviceAddress));
77 handleError(conn, ziAPIModSetDoubleData(conn, impedance, "freq/start", 1000.0));
78 handleError(conn, ziAPIModSetDoubleData(conn, impedance, "freq/stop", 5000000.0));
79 handleError(conn, ziAPIModSetIntegerData(conn, impedance, "freq/samplecount", 20));
80
81 // Important: this allows everything to pass, in case the short is much higher than 0 Ohm.
82 handleError(conn, ziAPIModSetIntegerData(conn, impedance, "validation", 0));
83
84 // Short-only mode.
85 handleError(conn, ziAPIModSetIntegerData(conn, impedance, "mode", 1));
86
87 // Add additional open, note this is the case for 21.08 onwards. otherwise the old mode 3 is short-open.
88 handleError(conn, ziAPIModSetIntegerData(conn, impedance, "openstep", 1));
89 // clang-format on
90 }
91
92 // Short compensation
93 {
94 std::cout << "-- Short compensation ..." << std::endl;
95 handleError(conn, ziAPIModSetIntegerData(conn, impedance, "step", 0));
96 handleError(conn, ziAPIModSetIntegerData(conn, impedance, "calibrate", 1));
97
98 for (;;)
99 {
100 std::this_thread::sleep_for(std::chrono::milliseconds{200});
101
102 double progress;
103 handleError(conn, ziAPIModProgress(conn, impedance, &progress));
104 std::cout << "Short compensation progress: " << 100 * progress
105 << " % \r" << std::flush;
106
107 ZIIntegerData finished;
108 handleError(conn, ziAPIModFinished(conn, impedance, &finished));
109 if (finished)
110 {
111 break;
112 }
113
114 // Wait until first calibration step succeeded. The calibration step is
115 // bit-encoded in the 'status' node.
116 ZIIntegerData status;
117 handleError(conn, ziAPIModGetInteger(conn, impedance, "status", &status));
118 if ((status & 0b01) != 0)
119 {
120 break;
121 }
122 }
123
124 std::cout << std::endl;
125 char message[1000];
126 unsigned int messageLength = 0;
127 handleError(
128 conn,
129 ziAPIModGetString(
130 conn,
131 impedance,
132 "message",
133 message,
134 &messageLength,
135 sizeof(message)));
136 std::cout << "Message: " << message << std::endl << std::endl;
137 }
138
139 std::cout << std::endl
140 << "Please change from short into open now. Then press [Enter] to "
141 "continue."
142 << std::endl;
143 std::cin.get();
144
145 // Open compensation
146 {
147 std::cout << "-- Open compensation ..." << std::endl;
148 handleError(conn, ziAPIModSetIntegerData(conn, impedance, "step", 1));
149 handleError(conn, ziAPIModSetIntegerData(conn, impedance, "calibrate", 1));
150
151 for (;;)
152 {
153 std::this_thread::sleep_for(std::chrono::milliseconds{200});
154
155 double progress;
156 handleError(conn, ziAPIModProgress(conn, impedance, &progress));
157 std::cout << "Open compensation progress: " << 100 * progress
158 << " % \r" << std::flush;
159
160 ZIIntegerData finished;
161 handleError(conn, ziAPIModFinished(conn, impedance, &finished));
162 if (finished)
163 {
164 break;
165 }
166
167 // Wait until second calibration step succeeded.
168 ZIIntegerData status;
169 handleError(conn, ziAPIModGetInteger(conn, impedance, "status", &status));
170 if ((status & 0b10) != 0)
171 {
172 break;
173 }
174 }
175
176 std::cout << std::endl;
177 char message[1000];
178 unsigned int messageLength = 0;
179 handleError(
180 conn,
181 ziAPIModGetString(
182 conn,
183 impedance,
184 "message",
185 message,
186 &messageLength,
187 sizeof(message)));
188 std::cout << "Message: " << message << std::endl << std::endl;
189 }
190
191 // Save and load compensation file
192 {
193 // clang-format off
194 std::cout << "-- Save and load compensation file" << std::endl;
195 handleError(conn, ziAPIModSetIntegerData(conn, impedance, "todevice", 1));
196 handleError(conn, ziAPISetValueI(conn, (pathBase + "system/impedance/calib/internal/store").c_str(), 1));
197 handleError(conn, ziAPIModSetString(conn, impedance, "filename", "testcal"));
198 handleError(conn, ziAPIModSetIntegerData(conn, impedance, "save", 1));
199
200 // Wait for data to be saved ('save' node returns to 0)
201 for(;;) {
202 ZIIntegerData save;
203 handleError(conn, ziAPIModGetInteger(conn, impedance, "save", &save));
204 if (save == 0) {
205 break;
206 }
207 std::this_thread::sleep_for(std::chrono::milliseconds{100});
208 }
209
210 // Use this to apply the just finished short-open compensation
211 handleError(conn, ziAPIModSetIntegerData(conn, impedance, "load", 1));
212 // clang-format on
213 }
214
215 std::cout << "Short-Open user compensation complete." << std::endl;
216
217 // Cleanup
218 handleError(conn, ziAPIModClear(conn, impedance));
219 handleError(conn, ziAPIDestroy(conn));
220}