11from typing import List
22import numpy
3+ import p4p .nt
34import pytest
45
56from enum import Enum
@@ -137,38 +138,10 @@ def record_values_names(fixture_value):
137138 numpy .array ([1.5 , 2.6 , 3.7 ], dtype = numpy .float64 ),
138139 numpy .ndarray ,
139140 ),
140- (
141- "wIn_int" ,
142- builder .WaveformIn ,
143- 567 ,
144- numpy .array ([567. ], dtype = numpy .float64 ),
145- numpy .ndarray ,
146- ),
147- (
148- "wOut_int" ,
149- builder .WaveformOut ,
150- 567 ,
151- numpy .array ([567. ], dtype = numpy .float64 ),
152- numpy .ndarray ,
153- ),
154- (
155- "wIn_float" ,
156- builder .WaveformIn ,
157- 12.345 ,
158- numpy .array ([12.345 ], dtype = numpy .float64 ),
159- numpy .ndarray ,
160- ),
161- (
162- "wOut_float" ,
163- builder .WaveformOut ,
164- 12.345 ,
165- numpy .array ([12.345 ], dtype = numpy .float64 ),
166- numpy .ndarray ,
167- ),
168141 (
169142 "wIn_bytes" ,
170143 builder .WaveformIn ,
171- b"HELLO \0 WORLD" ,
144+ [ 72 , 69 , 76 , 76 , 79 , 0 , 87 , 79 , 82 , 76 , 68 ] ,
172145 numpy .array (
173146 [72 , 69 , 76 , 76 , 79 , 0 , 87 , 79 , 82 , 76 , 68 ], dtype = numpy .uint8
174147 ),
@@ -177,7 +150,7 @@ def record_values_names(fixture_value):
177150 (
178151 "wOut_bytes" ,
179152 builder .WaveformOut ,
180- b"HELLO \0 WORLD" ,
153+ [ 72 , 69 , 76 , 76 , 79 , 0 , 87 , 79 , 82 , 76 , 68 ] ,
181154 numpy .array (
182155 [72 , 69 , 76 , 76 , 79 , 0 , 87 , 79 , 82 , 76 , 68 ], dtype = numpy .uint8
183156 ),
@@ -317,15 +290,25 @@ def record_value_asserts(
317290):
318291 """Asserts that the expected value and expected type are matched with
319292 the actual value. Handles both scalar and waveform data"""
293+
294+ # This function is shared between functions that may pass in either a native Python
295+ # type, or the value returned from p4p, which must be unwrapped
296+ if type (actual_value ) == p4p .nt .enum .ntenum :
297+ actual_val_type = type (actual_value .raw ["value" ].get ("index" ))
298+ elif isinstance (actual_value , p4p .nt .scalar .ntwrappercommon ):
299+ actual_val_type = type (actual_value .raw ["value" ])
300+ else :
301+ actual_val_type = type (actual_value )
302+
320303 try :
321304 if type (expected_value ) == float and isnan (expected_value ):
322305 assert isnan (actual_value ) # NaN != Nan, so needs special case
323306 elif creation_func in [builder .WaveformOut , builder .WaveformIn ]:
324307 assert numpy .array_equal (actual_value , expected_value )
325- assert type ( actual_value ) == expected_type
308+ assert actual_val_type == expected_type
326309 else :
327310 assert actual_value == expected_value
328- assert type ( actual_value ) == expected_type
311+ assert actual_val_type == expected_type
329312 except AssertionError as e :
330313 msg = (
331314 "Failed with parameters: "
@@ -479,18 +462,10 @@ def is_valid(configuration):
479462 # Wait for message that IOC has started
480463 select_and_recv (parent_conn , "R" )
481464
482- # Cannot do these imports before the subprocess starts, as the subprocess
483- # would inherit cothread's internal state which would break things!
484- from cothread import Yield
485- from cothread .catools import caget , caput , _channel_cache
486- from cothread .dbr import DBR_CHAR_STR
465+ from p4p .client .thread import Context
466+ ctx = Context ('pva' )
487467
488468 try :
489- # cothread remembers connected IOCs. As we potentially restart the same
490- # named IOC multiple times, we have to purge the cache else the
491- # result from caget/caput cache would be a DisconnectError during the
492- # second test
493- _channel_cache .purge ()
494469
495470 for configuration in record_configurations :
496471 (
@@ -501,52 +476,24 @@ def is_valid(configuration):
501476 expected_type ,
502477 ) = configuration
503478
504- # Infer some required keywords from parameters
505- kwargs = {}
506- put_kwarg = {}
507- if creation_func in [builder .longStringIn , builder .longStringOut ]:
508- kwargs ["datatype" ] = DBR_CHAR_STR
509-
510- if (creation_func in [builder .WaveformIn , builder .WaveformOut ]
511- and type (initial_value ) is bytes ):
512- # There's a bug in caput that means DBR_CHAR_STR doesn't
513- # truncate the array of the target record, meaning .get()
514- # returns all NELM rather than just NORD elements. Instead we
515- # encode the data ourselves
516- initial_value = numpy .frombuffer (
517- initial_value , dtype = numpy .uint8 )
518-
519479 if set_enum == SetValueEnum .CAPUT :
520480 if get_enum == GetValueEnum .GET :
521481 select_and_recv (parent_conn )
522- caput (
523- DEVICE_NAME + ":" + record_name ,
524- initial_value ,
525- wait = True ,
526- ** kwargs ,
527- ** put_kwarg ,
528- )
482+ ctx .put (DEVICE_NAME + ":" + record_name ,
483+ initial_value ,
484+ None ,
485+ timeout = TIMEOUT ,
486+ wait = True ,
487+ )
529488
530489 if get_enum == GetValueEnum .GET :
531490 parent_conn .send ("G" ) # "Get"
532491
533- # Ensure IOC process has time to execute.
534- # I saw failures on MacOS where it appeared the IOC had not
535- # processed the put'ted value as the caget returned the same
536- # value as was originally passed in.
537- Yield (timeout = TIMEOUT )
538-
539492 if get_enum == GetValueEnum .GET :
540493 rec_val = select_and_recv (parent_conn )
541494 else :
542- rec_val = caget (
543- DEVICE_NAME + ":" + record_name ,
544- timeout = TIMEOUT ,
545- ** kwargs ,
546- )
547- # '+' operator used to convert cothread's types into Python
548- # native types e.g. "+ca_int" -> int
549- rec_val = + rec_val
495+ rec_val = ctx .get (DEVICE_NAME + ":" + record_name ,
496+ timeout = TIMEOUT ,)
550497
551498 if (
552499 creation_func in [builder .WaveformOut , builder .WaveformIn ]
@@ -569,9 +516,6 @@ def is_valid(configuration):
569516 creation_func , rec_val , expected_value , expected_type
570517 )
571518 finally :
572- # Purge cache to suppress spurious "IOC disconnected" exceptions
573- _channel_cache .purge ()
574-
575519 parent_conn .send ("D" ) # "Done"
576520
577521 ioc_process .join (timeout = TIMEOUT )
0 commit comments