66All rights reserved."""
77
88import numpy as np
9+ import threading
10+ import time
911
1012from 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