Skip to content

Commit b8f4775

Browse files
Extend property test 4 pipeline
See merge request Karabo/Framework!3424
2 parents 565cf22 + e4e0a48 commit b8f4775

3 files changed

Lines changed: 416 additions & 68 deletions

File tree

auto_build_all.sh

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -133,17 +133,33 @@ runPythonIntegrationTests() {
133133
# safeRunCommand and adding option --force.
134134
safeRunCommand $scriptDir/run_python_tests.sh \
135135
--runIntegrationTests \
136-
--runLongTests \
137136
--collectCoverage \
138137
--rootDir $scriptDir
139138
else
140139
safeRunCommand $scriptDir/run_python_tests.sh \
141140
--runIntegrationTests \
142-
--runLongTests \
143141
--rootDir $scriptDir
144142
fi
145143
}
146144

145+
runLongTests() {
146+
if [ -z "$KARABO" ]; then
147+
source $scriptDir/karabo/activate
148+
fi
149+
150+
if [ $CODECOVERAGE = "y"]; then
151+
# Collect code coverage.
152+
safeRunCommand $scriptDir/run_python_tests.sh \
153+
--runLongTests \
154+
--collectCoverage \
155+
--rootDir $scriptDir
156+
else
157+
safeRunCommand $scriptDir/run_python_tests.sh \
158+
--runLongTests \
159+
--rootDir $scriptDir
160+
fi
161+
}
162+
147163
produceCodeCoverageReport() {
148164
echo "### Producing code coverage reports..."
149165
echo
@@ -160,6 +176,7 @@ produceCodeCoverageReport() {
160176
runUnitTests
161177
runIntegrationTests
162178
runPythonIntegrationTests
179+
runLongTests
163180

164181
# produce initial C++ coverage information
165182
safeRunCommand "$scriptDir/ci/coverage/report/gen_initial"
@@ -201,6 +218,8 @@ Available flags:
201218
--runTests - Run unit tests after building (useful for Debug|Release)
202219
--runIntegrationTests
203220
- Run integration tests after building (for Debug|Release)
221+
--runLongTests
222+
- Run long running tests after building (for Debug|Release)
204223
--numJobs N - Specify the number of jobs that make should use to run simultaneously
205224
206225
Note: "Dependencies" builds only the external dependencies
@@ -255,6 +274,7 @@ shift
255274
BUNDLE="n"
256275
RUNTESTS="n"
257276
RUNINTEGRATIONTESTS="n"
277+
RUNLONGTESTS="n"
258278
PYOPT="normal"
259279
NUM_JOBS=0
260280
CODECOVERAGE="n"
@@ -276,6 +296,9 @@ while [ -n "$1" ]; do
276296
# Run the integration tests
277297
RUNINTEGRATIONTESTS="y"
278298
;;
299+
--runLongTests)
300+
RUNLONGTESTS="y"
301+
;;
279302
--numJobs)
280303
# Limit the numbers of jobs for make runs
281304
if [ -n "$2" ]; then
@@ -294,13 +317,14 @@ while [ -n "$1" ]; do
294317
shift
295318
done
296319

297-
# selecting configuration CodeCoverage implies --runTests and --runIntegrationTests called by
298-
# the code coverage function. Also, other options are disabled.
320+
# selecting configuration CodeCoverage implies --runTests, --runIntegrationTests and
321+
# --runLongTests called by the code coverage function. Also, other options are disabled.
299322
# No need to run those separately, so we turn them off explicitly in case the user specified them.
300323
if [ "$CONF" = "CodeCoverage" ]; then
301324
CODECOVERAGE="y"
302325
RUNTESTS="n"
303326
RUNINTEGRATIONTESTS="n"
327+
RUNLONGTESTS="n"
304328
PYOPT="normal"
305329
fi
306330

@@ -349,6 +373,10 @@ if [ "$RUNINTEGRATIONTESTS" = "y" ]; then
349373
runPythonIntegrationTests
350374
fi
351375

376+
if [ "$RUNLONGTESTS" = "y" ]; then
377+
runLongTests
378+
fi
379+
352380
if [ "$CODECOVERAGE" = "y" ]; then
353381
produceCodeCoverageReport
354382
fi

src/pythonKarabo/karabo/bound_devices/property_test.py

Lines changed: 137 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,17 @@
66
All rights reserved."""
77

88
import numpy as np
9+
import threading
10+
import time
911

1012
from karabo.bound import (
1113
Encoding,
1214
KARABO_CLASSINFO, PythonDevice, DaqDataType, Hash, ImageData,
13-
Schema, State, Types, OVERWRITE_ELEMENT,
14-
ADMIN, AlarmCondition,
15+
Schema, State, Types, Unit, OVERWRITE_ELEMENT,
16+
ADMIN, AlarmCondition, MetricPrefix,
1517
BOOL_ELEMENT, FLOAT_ELEMENT, DOUBLE_ELEMENT, IMAGEDATA_ELEMENT,
1618
INT32_ELEMENT, UINT32_ELEMENT, INT64_ELEMENT, UINT64_ELEMENT,
17-
NDARRAY_ELEMENT, NODE_ELEMENT,
19+
NDARRAY_ELEMENT, NODE_ELEMENT, INPUT_CHANNEL,
1820
OUTPUT_CHANNEL, SLOT_ELEMENT, STRING_ELEMENT, TABLE_ELEMENT,
1921
VECTOR_BOOL_ELEMENT, VECTOR_CHAR_ELEMENT,
2022
VECTOR_FLOAT_ELEMENT, VECTOR_DOUBLE_ELEMENT,
@@ -37,9 +39,15 @@ def __init__(self, configuration):
3739
self.KARABO_SLOT(self.setReadonly)
3840
self.KARABO_SLOT(self.setAlarm)
3941
self.KARABO_SLOT(self.writeOutput)
42+
self.KARABO_SLOT(self.startWritingOutput)
43+
self.KARABO_SLOT(self.stopWritingOutput)
44+
self.KARABO_SLOT(self.resetChannelCounters)
4045
self.KARABO_SLOT(self.eosOutput)
4146

42-
self.outputCounter = 0
47+
self._writingOutput = False
48+
self._writingMutex = threading.Lock()
49+
self._writingWorker = None
50+
4351
# Define first function to be called after the constructor has finished
4452
self.registerInitialFunction(self.initialization)
4553

@@ -48,7 +56,7 @@ def expectedParameters(expected):
4856
'''Description of device parameters statically known'''
4957
(
5058
OVERWRITE_ELEMENT(expected).key("state")
51-
.setNewOptions(State.INIT, State.NORMAL, State.ERROR)
59+
.setNewOptions(State.INIT, State.NORMAL, State.ERROR, State.STARTED, State.STOPPING)
5260
.setNewDefaultValue(State.INIT)
5361
.commit(),
5462

@@ -357,7 +365,7 @@ def expectedParameters(expected):
357365
VECTOR_INT64_ELEMENT(pipeData).key("node.vecInt64")
358366
.description("A vector of signed 64-bit integers sent via the "
359367
"pipeline")
360-
.maxSize(PropertyTest.defVectorMaxSize) # DAQ needs that
368+
.maxSize(PropertyTest.defVectorMaxSize) # DAQ needs that
361369
.readOnly()
362370
.commit(),
363371

@@ -388,15 +396,77 @@ def expectedParameters(expected):
388396
.description("Write once to output channel 'Output'")
389397
.commit(),
390398

399+
FLOAT_ELEMENT(expected).key("outputFrequency")
400+
.displayedName("Output frequency")
401+
.description("The target frequency for continously writing to 'Output'")
402+
.unit(Unit.HERTZ)
403+
.maxInc(1000)
404+
.minExc(0.0)
405+
.assignmentOptional().defaultValue(1.0)
406+
.reconfigurable()
407+
.commit(),
408+
409+
INT32_ELEMENT(expected).key("outputCounter")
410+
.displayedName("Output Counter")
411+
.description("Last value sent as 'int32' via output channel 'Output'")
412+
.readOnly()
413+
.initialValue(0)
414+
.commit(),
415+
416+
SLOT_ELEMENT(expected).key("startWritingOutput")
417+
.displayedName("Start Writing")
418+
.description("Start writing continously to output channel 'Output'")
419+
.allowedStates(State.NORMAL)
420+
.commit(),
421+
422+
SLOT_ELEMENT(expected).key("stopWritingOutput")
423+
.displayedName("Stop Writing")
424+
.description("Stop writing continously to output channel 'Output'")
425+
.allowedStates(State.STARTED)
426+
.commit(),
427+
391428
SLOT_ELEMENT(expected).key("eosOutput")
392429
.displayedName("EOS to Output")
393430
.description("Write end-of-stream to output channel 'Output'")
394431
.commit(),
432+
433+
INPUT_CHANNEL(expected).key("input")
434+
.displayedName("Input")
435+
.dataSchema(pipeData) # re-use what the output channel sends
436+
.commit(),
437+
438+
UINT32_ELEMENT(expected).key("processingTime")
439+
.displayedName("Processing Time")
440+
.description("Processing time of input channel data handler")
441+
.assignmentOptional()
442+
.defaultValue(0)
443+
.reconfigurable()
444+
.unit(Unit.SECOND)
445+
.metricPrefix(MetricPrefix.MILLI)
446+
.commit(),
447+
448+
INT32_ELEMENT(expected).key("currentInputId")
449+
.displayedName("Current Input Id")
450+
.description("Last value received as 'int32' on input channel (default: 0)")
451+
.readOnly().initialValue(0)
452+
.commit(),
453+
454+
UINT32_ELEMENT(expected).key("inputCounter")
455+
.displayedName("Input Counter")
456+
.description("Number of data items received on input channel")
457+
.readOnly().initialValue(0)
458+
.commit(),
459+
460+
SLOT_ELEMENT(expected).key("resetChannelCounters")
461+
.displayedName("Reset Channels")
462+
.description("Reset counters involved in input/output channel data flow")
463+
.allowedStates(State.NORMAL)
464+
.commit(),
395465
)
396466

397467
def initialization(self):
398-
399468
self.updateState(State.NORMAL)
469+
self.KARABO_ON_DATA("input", self.onData)
400470

401471
def setReadonly(self):
402472
props = ["int32Property", "uint32Property",
@@ -422,24 +492,77 @@ def setAlarm(self):
422492
description="Converted from stringProperty")
423493

424494
def writeOutput(self):
425-
self.outputCounter += 1
495+
outputCounter = self["outputCounter"]
496+
outputCounter += 1
497+
self.set("outputCounter", outputCounter)
426498

427-
# Set all numbers inside to self.outputCounter:
499+
# Set all numbers inside to outputCounter:
428500
data = Hash("node", Hash())
429501
node = data["node"]
430502

431503
# setAs needed if counter exceeds INT32 range...
432-
node.setAs("int32", self.outputCounter, Types.INT32)
433-
node.set("string", str(self.outputCounter))
504+
node.setAs("int32", outputCounter, Types.INT32)
505+
node.set("string", str(outputCounter))
434506
# Using plain Hash.set(..), type of vector is determined from 1st elem.:
435-
node.setAs("vecInt64", [self.outputCounter] * self.defVectorMaxSize,
507+
node.setAs("vecInt64", outputCounter * self.defVectorMaxSize,
436508
Types.VECTOR_INT64)
437-
arr = np.full((100, 200), self.outputCounter, dtype=np.float32)
509+
arr = np.full((100, 200), outputCounter, dtype=np.float32)
438510
node.set("ndarray", arr)
439-
imArr = np.full((400, 500), self.outputCounter, dtype=np.uint16)
511+
imArr = np.full((400, 500), outputCounter, dtype=np.uint16)
440512
node.set("image", ImageData(imArr, encoding=Encoding.GRAY))
441513

442514
self.writeChannel("output", data)
443515

444516
def eosOutput(self):
445517
self.signalEndOfStream("output")
518+
519+
def startWritingOutput(self):
520+
self._writingOutput = True
521+
self.updateState(State.STARTED)
522+
self._writingWorker = threading.Thread(target=self.writeLoop)
523+
self._writingWorker.start()
524+
525+
def stopWritingOutput(self):
526+
with self._writingMutex:
527+
self._writingOutput = False
528+
self.updateState(State.STOPPING)
529+
self._writingWorker.join()
530+
self._writingWorker = None
531+
532+
def resetChannelCounters(self):
533+
self.set(Hash("inputCounter", 0,
534+
"outputCounter", 0,
535+
"currentInputId", 0))
536+
537+
def writeLoop(self):
538+
shouldWrite = True # Always run at least once: write should start immediately
539+
while shouldWrite:
540+
self.writeOutput()
541+
542+
with self._writingMutex:
543+
shouldWrite = self._writingOutput
544+
545+
if shouldWrite:
546+
# Waits for an interval as close as possible to the interval defined by
547+
# the nominal outputFrequency.
548+
delayTime = 1.0 / self.get("outputFrequency")
549+
time.sleep(delayTime)
550+
551+
# "Refreshes" the shouldWrite flag before entering a new loop interaction
552+
# During the sleep an stop command might have been issued and we want to
553+
# avoid the extra writeOutput call.
554+
with self._writingMutex:
555+
shouldWrite = self._writingOutput
556+
557+
self.updateState(State.NORMAL)
558+
559+
def onData(self, data, meta):
560+
# Sleeps to simulate heavy work.
561+
procTimeSecs = self["processingTime"]/1000.0
562+
time.sleep(procTimeSecs)
563+
564+
currentInputId = data.get("node.int32")
565+
inputCounter = self["inputCounter"]
566+
567+
self.set(Hash("currentInputId", currentInputId,
568+
"inputCounter", inputCounter+1))

0 commit comments

Comments
 (0)