Skip to content
GitLab
Explore
Sign in
Primary navigation
Search or go to…
Project
A
ApplicationCore
Manage
Activity
Members
Labels
Plan
Issues
Issue boards
Milestones
Wiki
Code
Merge requests
Repository
Branches
Commits
Tags
Repository graph
Compare revisions
Snippets
Build
Pipelines
Jobs
Pipeline schedules
Artifacts
Deploy
Releases
Package registry
Container Registry
Model registry
Operate
Environments
Terraform modules
Monitor
Incidents
Analyze
Value stream analytics
Contributor analytics
CI/CD analytics
Repository analytics
Model experiments
Help
Help
Support
GitLab documentation
Compare GitLab plans
Community forum
Contribute to GitLab
Provide feedback
Keyboard shortcuts
?
Snippets
Groups
Projects
Show more breadcrumbs
ChimeraTK Mirror
ApplicationCore
Commits
59ad2613
Commit
59ad2613
authored
2 years ago
by
Jens Georg
Committed by
Martin Christoph Hierholzer
2 years ago
Browse files
Options
Downloads
Patches
Plain Diff
Fix testTrigger
parent
b7237dcd
No related branches found
Branches containing commit
No related tags found
Tags containing commit
No related merge requests found
Changes
3
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
src/ConnectionMaker.cc
+5
-5
5 additions, 5 deletions
src/ConnectionMaker.cc
tests/executables_src/testIllegalNetworks.cc
+0
-5
0 additions, 5 deletions
tests/executables_src/testIllegalNetworks.cc
tests/executables_src/testTrigger.cc
+437
-269
437 additions, 269 deletions
tests/executables_src/testTrigger.cc
with
442 additions
and
279 deletions
src/ConnectionMaker.cc
+
5
−
5
View file @
59ad2613
...
...
@@ -54,7 +54,7 @@ namespace ChimeraTK {
if
(
not
neededFeeder
)
{
// Only add CS consumer if we did not previously add CS feeder, we will add one or the other, but never both
debug
(
" N
o CS feeder in network,
creat
ing
additional ControlSystem consumer"
);
debug
(
" N
etwork has a non-CS feeder, can
creat
e
additional ControlSystem consumer"
);
net
.
consumers
.
push_back
(
VariableNetworkNode
(
proxy
.
getFullyQualifiedPath
(),
{
VariableDirection
::
consuming
,
false
},
*
net
.
valueType
,
net
.
valueLength
));
}
...
...
@@ -94,12 +94,12 @@ namespace ChimeraTK {
debug
(
" Creating fixed implementation for feeder '"
,
net
.
feeder
.
getName
(),
"'..."
);
if
(
net
.
consumers
.
size
()
==
1
&&
!
net
.
useExternalTrigger
)
{
debug
(
" One consumer
, set
ting
up
direct connection
without external trigger.
"
);
debug
(
" One consumer
without external trigger, crea
ting direct connection"
);
makeDirectConnectionForFeederWithImplementation
(
net
);
}
else
{
// More than one consuming node
debug
(
" More than one consuming node, setting up FanOut"
);
debug
(
" More than one consuming node
or having external trigger
, setting up FanOut"
);
makeFanOutConnectionForFeederWithImplementation
(
net
,
device
,
trigger
);
}
}
...
...
@@ -623,8 +623,8 @@ namespace ChimeraTK {
});
break
;
case
NodeType
::
TriggerReceiver
:
// This cannot happen. In a network Application -> TriggerReceiver, the
trigger
//
collection
code will always add a CS consumer, so there is never a 1:1 connection
// This cannot happen. In a network Application -> TriggerReceiver, the
connectNetwork()
// code will always add a CS consumer, so there is never a 1:1 connection
debug
(
" Node type is TriggerReceiver"
);
assert
(
false
);
break
;
...
...
This diff is collapsed.
Click to expand it.
tests/executables_src/testIllegalNetworks.cc
+
0
−
5
View file @
59ad2613
...
...
@@ -176,8 +176,3 @@ struct TestApplication6 : public ctk::Application {
void
mainLoop
()
override
{}
}
testModule2
{
this
,
"."
,
""
};
};
BOOST_AUTO_TEST_CASE_TEMPLATE
(
testNoElements
,
T
,
TestTypes
)
{
TestApplication6
<
T
>
app
;
BOOST_CHECK_THROW
(
ctk
::
TestFacility
tf
(
app
,
false
),
ctk
::
logic_error
);
}
This diff is collapsed.
Click to expand it.
tests/executables_src/testTrigger.cc
+
437
−
269
View file @
59ad2613
// SPDX-FileCopyrightText: Deutsches Elektronen-Synchrotron DESY, MSK, ChimeraTK Project <chimeratk-support@desy.de>
// SPDX-License-Identifier: LGPL-3.0-or-later
#include
"VoidAccessor.h"
#include
<chrono>
#include
<future>
...
...
@@ -26,340 +28,503 @@
using
namespace
boost
::
unit_test_framework
;
namespace
ctk
=
ChimeraTK
;
constexpr
char
dummySdm
[]
=
"sdm://./TestTransferGroupDummy=test.map"
;
/**********************************************************************************************************************/
// list of user types the accessors are tested with
typedef
boost
::
mpl
::
list
<
int8_t
,
uint8_t
,
int16_t
,
uint16_t
,
int32_t
,
uint32_t
,
float
,
double
>
test_types
;
// Application that has one polling consumer for a polling provider
// It should work without any trigger
struct
TestApp1
:
ctk
::
Application
{
TestApp1
()
:
ctk
::
Application
(
"testApp1"
)
{}
~
TestApp1
()
override
{
shutdown
();
}
/**********************************************************************************************************************/
struct
:
ctk
::
ApplicationModule
{
using
ctk
::
ApplicationModule
::
ApplicationModule
;
class
TestTransferGroupDummy
:
public
ChimeraTK
::
DummyBackend
{
public:
TestTransferGroupDummy
(
std
::
string
mapFileName
)
:
DummyBackend
(
mapFileName
)
{}
ctk
::
ScalarPollInput
<
int
>
readBack
{
this
,
"/MyModule/readBack"
,
"unit"
,
"description"
};
// This is just here so that we do not need a trigger - otherwise it would be connected to a pushing CS consumer
// automatically which would require a trigger
ctk
::
ScalarPollInput
<
int
>
tests
{
this
,
"/Deeper/hierarchies/need/tests"
,
"unit"
,
"description"
};
ctk
::
VoidInput
finger
{
this
,
"/finger"
,
""
,
""
};
void
mainLoop
()
override
{
while
(
true
)
{
readAll
();
}
}
}
someModule
{
this
,
"."
,
""
};
ctk
::
SetDMapFilePath
path
{
"test.dmap"
};
ctk
::
DeviceModule
dev
;
};
static
boost
::
shared_ptr
<
DeviceBackend
>
createInstance
(
std
::
string
,
std
::
string
,
std
::
list
<
std
::
string
>
parameters
,
std
::
string
)
{
return
boost
::
shared_ptr
<
DeviceBackend
>
(
new
TestTransferGroupDummy
(
parameters
.
front
()));
BOOST_AUTO_TEST_CASE
(
testDev2AppWithPollTrigger
)
{
// TestApp1 should work without specifying any trigger
{
TestApp1
app
;
app
.
dev
=
{
&
app
,
"Dummy0"
};
ChimeraTK
::
TestFacility
tf
{
app
};
auto
finger
=
tf
.
getVoid
(
"/finger"
);
auto
rb
=
tf
.
getScalar
<
int
>
(
"/MyModule/readBack"
);
tf
.
runApplication
();
ctk
::
Device
dev
(
"Dummy0"
);
dev
.
open
();
dev
.
write
(
"MyModule/actuator"
,
1
);
BOOST_TEST
(
rb
.
readNonBlocking
()
==
false
);
finger
.
write
();
tf
.
stepApplication
();
BOOST_TEST
(
rb
.
readNonBlocking
()
==
true
);
BOOST_TEST
(
rb
==
1
);
dev
.
write
(
"MyModule/actuator"
,
10
);
finger
.
write
();
tf
.
stepApplication
();
BOOST_TEST
(
rb
.
readNonBlocking
()
==
true
);
BOOST_TEST
(
rb
==
10
);
}
void
read
(
uint64_t
bar
,
uint64_t
address
,
int32_t
*
data
,
size_t
sizeInBytes
)
override
{
last_bar
=
bar
;
last_address
=
address
;
last_sizeInBytes
=
sizeInBytes
;
numberOfTransfers
++
;
DummyBackend
::
read
(
bar
,
address
,
data
,
sizeInBytes
);
// TestApp1 should also work with any trigger, but the trigger should be ignored
{
TestApp1
app
;
app
.
dev
=
{
&
app
,
"Dummy0"
,
"/cs/tick"
};
ChimeraTK
::
TestFacility
tf
{
app
};
auto
tick
=
tf
.
getVoid
(
"/cs/tick"
);
auto
finger
=
tf
.
getVoid
(
"/finger"
);
auto
rb
=
tf
.
getScalar
<
int
>
(
"/MyModule/readBack"
);
tf
.
runApplication
();
ctk
::
Device
dev
(
"Dummy0"
);
dev
.
open
();
dev
.
write
(
"MyModule/actuator"
,
2
);
BOOST_TEST
(
rb
.
readNonBlocking
()
==
false
);
finger
.
write
();
tf
.
stepApplication
();
BOOST_TEST
(
rb
.
readNonBlocking
()
==
true
);
BOOST_TEST
(
rb
==
2
);
// Trigger device trigger - values should not change
tick
.
write
();
tf
.
stepApplication
();
BOOST_TEST
(
rb
.
readNonBlocking
()
==
false
);
BOOST_TEST
(
rb
==
2
);
dev
.
write
(
"MyModule/actuator"
,
20
);
// Trigger read-out of poll variables in main loop
finger
.
write
();
tf
.
stepApplication
();
BOOST_TEST
(
rb
.
readNonBlocking
()
==
true
);
BOOST_TEST
(
rb
==
20
);
// Trigger device trigger - values should not change
tick
.
write
();
tf
.
stepApplication
();
BOOST_TEST
(
rb
.
readNonBlocking
()
==
false
);
BOOST_TEST
(
rb
==
20
);
}
}
std
::
atomic
<
size_t
>
numberOfTransfers
{
0
};
std
::
atomic
<
uint64_t
>
last_bar
;
std
::
atomic
<
uint64_t
>
last_address
;
std
::
atomic
<
size_t
>
last_sizeInBytes
;
/**********************************************************************************************************************/
struct
TestApp2
:
ctk
::
Application
{
TestApp2
()
:
ctk
::
Application
(
"testApp2"
)
{}
~
TestApp2
()
override
{
shutdown
();
}
struct
:
ctk
::
ApplicationModule
{
using
ctk
::
ApplicationModule
::
ApplicationModule
;
ctk
::
ScalarPushInput
<
int
>
readBack
{
this
,
"/MyModule/readBack"
,
"unit"
,
"description"
};
void
mainLoop
()
override
{
while
(
true
)
{
readAll
();
}
}
}
someModule
{
this
,
"."
,
""
};
ctk
::
SetDMapFilePath
path
{
"test.dmap"
};
ctk
::
DeviceModule
dev
;
};
/*********************************************************************************************************************/
/* the ApplicationModule for the test is a template of the user type */
// Device that requires trigger, the trigger is 1:1 put into the CS
BOOST_AUTO_TEST_CASE
(
testDev2AppWithCsDirectTrigger
)
{
// TestApp2 should not work without specifying any trigger
{
TestApp2
app
;
app
.
dev
=
{
&
app
,
"Dummy0"
};
BOOST_CHECK_THROW
(
ChimeraTK
::
TestFacility
(
app
,
true
),
ChimeraTK
::
logic_error
);
}
template
<
typename
T
>
struct
TestModule
:
public
ctk
::
ApplicationModule
{
TestModule
(
ctk
::
ModuleGroup
*
owner
,
const
std
::
string
&
name
,
const
std
::
string
&
description
,
const
std
::
unordered_set
<
std
::
string
>&
tags
=
{})
:
ApplicationModule
(
owner
,
name
,
description
,
tags
),
mainLoopStarted
(
2
)
{}
// TestApp2 also works with a trigger. If the trigger is triggered, no data transfer should happen
{
TestApp2
app
;
app
.
dev
=
{
&
app
,
"Dummy0"
,
"/cs/trigger"
};
ctk
::
ScalarPushInput
<
T
>
consumingPush
{
this
,
"consumingPush"
,
"MV/m"
,
"Description"
};
ctk
::
ScalarPushInput
<
T
>
consumingPush2
{
this
,
"consumingPush2"
,
"MV/m"
,
"Description"
}
;
ctk
::
ScalarPushInput
<
T
>
consumingPush3
{
this
,
"consumingPush3"
,
"MV/m"
,
"Description"
}
;
ChimeraTK
::
TestFacility
tf
{
app
,
true
};
auto
tick
=
tf
.
getVoid
(
"/cs/trigger"
)
;
auto
rb
=
tf
.
getScalar
<
int
>
(
"/MyModule/readBack"
)
;
ctk
::
ScalarPollInput
<
T
>
consumingPoll
{
this
,
"consumingPoll"
,
"MV/m"
,
"Description"
};
ctk
::
ScalarPollInput
<
T
>
consumingPoll2
{
this
,
"consumingPoll2"
,
"MV/m"
,
"Description"
};
ctk
::
ScalarPollInput
<
T
>
consumingPoll3
{
this
,
"consumingPoll3"
,
"MV/m"
,
"Description"
};
tf
.
runApplication
();
ctk
::
ScalarOutput
<
T
>
theTrigger
{
this
,
"theTrigger"
,
"MV/m"
,
"Description"
};
ctk
::
ScalarOutput
<
T
>
feedingToDevice
{
this
,
"feedingToDevice"
,
"MV/m"
,
"Description"
};
ctk
::
Device
dev
(
"Dummy0"
);
dev
.
open
();
dev
.
write
(
"MyModule/actuator"
,
1
);
// We do not use testable mode for this test, so we need this barrier to synchronise to the beginning of the
// mainLoop(). This is required since the mainLoopWrapper accesses the module variables before the start of the
// mainLoop.
// execute this right after the Application::run():
// app.testModule.mainLoopStarted.wait(); // make sure the module's mainLoop() is entered
boost
::
barrier
mainLoopStarted
;
tick
.
write
();
tf
.
stepApplication
();
void
prepare
()
override
{
incrementDataFaultCounter
();
// force data to be flagged as faulty
feedingToDevice
=
13
;
// the initial value
writeAll
();
decrementDataFaultCounter
();
// data validity depends on inputs
BOOST_TEST
(
rb
.
readNonBlocking
()
==
true
);
BOOST_TEST
(
rb
==
1
);
dev
.
write
(
"MyModule/actuator"
,
12
);
BOOST_TEST
(
rb
.
readNonBlocking
()
==
false
);
BOOST_TEST
(
rb
==
1
);
tick
.
write
();
tf
.
stepApplication
();
BOOST_TEST
(
rb
.
readNonBlocking
()
==
true
);
BOOST_TEST
(
rb
==
12
);
}
}
void
mainLoop
()
override
{
mainLoopStarted
.
wait
();
}
/**********************************************************************************************************************/
struct
TestApp3
:
ctk
::
Application
{
TestApp3
()
:
ctk
::
Application
(
"testApp3"
)
{}
~
TestApp3
()
override
{
shutdown
();
}
struct
:
ctk
::
ApplicationModule
{
using
ctk
::
ApplicationModule
::
ApplicationModule
;
ctk
::
VoidInput
tick
{
this
,
"/cs/trigger"
,
"unit"
,
"description"
};
ctk
::
ScalarOutput
<
int
>
tock
{
this
,
"/tock"
,
""
,
""
};
void
mainLoop
()
override
{
tock
=
0
;
while
(
true
)
{
tock
.
write
();
tock
++
;
readAll
();
}
}
}
tock
{
this
,
"."
,
""
};
struct
:
ctk
::
ApplicationModule
{
using
ctk
::
ApplicationModule
::
ApplicationModule
;
ctk
::
ScalarPushInput
<
int
>
readBack
{
this
,
"/MyModule/readBack"
,
"unit"
,
"description"
};
ctk
::
ScalarPollInput
<
int
>
tests
{
this
,
"/Deeper/hierarchies/need/tests"
,
"unit"
,
"description"
};
void
mainLoop
()
override
{
while
(
true
)
{
readAll
();
}
}
}
someModule
{
this
,
"."
,
""
};
ctk
::
SetDMapFilePath
path
{
"test.dmap"
};
ctk
::
DeviceModule
dev
{
this
,
"Dummy0"
,
"/cs/trigger"
};
};
/*********************************************************************************************************************/
/* dummy application */
// Device that requires trigger, the trigger is distributed in the Application as well
BOOST_AUTO_TEST_CASE
(
testDev2AppWithCsDistributedTrigger
)
{
TestApp3
app
;
template
<
typename
T
>
struct
TestApplication
:
public
ctk
::
Application
{
TestApplication
()
:
Application
(
"testSuite"
)
{
ChimeraTK
::
BackendFactory
::
getInstance
().
registerBackendType
(
"TestTransferGroupDummy"
,
""
,
&
TestTransferGroupDummy
::
createInstance
,
CHIMERATK_DEVICEACCESS_VERSION
);
}
~
TestApplication
()
{
shutdown
();
}
ChimeraTK
::
TestFacility
tf
{
app
,
true
};
auto
tick
=
tf
.
getVoid
(
"/cs/trigger"
);
auto
tock
=
tf
.
getScalar
<
int
>
(
"/tock"
);
auto
rb
=
tf
.
getScalar
<
int
>
(
"/MyModule/readBack"
);
TestModule
<
T
>
testModule
{
this
,
"testModule"
,
"The test module"
};
ctk
::
DeviceModule
dev
{
this
,
"Dummy0"
};
ctk
::
DeviceModule
dev2
{
this
,
dummySdm
};
};
tf
.
runApplication
();
/*********************************************************************************************************************/
/* test trigger by app variable when connecting a polled device register to an
* app variable */
ctk
::
Device
dev
(
"Dummy0"
);
dev
.
open
();
dev
.
write
(
"MyModule/actuator"
,
1
);
tick
.
write
();
tf
.
stepApplication
();
BOOST_TEST
(
tock
.
readNonBlocking
()
==
true
);
BOOST_TEST
(
tock
==
1
);
BOOST_TEST
(
rb
.
readNonBlocking
()
==
true
);
BOOST_TEST
(
rb
==
1
);
dev
.
write
(
"MyModule/actuator"
,
12
);
BOOST_TEST
(
tock
.
readNonBlocking
()
==
false
);
BOOST_TEST
(
tock
==
1
);
BOOST_TEST
(
rb
.
readNonBlocking
()
==
false
);
BOOST_TEST
(
rb
==
1
);
tick
.
write
();
tf
.
stepApplication
();
BOOST_TEST
(
tock
.
readNonBlocking
()
==
true
);
BOOST_TEST
(
tock
==
2
);
BOOST_TEST
(
rb
.
readNonBlocking
()
==
true
);
BOOST_TEST
(
rb
==
12
);
}
BOOST_AUTO_TEST_CASE_TEMPLATE
(
testTriggerDevToApp
,
T
,
test_types
)
{
std
::
cout
<<
"***************************************************************"
"******************************************************"
<<
std
::
endl
;
std
::
cout
<<
"==> testTriggerDevToApp<"
<<
typeid
(
T
).
name
()
<<
">"
<<
std
::
endl
;
/**********************************************************************************************************************/
ChimeraTK
::
BackendFactory
::
getInstance
().
setDMapFilePath
(
"test.dmap"
);
struct
TestApp4
:
ctk
::
Application
{
TestApp4
()
:
ctk
::
Application
(
"testApp4"
)
{}
~
TestApp4
()
override
{
shutdown
();
}
TestApplication
<
T
>
app
;
auto
pvManagers
=
ctk
::
createPVManager
();
app
.
setPVManager
(
pvManagers
.
second
);
struct
:
ctk
::
ApplicationModule
{
using
ctk
::
ApplicationModule
::
ApplicationModule
;
// app.testModule.feedingToDevice >> app.dev["MyModule"]("actuator")
;
ctk
::
ScalarPushInput
<
float
>
singed32
{
this
,
"/Device/signed32"
,
"unit"
,
"description"
}
;
// app.dev["MyModule"]("readBack")[app.testModule.theTrigger] >> app.testModule.consumingPush;
app
.
initialise
();
void
mainLoop
()
override
{
while
(
true
)
{
readAll
();
}
}
}
someOtherModule
{
this
,
"."
,
""
};
app
.
run
();
app
.
testModule
.
mainLoopStarted
.
wait
();
// make sure the module's mainLoop() is entered
struct
:
ctk
::
ApplicationModule
{
using
ctk
::
ApplicationModule
::
ApplicationModule
;
// check for initial value. Should be there when entering the main loop.
BOOST_CHECK_EQUAL
(
static_cast
<
T
>
(
app
.
testModule
.
consumingPush
),
13
)
;
ctk
::
ScalarPushInput
<
int
>
readBack
{
this
,
"/MyModule/readBack"
,
"unit"
,
"description"
};
ctk
::
ScalarPollInput
<
int
>
tests
{
this
,
"/Deeper/hierarchies/need/tests"
,
"unit"
,
"description"
}
;
// single theaded test
app
.
testModule
.
feedingToDevice
=
42
;
app
.
testModule
.
feedingToDevice
.
write
();
BOOST_CHECK
(
app
.
testModule
.
consumingPush
.
readNonBlocking
()
==
false
);
BOOST_CHECK_EQUAL
(
static_cast
<
T
>
(
app
.
testModule
.
consumingPush
),
13
);
app
.
testModule
.
theTrigger
.
write
();
app
.
testModule
.
consumingPush
.
read
();
BOOST_CHECK
(
app
.
testModule
.
consumingPush
==
42
);
void
mainLoop
()
override
{
while
(
true
)
{
readAll
();
}
}
}
someModule
{
this
,
"."
,
""
};
ctk
::
SetDMapFilePath
path
{
"test.dmap"
};
ctk
::
DeviceModule
dev
{
this
,
"Dummy0"
,
"/cs/trigger"
};
ctk
::
DeviceModule
dev2
{
this
,
"Dummy1Mapped"
,
"/cs/trigger"
};
};
// Two devices using the same trigger
BOOST_AUTO_TEST_CASE
(
testDev2App1Trigger2Devices
)
{
TestApp4
app
;
ChimeraTK
::
TestFacility
tf
{
app
,
true
};
auto
tick
=
tf
.
getVoid
(
"/cs/trigger"
);
auto
f
=
tf
.
getScalar
<
float
>
(
"/Device/signed32"
);
auto
rb
=
tf
.
getScalar
<
int
>
(
"/MyModule/readBack"
);
// launch read() on the consumer asynchronously and make sure it does not yet
// receive anything
auto
futRead
=
std
::
async
(
std
::
launch
::
async
,
[
&
app
]
{
app
.
testModule
.
consumingPush
.
read
();
});
BOOST_CHECK
(
futRead
.
wait_for
(
std
::
chrono
::
milliseconds
(
200
))
==
std
::
future_status
::
timeout
);
ctk
::
Device
dev
(
"Dummy0"
);
dev
.
open
();
BOOST_CHECK
(
app
.
testModule
.
consumingPush
==
42
);
ctk
::
Device
dev2
(
"Dummy1"
);
dev2
.
open
();
dev2
.
write
(
"FixedPoint/value"
,
12.4
);
// write to the feeder
app
.
testModule
.
feedingToDevice
=
120
;
app
.
testModule
.
feedingToDevice
.
write
();
BOOST_CHECK
(
futRead
.
wait_for
(
std
::
chrono
::
milliseconds
(
200
))
==
std
::
future_status
::
timeout
);
BOOST_CHECK
(
app
.
testModule
.
consumingPush
==
42
);
tf
.
runApplication
();
// send trigger
app
.
testModule
.
theTrigger
.
write
();
dev
.
write
(
"MyModule/actuator"
,
1
);
// check that the consumer now receives the just written value
BOOST_CHECK
(
futRead
.
wait_for
(
std
::
chrono
::
milliseconds
(
2000
))
==
std
::
future_status
::
ready
);
BOOST_CHECK
(
app
.
testModule
.
consumingPush
==
120
);
BOOST_TEST
(
f
.
readNonBlocking
()
==
false
);
BOOST_TEST
(
rb
.
readNonBlocking
()
==
false
);
tick
.
write
();
tf
.
stepApplication
();
BOOST_TEST
(
f
.
readNonBlocking
()
==
true
);
BOOST_TEST
(
rb
.
readNonBlocking
()
==
true
);
BOOST_TEST
((
f
-
12.4
)
<
0.01
);
BOOST_TEST
(
rb
==
1
);
dev
.
write
(
"MyModule/actuator"
,
2
);
dev2
.
write
(
"FixedPoint/value"
,
24.8
);
BOOST_TEST
(
f
.
readNonBlocking
()
==
false
);
BOOST_TEST
(
rb
.
readNonBlocking
()
==
false
);
tick
.
write
();
tf
.
stepApplication
();
BOOST_TEST
(
f
.
readNonBlocking
()
==
true
);
BOOST_TEST
(
rb
.
readNonBlocking
()
==
true
);
BOOST_TEST
((
f
-
24.8
)
<
0.001
);
BOOST_TEST
(
rb
==
2
);
}
/*********************************************************************************************************************/
/* test trigger by app variable when connecting a polled device register to
* control system variable */
#if 0
BOOST_AUTO_TEST_CASE_TEMPLATE(testTriggerDevToCS, T, test_types) {
std::cout << "***************************************************************"
"******************************************************"
<< std::endl;
std::cout << "==> testTriggerDevToCS<" << typeid(T).name() << ">" << std::endl;
/**********************************************************************************************************************/
ChimeraTK::BackendFactory::getInstance().setDMapFilePath("test.dmap");
struct
TestApp5
:
ctk
::
Application
{
TestApp5
()
:
ctk
::
Application
(
"testApp5"
)
{
}
~
TestApp5
()
override
{
shutdown
();
}
TestApplication<T> app;
struct
:
ctk
::
ApplicationModule
{
using
ctk
::
ApplicationModule
::
ApplicationModule
;
auto pvManagers = ctk::createPVManager()
;
app.setPVManager(pvManagers.second)
;
ctk
::
VoidInput
finger
{
this
,
"/finger"
,
""
,
""
}
;
ctk
::
VoidOutput
trigger
{
this
,
"/trigger"
,
""
,
""
}
;
app.dev("/MyModule/readBack", typeid(T), 1)[app.testModule.theTrigger] >> app.cs("myCSVar");
void
mainLoop
()
override
{
while
(
true
)
{
readAll
();
trigger
.
write
();
}
}
}
someModule
{
this
,
"."
,
""
};
ctk::
Device dev("Dummy0")
;
dev.open()
;
dev.write("MyModule/actuator", 1); // write initial value
ctk
::
SetDMapFilePath
path
{
"test.dmap"
}
;
ctk
::
DeviceModule
dev
;
};
app.initialise();
app.run()
;
app.
testModule.mainLoopStarted.wait(); // make sure the module's mainLoop() is entered
BOOST_AUTO_TEST_CASE
(
testDev2CSCsTrigger
)
{
TestApp5
app
;
app
.
dev
=
{
&
app
,
"Dummy0"
,
"/cs/trigger"
};
auto myCSVar = pvManagers.first->getProcessArray<T>("/myCSVar");
ChimeraTK
::
TestFacility
tf
{
app
,
true
};
auto
tick
=
tf
.
getVoid
(
"/cs/trigger"
);
auto
rb
=
tf
.
getScalar
<
int
>
(
"/MyModule/readBack"
);
// single theaded test only, since the receiving process scalar does not support blocking
myCSVar->read(); // read initial value
BOOST_CHECK_EQUAL(myCSVar->accessData(0), 1);
BOOST_CHECK(myCSVar->readNonBlocking() == false);
dev.write("MyModule/actuator", 42);
usleep(10000);
BOOST_CHECK(myCSVar->readNonBlocking() == false);
app.testModule.setCurrentVersionNumber({});
app.testModule.theTrigger.write();
myCSVar->read();
BOOST_CHECK_EQUAL(myCSVar->accessData(0), 42);
tf
.
runApplication
();
BOOST_CHECK(myCSVar->readNonBlocking() == false);
dev.write("MyModule/actuator", 120);
usleep(10000);
BOOST_CHECK(myCSVar->readNonBlocking() == false);
app.testModule.theTrigger.write();
myCSVar->read();
BOOST_CHECK_EQUAL(myCSVar->accessData(0), 120);
ctk
::
Device
dev
(
"Dummy0"
);
dev
.
open
();
dev
.
write
(
"MyModule/actuator"
,
1
);
BOOST_CHECK(myCSVar->readNonBlocking() == false
);
}
tick
.
write
(
);
tf
.
stepApplication
();
/*********************************************************************************************************************/
/* test trigger by app variable when connecting a polled device register to
* control system variable */
BOOST_TEST
(
rb
.
readNonBlocking
()
==
true
);
BOOST_TEST
(
rb
==
1
);
BOOST_AUTO_TEST_CASE_TEMPLATE(testTriggerByCS, T, test_types) {
std::cout << "***************************************************************"
"******************************************************"
<< std::endl;
std::cout << "==> testTriggerByCS<" << typeid(T).name() << ">" << std::endl;
dev
.
write
(
"MyModule/actuator"
,
12
);
BOOST_TEST
(
rb
.
readNonBlocking
()
==
false
);
BOOST_TEST
(
rb
==
1
);
ChimeraTK::BackendFactory::getInstance().setDMapFilePath("test.dmap");
tick
.
write
();
tf
.
stepApplication
();
BOOST_TEST
(
rb
.
readNonBlocking
()
==
true
);
BOOST_TEST
(
rb
==
12
);
}
TestApplication<T> app;
BOOST_AUTO_TEST_CASE
(
testDev2CSAppTrigger
)
{
TestApp5
app
;
app
.
dev
=
{
&
app
,
"Dummy0"
,
"/trigger"
};
auto pvManagers = ctk::createPVManager();
app.setPVManager(pvManagers.second);
ChimeraTK
::
TestFacility
tf
{
app
,
true
};
auto
tick
=
tf
.
getVoid
(
"/finger"
);
auto
rb
=
tf
.
getScalar
<
int
>
(
"/MyModule/readBack"
);
app.dev("/MyModule/readBack", typeid(T), 1)[app.cs("theTrigger", typeid(T), 1)] >> app.cs("myCSVar"
);
tf
.
runApplication
(
);
ctk
::
Device
dev
(
"Dummy0"
);
dev
.
open
();
dev.write("MyModule/actuator", 1);
// write initial value
dev
.
write
(
"MyModule/actuator"
,
1
);
app.initialise();
app.run();
app.testModule.mainLoopStarted.wait(); // make sure the module's mainLoop() is entered
tick
.
write
();
tf
.
stepApplication
();
BOOST_TEST
(
rb
.
readNonBlocking
()
==
true
);
BOOST_TEST
(
rb
==
1
);
dev
.
write
(
"MyModule/actuator"
,
12
);
BOOST_TEST
(
rb
.
readNonBlocking
()
==
false
);
BOOST_TEST
(
rb
==
1
);
auto myCSVar = pvManagers.first->getProcessArray<T>("/myCSVar");
auto theTrigger = pvManagers.first->getProcessArray<T>("/theTrigger");
// Need to send the trigger once, since ApplicationCore expects all CS variables to be written once by the
// ControlSystemAdapter. We do not use the TestFacility here, so we have to do it ourself.
theTrigger->write();
// single theaded test only, since the receiving process scalar does not support blocking
myCSVar->read(); // read initial value
BOOST_CHECK_EQUAL(myCSVar->accessData(0), 1);
BOOST_CHECK(myCSVar->readNonBlocking() == false);
dev.write("MyModule/actuator", 42);
usleep(10000);
BOOST_CHECK(myCSVar->readNonBlocking() == false);
myCSVar->accessData(0) = 0;
theTrigger->write();
myCSVar->read();
BOOST_CHECK_EQUAL(myCSVar->accessData(0), 42);
BOOST_CHECK(myCSVar->readNonBlocking() == false);
dev.write("MyModule/actuator", 120);
usleep(10000);
BOOST_CHECK(myCSVar->readNonBlocking() == false);
myCSVar->accessData(0) = 0;
theTrigger->write();
myCSVar->read();
BOOST_CHECK_EQUAL(myCSVar->accessData(0), 120);
BOOST_CHECK(myCSVar->readNonBlocking() == false);
tick
.
write
();
tf
.
stepApplication
();
BOOST_TEST
(
rb
.
readNonBlocking
()
==
true
);
BOOST_TEST
(
rb
==
12
);
}
constexpr
char
dummySdm
[]
=
"(TestTransferGroupDummy?map=test_readonly.map)"
;
// list of user types the accessors are tested with
typedef
boost
::
mpl
::
list
<
int8_t
,
uint8_t
,
int16_t
,
uint16_t
,
int32_t
,
uint32_t
,
float
,
double
>
test_types
;
/**********************************************************************************************************************/
class
TestTransferGroupDummy
:
public
ChimeraTK
::
DummyBackend
{
public:
TestTransferGroupDummy
(
std
::
string
mapFileName
)
:
DummyBackend
(
mapFileName
)
{}
static
boost
::
shared_ptr
<
DeviceBackend
>
createInstance
(
std
::
string
,
std
::
map
<
std
::
string
,
std
::
string
>
parameters
)
{
return
boost
::
shared_ptr
<
DeviceBackend
>
(
new
TestTransferGroupDummy
(
parameters
[
"map"
]));
}
void
read
(
uint64_t
bar
,
uint64_t
address
,
int32_t
*
data
,
size_t
sizeInBytes
)
override
{
last_bar
=
bar
;
last_address
=
address
;
last_sizeInBytes
=
sizeInBytes
;
numberOfTransfers
++
;
DummyBackend
::
read
(
bar
,
address
,
data
,
sizeInBytes
);
}
std
::
atomic
<
size_t
>
numberOfTransfers
{
0
};
std
::
atomic
<
uint64_t
>
last_bar
;
std
::
atomic
<
uint64_t
>
last_address
;
std
::
atomic
<
size_t
>
last_sizeInBytes
;
};
/*********************************************************************************************************************/
/* t
est trigger by app variable through FanOut (i.e. trigger variable is also used else where)
*/
/* t
he ApplicationModule for the test is a template of the user type
*/
BOOST_AUTO_TEST_CASE_TEMPLATE(testTriggerByCSFanOut, T, test_types) {
std::cout << "***************************************************************"
"******************************************************"
<< std::endl;
std::cout << "==> testTriggerByCS<" << typeid(T).name() << ">" << std::endl;
struct
TestModule
:
public
ctk
::
ApplicationModule
{
TestModule
(
ctk
::
ModuleGroup
*
owner
,
const
std
::
string
&
name
,
const
std
::
string
&
description
,
const
std
::
unordered_set
<
std
::
string
>&
tags
=
{})
:
ApplicationModule
(
owner
,
name
,
description
,
tags
),
mainLoopStarted
(
2
)
{}
ChimeraTK::BackendFactory::getInstance().setDMapFilePath("test.dmap");
ctk
::
ScalarPushInput
<
int
>
consumingPush
{
this
,
"/REG1"
,
"MV/m"
,
"Description"
};
ctk
::
ScalarPushInput
<
int
>
consumingPush2
{
this
,
"/REG2"
,
"MV/m"
,
"Description"
};
ctk
::
ScalarPushInput
<
int
>
consumingPush3
{
this
,
"/REG3"
,
"MV/m"
,
"Description"
};
TestApplication<T> app
;
ctk
::
ScalarOutput
<
int
>
theTrigger
{
this
,
"theTrigger"
,
"MV/m"
,
"Description"
}
;
auto pvManagers = ctk::createPVManager();
app.setPVManager(pvManagers.second);
// We do not use testable mode for this test, so we need this barrier to synchronise to the beginning of the
// mainLoop(). This is required since the mainLoopWrapper accesses the module variables before the start of the
// mainLoop.
// execute this right after the Application::run():
// app.testModule.mainLoopStarted.wait(); // make sure the module's mainLoop() is entered
boost
::
barrier
mainLoopStarted
;
auto trigger = app.cs("theTrigger", typeid(T), 1);
trigger >> app.cs("theTriggerCopied");
app.dev("/MyModule/readBack", typeid(T), 1)[trigger] >> app.cs("myCSVar");
void
prepare
()
override
{
incrementDataFaultCounter
();
// force data to be flagged as faulty
writeAll
();
decrementDataFaultCounter
();
// data validity depends on inputs
}
ctk::Device dev("Dummy0");
dev.open();
dev.write("MyModule/actuator", 1); // write initial value
void
mainLoop
()
override
{
std
::
cout
<<
"Start of main loop"
<<
std
::
endl
;
mainLoopStarted
.
wait
();
std
::
cout
<<
"End of main loop"
<<
std
::
endl
;
}
};
app.initialise();
/*********************************************************************************************************************/
/* dummy application */
app.run();
app.testModule.mainLoopStarted.wait(); // make sure the module's mainLoop() is entered
struct
TestApplication
:
public
ctk
::
Application
{
TestApplication
()
:
Application
(
"testSuite"
)
{
ChimeraTK
::
BackendFactory
::
getInstance
().
registerBackendType
(
"TestTransferGroupDummy"
,
&
TestTransferGroupDummy
::
createInstance
);
dev2
=
{
this
,
dummySdm
,
"/testModule/theTrigger"
};
}
~
TestApplication
()
override
{
shutdown
();
}
TestModule
testModule
{
this
,
"testModule"
,
"The test module"
};
ctk
::
DeviceModule
dev2
;
};
auto myCSVar = pvManagers.first->getProcessArray<T>("/myCSVar");
auto theTrigger = pvManagers.first->getProcessArray<T>("/theTrigger");
auto theTriggerCopied = pvManagers.first->getProcessArray<T>("/theTriggerCopied");
// Need to send the trigger once, since ApplicationCore expects all CS variables to be written once by the
// ControlSystemAdapter. We do not use the TestFacility here, so we have to do it ourself.
theTrigger->accessData(0) = 2;
theTrigger->write();
theTriggerCopied->read();
BOOST_CHECK_EQUAL(theTriggerCopied->accessData(0), 2);
// single theaded test only, since the receiving process scalar does not support blocking
myCSVar->read(); // read initial value
BOOST_CHECK_EQUAL(myCSVar->accessData(0), 1);
BOOST_CHECK(myCSVar->readNonBlocking() == false);
dev.write("MyModule/actuator", 42);
usleep(10000);
BOOST_CHECK(myCSVar->readNonBlocking() == false);
myCSVar->accessData(0) = 0;
theTrigger->accessData(0) = 3;
theTrigger->write();
myCSVar->read();
BOOST_CHECK_EQUAL(myCSVar->accessData(0), 42);
theTriggerCopied->read();
BOOST_CHECK_EQUAL(theTriggerCopied->accessData(0), 3);
BOOST_CHECK(myCSVar->readNonBlocking() == false);
dev.write("MyModule/actuator", 120);
usleep(10000);
BOOST_CHECK(myCSVar->readNonBlocking() == false);
myCSVar->accessData(0) = 0;
theTrigger->accessData(0) = 4;
theTrigger->write();
myCSVar->read();
BOOST_CHECK_EQUAL(myCSVar->accessData(0), 120);
theTriggerCopied->read();
BOOST_CHECK_EQUAL(theTriggerCopied->accessData(0), 4);
BOOST_CHECK(myCSVar->readNonBlocking() == false);
BOOST_CHECK(theTriggerCopied->readNonBlocking() == false);
}
#endif
/*********************************************************************************************************************/
/* test that multiple variables triggered by the same source are put into the
* same TransferGroup */
BOOST_AUTO_TEST_CASE
_TEMPLATE
(
testTriggerTransferGroup
,
T
,
test_types
)
{
BOOST_AUTO_TEST_CASE
(
testTriggerTransferGroup
)
{
std
::
cout
<<
"***************************************************************"
"******************************************************"
<<
std
::
endl
;
std
::
cout
<<
"==> testTriggerTransferGroup
<"
<<
typeid
(
T
).
name
()
<<
">
"
<<
std
::
endl
;
std
::
cout
<<
"==> testTriggerTransferGroup"
<<
std
::
endl
;
ChimeraTK
::
BackendFactory
::
getInstance
().
setDMapFilePath
(
"test.dmap"
);
TestApplication
<
T
>
app
;
TestApplication
app
;
auto
pvManagers
=
ctk
::
createPVManager
();
app
.
setPVManager
(
pvManagers
.
second
);
...
...
@@ -369,9 +534,6 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(testTriggerTransferGroup, T, test_types) {
ChimeraTK
::
BackendFactory
::
getInstance
().
createBackend
(
dummySdm
));
BOOST_CHECK
(
backend
!=
NULL
);
/*app.dev2("/REG1")[app.testModule.theTrigger] >> app.testModule.consumingPush;
app.dev2("/REG2")[app.testModule.theTrigger] >> app.testModule.consumingPush2;
app.dev2("/REG3")[app.testModule.theTrigger] >> app.testModule.consumingPush3;*/
app
.
initialise
();
app
.
run
();
app
.
testModule
.
mainLoopStarted
.
wait
();
// make sure the module's mainLoop() is entered
...
...
@@ -380,19 +542,22 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(testTriggerTransferGroup, T, test_types) {
app
.
testModule
.
consumingPush
=
0
;
app
.
testModule
.
consumingPush2
=
0
;
app
.
testModule
.
consumingPush3
=
0
;
dev
.
write
(
"/REG1"
,
11
);
dev
.
write
(
"/REG2"
,
22
);
dev
.
write
(
"/REG3"
,
33
);
dev
.
write
(
"/REG1
.DUMMY_WRITEABLE
"
,
11
);
dev
.
write
(
"/REG2
.DUMMY_WRITEABLE
"
,
22
);
dev
.
write
(
"/REG3
.DUMMY_WRITEABLE
"
,
33
);
// from the inital value transfer
// from the init
i
al value transfer
CHECK_TIMEOUT
(
backend
->
numberOfTransfers
==
1
,
10000
);
// trigger the transfer
app
.
testModule
.
theTrigger
.
write
();
CHECK_TIMEOUT
(
backend
->
numberOfTransfers
==
2
,
10000
);
BOOST_CHECK
(
backend
->
last_bar
==
0
);
BOOST_CHECK
(
backend
->
last_address
==
0
);
BOOST_CHECK
(
backend
->
last_sizeInBytes
==
12
);
BOOST_TEST
(
backend
->
last_bar
==
0
);
BOOST_TEST
(
backend
->
last_address
==
0
);
// We only explicitly connect the three registers in the app, but the connection code will also connect the other
// registers into the CS, hence we need to check for the full size
BOOST_TEST
(
backend
->
last_sizeInBytes
==
32
);
// check result
app
.
testModule
.
consumingPush
.
read
();
...
...
@@ -403,16 +568,19 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(testTriggerTransferGroup, T, test_types) {
BOOST_CHECK_EQUAL
(
app
.
testModule
.
consumingPush3
,
33
);
// prepare a second transfer
dev
.
write
(
"/REG1"
,
12
);
dev
.
write
(
"/REG2"
,
23
);
dev
.
write
(
"/REG3"
,
34
);
dev
.
write
(
"/REG1
.DUMMY_WRITEABLE
"
,
12
);
dev
.
write
(
"/REG2
.DUMMY_WRITEABLE
"
,
23
);
dev
.
write
(
"/REG3
.DUMMY_WRITEABLE
"
,
34
);
// trigger the transfer
app
.
testModule
.
theTrigger
.
write
();
CHECK_TIMEOUT
(
backend
->
numberOfTransfers
==
3
,
10000
);
BOOST_CHECK
(
backend
->
last_bar
==
0
);
BOOST_CHECK
(
backend
->
last_address
==
0
);
BOOST_CHECK
(
backend
->
last_sizeInBytes
==
12
);
BOOST_TEST
(
backend
->
last_bar
==
0
);
BOOST_TEST
(
backend
->
last_address
==
0
);
// We only explicitly connect the three registers in the app, but the connection code will also connect the other
// registers into the CS, hence we need to check for the full size
BOOST_TEST
(
backend
->
last_sizeInBytes
==
32
);
// check result
app
.
testModule
.
consumingPush
.
read
();
...
...
This diff is collapsed.
Click to expand it.
Preview
0%
Loading
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Save comment
Cancel
Please
register
or
sign in
to comment