| THM1176InstrumentDriver 1.2 C++ API for Metrolab THM1176

Loading...
Searching...
No Matches
IEEE488InstrumentOpenCloseTest.h
Go to the documentation of this file.
1// Copyright (c) 2020 Metrolab Technology S.A., Geneva, Switzerland (www.metrolab.com)
2// See the included file LICENSE.txt for the licensing conditions.
3
7
8#pragma once
9
10#include <gtest/gtest.h>
11#include <regex>
12#include <future>
13
14#include "IEEE488InstrumentTest.h"
15#include "Exception.h"
16#include "Helpers.h"
17
18 using namespace testing;
19
20 class IEEE488InstrumentOpenCloseLockTest : public ::testing::Test
21{
22 protected:
23 static IEEE4888_TEST_RESOURCE_MANAGER_CLASS * pResourceManager;
24
25 static void SetUpTestCase()
26 {
27 pResourceManager = new IEEE4888_TEST_RESOURCE_MANAGER_CLASS;
28 ASSERT_NE(nullptr, pResourceManager);
29 ASSERT_EQ(true, pResourceManager->Initialize());
30 }
31
32 static void TearDownTestCase()
33 {
34 delete pResourceManager;
35 pResourceManager = nullptr;
36 }
37};
38
39 static const U32 IEEE488_TEST_DURATION = 5; // s
40IEEE4888_TEST_RESOURCE_MANAGER_CLASS * IEEE488InstrumentOpenCloseLockTest::pResourceManager = nullptr;
41
44 static void l_OpenLoopQueryClose(IEEE4888_TEST_RESOURCE_MANAGER_CLASS * pResourceManager,
45 std::string InstrumentName,
46 U32 NSeconds,
47 std::promise<bool> & rSucceeded,
48 std::promise<U32> & rNLoopsPerformed)
49{
50 bool l_Status = true;
51 U32 l_LoopCount = 0;
52 IEEE4888_TEST_INSTRUMENT_CLASS * l_pInstrument = nullptr;
53 try
54 {
55 // Make sure we got a non-null Resource Manager
56 if (nullptr == pResourceManager)
57 throw MTL::CException<IEEE4888_TEST_INSTRUMENT_CLASS>("No Resource Manager", MTL__LOCATION__);
58
59 // Create an Instrument object and open it.
60 l_pInstrument = new IEEE4888_TEST_INSTRUMENT_CLASS(*pResourceManager, InstrumentName);
61 if (nullptr == l_pInstrument ||
62 !l_pInstrument->Open() ||
63 !l_pInstrument->SetTimeout(1000*NSeconds))
64 throw MTL::CException<IEEE4888_TEST_INSTRUMENT_CLASS>("Couldn't open instrument", MTL__LOCATION__);
65
66 // Perform queries for the requested time period.
67 auto l_EndTime = std::chrono::steady_clock::now() + std::chrono::seconds(NSeconds);
68 do
69 {
70// std::cout << "a" << std::flush;
71 if (!l_pInstrument->LockExclusive(1000*NSeconds))
72 throw MTL::CException<IEEE4888_TEST_INSTRUMENT_CLASS>("Couldn't lock", MTL__LOCATION__);
73// std::cout << "b" << std::flush;
74
75 if (!l_pInstrument->Write ("*IDN?"))
76 throw MTL::CException<IEEE4888_TEST_INSTRUMENT_CLASS>("Couldn't write", MTL__LOCATION__);
77
78 CSCPIBuffer l_Buffer;
79 if (!l_pInstrument->Read (l_Buffer))
80 throw MTL::CException<IEEE4888_TEST_INSTRUMENT_CLASS>("Couldn't read", MTL__LOCATION__);
81
82 if (!CheckIDNResponse (l_Buffer))
83 throw MTL::CException<IEEE4888_TEST_INSTRUMENT_CLASS>("Invalid response", MTL__LOCATION__);
84
85// std::cout << "c" << std::flush;
86 if (!l_pInstrument->Unlock())
87 throw MTL::CException<IEEE4888_TEST_INSTRUMENT_CLASS>("Couldn't unlock", MTL__LOCATION__);
88
89 // Sleep at the end of each cycle to force the scheduler to allow the other thread to run.
90 // Windows tends to grant long runs to a single thread, causing the test to fail artificially.
91 ++l_LoopCount;
92 std::this_thread::sleep_for(std::chrono::milliseconds(10));
93 } while (std::chrono::steady_clock::now() < l_EndTime);
94
95 }
96 catch (MTL::CException<IEEE4888_TEST_INSTRUMENT_CLASS> & rE)
97 {
98 MTL_Unused(rE);
99 CERR(rE.what());
100 CERR(std::string("Instrument status: ") + l_pInstrument->StatusDescription(l_pInstrument->Status()));
101 l_Status = false;
102 }
103
104 // Close and delete the instrument.
105 if (nullptr != l_pInstrument)
106 {
107 l_pInstrument->Close();
108 delete l_pInstrument;
109 }
110
111 // Return the promised parameters.
112 rSucceeded.set_value(l_Status);
113 rNLoopsPerformed.set_value(l_LoopCount);
114}
115
118 static void l_OpenLockWaitClose(IEEE4888_TEST_RESOURCE_MANAGER_CLASS * pResourceManager,
119 std::string InstrumentName,
120 U32 NSeconds,
121 std::promise<bool> & rSucceeded)
122{
123 bool l_Status = true;
124 IEEE4888_TEST_INSTRUMENT_CLASS * l_pInstrument = nullptr;
125 try
126 {
127 // Make sure we got a non-null Resource Manager
128 if (nullptr == pResourceManager)
129 throw MTL::CException<IEEE4888_TEST_INSTRUMENT_CLASS>("No Resource Manager", MTL__LOCATION__);
130
131 // Create an Instrument object, open it, and lock it.
132 l_pInstrument = new IEEE4888_TEST_INSTRUMENT_CLASS(*pResourceManager, InstrumentName);
133 if (nullptr == l_pInstrument ||
134 !l_pInstrument->Open() ||
135 !l_pInstrument->LockExclusive(1000))
136 throw MTL::CException<IEEE4888_TEST_INSTRUMENT_CLASS>("Couldn't open instrument", MTL__LOCATION__);
137
138 // Keep it locked for the specified amount of time.
139 std::this_thread::sleep_for(std::chrono::seconds(NSeconds));
140 }
141 catch (MTL::CException<IEEE4888_TEST_INSTRUMENT_CLASS> & rE)
142 {
143 MTL_Unused(rE);
144 CERR(rE.what());
145 l_Status = false;
146 }
147
148 // Close and delete the instrument.
149 if (nullptr != l_pInstrument)
150 {
151 l_pInstrument->Close();
152 delete l_pInstrument;
153 }
154
155 // Return the promised status flag.
156 rSucceeded.set_value(l_Status);
157}
158
160 TEST_F(IEEE488InstrumentOpenCloseLockTest, OpenClose)
161{
162 // Find all instruments.
163 CResourceList l_InstrumentList;
164 ASSERT_EQ(true, pResourceManager->FindResources(l_InstrumentList, IEEE4888_TEST_RESOURCE_FILTER));
165 ASSERT_EQ(false, l_InstrumentList.empty());
166
167 // Create the Instrument. Run the tests on the first one in the list.
168 std::string l_InstrumentName = l_InstrumentList.front();
169 IEEE4888_TEST_INSTRUMENT_CLASS * l_pInstrument = new IEEE4888_TEST_INSTRUMENT_CLASS(*pResourceManager, l_InstrumentName);
170 ASSERT_NE(nullptr, l_pInstrument);
171 ASSERT_EQ(false, l_pInstrument->IsOpen());
172
173 // Open the instrument.
174 ASSERT_EQ(true, l_pInstrument->Open());
175 ASSERT_EQ(true, l_pInstrument->IsOpen());
176
177 // Check the status.
178 I32 l_Status = l_pInstrument->Status();
179 ASSERT_EQ(0, l_Status);
180 std::string l_StatusDescription = l_pInstrument->StatusDescription(l_Status);
181 std::regex l_Regex(".*Success.*", std::regex::icase);
182 std::smatch l_Match;
183 EXPECT_EQ(true, std::regex_match(l_StatusDescription, l_Match, l_Regex));
184
185 // Close the instrument.
186 l_pInstrument->Close();
187 ASSERT_EQ(false, l_pInstrument->IsOpen());
188
189 // Destroy the object.
190 delete l_pInstrument;
191
192}
193
195 TEST_F(IEEE488InstrumentOpenCloseLockTest, OpenCloseTwice)
196{
197 // Find all instruments.
198 CResourceList l_InstrumentList;
199 ASSERT_EQ(true, pResourceManager->FindResources(l_InstrumentList, IEEE4888_TEST_RESOURCE_FILTER));
200 ASSERT_EQ(false, l_InstrumentList.empty());
201
202 // Create the Instrument. Run the tests on the first one in the list.
203 std::string l_InstrumentName = l_InstrumentList.front();
204 IEEE4888_TEST_INSTRUMENT_CLASS * l_pInstrument = new IEEE4888_TEST_INSTRUMENT_CLASS(*pResourceManager, l_InstrumentName);
205 ASSERT_NE(nullptr, l_pInstrument);
206 ASSERT_EQ(false, l_pInstrument->IsOpen());
207
208 // Open the instrument.
209 ASSERT_EQ(true, l_pInstrument->Open());
210 ASSERT_EQ(true, l_pInstrument->IsOpen());
211
212 // Check the status.
213 I32 l_Status = l_pInstrument->Status();
214 ASSERT_EQ(0, l_Status);
215 std::string l_StatusDescription = l_pInstrument->StatusDescription(l_Status);
216 std::regex l_Regex(".*Success.*", std::regex::icase);
217 std::smatch l_Match;
218 EXPECT_EQ(true, std::regex_match(l_StatusDescription, l_Match, l_Regex));
219
220 // Close the instrument.
221 l_pInstrument->Close();
222 ASSERT_EQ(false, l_pInstrument->IsOpen());
223
224 // Open the instrument.
225 ASSERT_EQ(true, l_pInstrument->Open());
226 ASSERT_EQ(true, l_pInstrument->IsOpen());
227
228 // Check the status.
229 l_Status = l_pInstrument->Status();
230 ASSERT_EQ(0, l_Status);
231 l_StatusDescription = l_pInstrument->StatusDescription(l_Status);
232 l_Regex = std::regex(".*Success.*", std::regex::icase);
233 EXPECT_EQ(true, std::regex_match(l_StatusDescription, l_Match, l_Regex));
234
235 // Try to open it again - should just sail through.
236 ASSERT_EQ(true, l_pInstrument->Open());
237
238 // Close the instrument.
239 l_pInstrument->Close();
240 ASSERT_EQ(false, l_pInstrument->IsOpen());
241
242 // Try to close it again - should just sail through.
243 l_pInstrument->Close();
244 ASSERT_EQ(0, l_pInstrument->Status());
245
246 // Destroy the object.
247 delete l_pInstrument;
248
249}
250
252 TEST_F(IEEE488InstrumentOpenCloseLockTest, ShareInstrument)
253{
254 // Find the instrument.
255 CResourceList l_InstrumentList;
256 ASSERT_EQ(true, pResourceManager->FindResources(l_InstrumentList, IEEE4888_TEST_RESOURCE_FILTER));
257 ASSERT_EQ(false, l_InstrumentList.empty());
258
259 // Create the Instrument. Run the tests on the first one in the list.
260 std::string l_InstrumentName = l_InstrumentList.front();
261 IEEE4888_TEST_INSTRUMENT_CLASS * l_pInstrument = new IEEE4888_TEST_INSTRUMENT_CLASS(*pResourceManager, l_InstrumentName);
262 ASSERT_NE(nullptr, l_pInstrument);
263
264 // Launch a thread to perform queries during the specified time period.
265 std::promise<bool> l_PromisedStatus;
266 std::future<bool> l_FutureStatus = l_PromisedStatus.get_future();
267 std::promise<U32> l_PromisedLoopCount;
268 std::future<U32> l_FutureLoopcount = l_PromisedLoopCount.get_future();
269 std::thread l_Thread(l_OpenLoopQueryClose, pResourceManager, l_InstrumentName, IEEE488_TEST_DURATION, std::ref(l_PromisedStatus), std::ref(l_PromisedLoopCount));
270
271 // Open the instrument.
272 EXPECT_EQ(true, l_pInstrument->Open());
273 EXPECT_EQ(true, l_pInstrument->SetTimeout(1000*IEEE488_TEST_DURATION));
274
275 // Perform queries for the requested time period.
276 U32 l_LoopCount = 0;
277 auto l_EndTime = std::chrono::steady_clock::now() + std::chrono::seconds(IEEE488_TEST_DURATION);
278 do
279 {
280// std::cout << "A" << std::flush;
281 ASSERT_EQ(true, l_pInstrument->LockExclusive(1000*IEEE488_TEST_DURATION));
282// std::cout << "B" << std::flush;
283
284 // Note: With NI-VISA and Qt, there seems to be a timing issue; hence the sleep_for() calls.
285 // From USB protocol analysis, it looks like it's related to periodic calls to assert REN (Remote ENable);
286 // the following Write/Read cycle tends to fail. The mechanism is not at all clear.
287 ASSERT_EQ(true, l_pInstrument->Write ("*IDN?"));
288 std::this_thread::sleep_for(std::chrono::milliseconds(10));
289
290 CSCPIBuffer l_Buffer;
291 ASSERT_EQ(true, l_pInstrument->Read (l_Buffer));
292 std::this_thread::sleep_for(std::chrono::milliseconds(10));
293
294 EXPECT_EQ(true, CheckIDNResponse (l_Buffer));
295
296// std::cout << "C" << std::flush;
297 ASSERT_EQ(true, l_pInstrument->Unlock());
298
299 // Sleep at the end of each cycle to force the scheduler to allow the other thread to run.
300 // Windows tends to grant long runs to a single thread, causing the test to fail artificially.
301 ++l_LoopCount;
302 std::this_thread::sleep_for(std::chrono::milliseconds(10));
303 } while (std::chrono::steady_clock::now() < l_EndTime);
304
305 // Wait for the child thread to end and check whether it returned success,
306 // and whether the loop counts were both positive.
307 EXPECT_GT(l_LoopCount, 10u);
308 EXPECT_EQ(true, l_FutureStatus.get());
309 EXPECT_GT(l_FutureLoopcount.get(), 10u);
310 l_Thread.join();
311
312 // Close and delete the instrument.
313 l_pInstrument->Close();
314 delete l_pInstrument;
315}
316
318 TEST_F(IEEE488InstrumentOpenCloseLockTest, LockWhenAlreadyLocked)
319{
320 // Find the instrument.
321 CResourceList l_InstrumentList;
322 ASSERT_EQ(true, pResourceManager->FindResources(l_InstrumentList, IEEE4888_TEST_RESOURCE_FILTER));
323 ASSERT_EQ(false, l_InstrumentList.empty());
324
325 // Create the Instrument. Run the tests on the first one in the list.
326 std::string l_InstrumentName = l_InstrumentList.front();
327 IEEE4888_TEST_INSTRUMENT_CLASS * l_pInstrument = new IEEE4888_TEST_INSTRUMENT_CLASS(*pResourceManager, l_InstrumentName);
328
329 // Launch a thread to keep the instrument locked for the specified test duration, giving it a second to start.
330 std::promise<bool> l_PromisedStatus;
331 std::future<bool> l_FutureStatus = l_PromisedStatus.get_future();
332 std::thread l_Thread(l_OpenLockWaitClose, pResourceManager, l_InstrumentName, IEEE488_TEST_DURATION, std::ref(l_PromisedStatus));
333 std::this_thread::sleep_for(std::chrono::seconds(1));
334
335 // Open the instrument and obtain a lock.
336 // Should fail because we time out before the other thread closes the instrument.
337 ASSERT_EQ(true, l_pInstrument->Open());
338 auto l_StartTime = std::chrono::steady_clock::now();
339 EXPECT_EQ(false, l_pInstrument->LockExclusive(1000*(IEEE488_TEST_DURATION-2)));
340 auto l_StopTime = std::chrono::steady_clock::now();
341 std::chrono::duration<F64> l_Duration = l_StopTime - l_StartTime;
342 EXPECT_NEAR(static_cast<F64>(IEEE488_TEST_DURATION-2), l_Duration.count(), 0.1);
343
344 // Wait for the child thread to end and check whether it returned success.
345 EXPECT_EQ(true, l_FutureStatus.get());
346 l_Thread.join();
347
348 // Close and delete the instrument.
349 l_pInstrument->Close();
350 delete l_pInstrument;
351}
352
354 TEST_F(IEEE488InstrumentOpenCloseLockTest, LockAfterWait)
355{
356 // Find the instrument.
357 CResourceList l_InstrumentList;
358 ASSERT_EQ(true, pResourceManager->FindResources(l_InstrumentList, IEEE4888_TEST_RESOURCE_FILTER));
359 ASSERT_EQ(false, l_InstrumentList.empty());
360
361 // Create the Instrument. Run the tests on the first one in the list.
362 std::string l_InstrumentName = l_InstrumentList.front();
363 IEEE4888_TEST_INSTRUMENT_CLASS * l_pInstrument = new IEEE4888_TEST_INSTRUMENT_CLASS(*pResourceManager, l_InstrumentName);
364
365 // Launch a thread to keep the instrument locked for the specified test duration, giving it a second to start.
366 std::promise<bool> l_PromisedStatus;
367 std::future<bool> l_FutureStatus = l_PromisedStatus.get_future();
368 std::thread l_Thread(l_OpenLockWaitClose, pResourceManager, l_InstrumentName, IEEE488_TEST_DURATION, std::ref(l_PromisedStatus));
369 std::this_thread::sleep_for(std::chrono::seconds(1));
370
371 // Open the instrument and obtain a lock.
372 // Should succeed when the other thread closes its instrument.
373 ASSERT_EQ(true, l_pInstrument->Open());
374 auto l_StartTime = std::chrono::steady_clock::now();
375 EXPECT_EQ(true, l_pInstrument->LockExclusive(1000*(IEEE488_TEST_DURATION+1)));
376 auto l_StopTime = std::chrono::steady_clock::now();
377 std::chrono::duration<F64> l_Duration = l_StopTime - l_StartTime;
378 EXPECT_NEAR(static_cast<F64>(IEEE488_TEST_DURATION-1), l_Duration.count(), 0.1);
379
380 // Wait for the child thread to end and check whether it returned success.
381 EXPECT_EQ(true, l_FutureStatus.get());
382 l_Thread.join();
383
384 // Close and delete the instrument.
385 l_pInstrument->Close();
386 delete l_pInstrument;
387}
Exception.h
Exception handling utilities.
Helpers.h
Collection of utility macros for error messages.
CERR
#define CERR(__X__)
Definition Helpers.h:28
MTL_Unused
#define MTL_Unused(x)
Definition Helpers.h:47
MTL__LOCATION__
#define MTL__LOCATION__
Definition Helpers.h:22
IEEE488_TEST_DURATION
static const U32 IEEE488_TEST_DURATION
Definition IEEE488InstrumentOpenCloseTest.h:39
l_OpenLockWaitClose
static void l_OpenLockWaitClose(IEEE4888_TEST_RESOURCE_MANAGER_CLASS *pResourceManager, std::string InstrumentName, U32 NSeconds, std::promise< bool > &rSucceeded)
Utility function to be run in a separate thread: keep an instrument locked for a while.
Definition IEEE488InstrumentOpenCloseTest.h:118
TEST_F
TEST_F(IEEE488InstrumentOpenCloseLockTest, OpenClose)
Test Instrument object creation/destruction, Open, Close, Status, StatusDescription.
Definition IEEE488InstrumentOpenCloseTest.h:160
l_OpenLoopQueryClose
static void l_OpenLoopQueryClose(IEEE4888_TEST_RESOURCE_MANAGER_CLASS *pResourceManager, std::string InstrumentName, U32 NSeconds, std::promise< bool > &rSucceeded, std::promise< U32 > &rNLoopsPerformed)
Utility function to be run in a separate thread: open an instrument, do an *IDN?, wait a while and th...
Definition IEEE488InstrumentOpenCloseTest.h:44
CheckIDNResponse
bool CheckIDNResponse(const CSCPIBuffer &rBuffer)
Sanity-check of the response to an *IDN? query.
Definition IEEE488InstrumentTest.cpp:70
IEEE488InstrumentTest.h
Utility functions used to test IEEE488Instrument API.
I32
int I32
32-bit signed integer.
Definition OSDefines.h:27
U32
unsigned int U32
32-bit unsigned integer.
Definition OSDefines.h:31
F64
double F64
64-bit floating-point number.
Definition OSDefines.h:34
IEEE488InstrumentOpenCloseLockTest
Definition IEEE488InstrumentOpenCloseTest.h:21
IEEE488InstrumentOpenCloseLockTest::TearDownTestCase
static void TearDownTestCase()
Definition IEEE488InstrumentOpenCloseTest.h:32
IEEE488InstrumentOpenCloseLockTest::SetUpTestCase
static void SetUpTestCase()
Definition IEEE488InstrumentOpenCloseTest.h:25
IEEE488InstrumentOpenCloseLockTest::pResourceManager
static IEEE4888_TEST_RESOURCE_MANAGER_CLASS * pResourceManager
Definition IEEE488InstrumentOpenCloseTest.h:23
MTL::CException
Exception to be thrown.
Definition Exception.h:17
MTL::CException::what
virtual const char * what() const noexcept
Return string describing what happened.
Definition Exception.h:34
MTL::Instrument::CResourceList
List of VISA resource names.
Definition IEEE488InstrumentTypes.h:26
MTL::Instrument::CSCPIBuffer
Instrument Buffer.
Definition SCPIInstrumentBuffer.h:44