Compare commits
418 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d776f6eaf0 | ||
|
|
706ef01782 | ||
|
|
00c62cbd67 | ||
|
|
da8f3d6cc7 | ||
|
|
0bc95e51c2 | ||
|
|
727153e965 | ||
|
|
d00f54228d | ||
|
|
77c67802a3 | ||
|
|
7d68d177d7 | ||
|
|
9b20505dcd | ||
|
|
5f93e292b2 | ||
|
|
edd3e20f3c | ||
|
|
90cffa60d6 | ||
|
|
6171cd6867 | ||
|
|
fa731bf6c3 | ||
|
|
818fce324c | ||
|
|
1bc867e48d | ||
|
|
434b9f7a9f | ||
|
|
f54602dead | ||
|
|
fb546b41c1 | ||
|
|
e400d9f5fd | ||
|
|
f0fa8a2481 | ||
|
|
c3b0b49e3f | ||
|
|
45265b4f9b | ||
|
|
aa87a2a23d | ||
|
|
5a59b1da75 | ||
|
|
32aa0dd72f | ||
|
|
c1188b16a1 | ||
|
|
a02a60c658 | ||
| c5f9f5a2dc | |||
|
|
342b1bc8ef | ||
|
|
64158376f5 | ||
|
|
850d4ff056 | ||
|
|
f0cfe1c85a | ||
|
|
c8b615b3ee | ||
|
|
c67fdafb43 | ||
|
|
f66d277918 | ||
|
|
4ef7db20f8 | ||
|
|
e1216dfa76 | ||
|
|
340fa8a7cb | ||
|
|
a029455466 | ||
|
|
27f78c430b | ||
|
|
3d707e5e95 | ||
|
|
0406a2f614 | ||
|
|
a1c0e432ee | ||
|
|
57e57d9e43 | ||
|
|
e4e4188eaf | ||
|
|
f1553cc90e | ||
|
|
997e68c99a | ||
|
|
25663d9a7b | ||
|
|
1e55266396 | ||
|
|
810ae15991 | ||
|
|
271fec7f5e | ||
|
|
c43486791e | ||
|
|
499c03265f | ||
|
|
7eaa613d4d | ||
|
|
19db72031c | ||
|
|
671f9cca4b | ||
|
|
9ecdb80534 | ||
|
|
e973422ee1 | ||
|
|
8093c25b72 | ||
|
|
a7c9c620dd | ||
|
|
1e1d94ed73 | ||
|
|
7b8ef390ce | ||
|
|
87ade13234 | ||
|
|
32abde7f19 | ||
|
|
fe413af177 | ||
|
|
b4cd026fe5 | ||
|
|
a51b308cc8 | ||
|
|
1c09b42951 | ||
|
|
e42bb46563 | ||
|
|
a7788f9847 | ||
|
|
b597364419 | ||
|
|
a9a951d970 | ||
|
|
6ac879ec6a | ||
|
|
2422ef50b6 | ||
|
|
06dbf96b65 | ||
|
|
172046e78f | ||
|
|
7e8c49f0a0 | ||
|
|
f2ad6292f5 | ||
|
|
337e13b72e | ||
|
|
2ab2fc62dc | ||
|
|
786575c3de | ||
|
|
4cca194000 | ||
|
|
cd3ead0028 | ||
|
|
a239b95ca1 | ||
|
|
09574c0e82 | ||
|
|
0b6b01ef83 | ||
|
|
34145e459b | ||
|
|
207efca15c | ||
|
|
3e25c2ea46 | ||
|
|
2046678caa | ||
|
|
cb7e4e858b | ||
|
|
43ee4b9cb6 | ||
|
|
207c24a4fd | ||
|
|
6465ab3b6d | ||
|
|
cf624bc679 | ||
|
|
df55a776c7 | ||
|
|
07afe3887b | ||
|
|
25434ba84f | ||
|
|
9787dbd14f | ||
|
|
fd9081c80e | ||
|
|
e79c49019d | ||
|
|
5bc081a3af | ||
|
|
5976eb5186 | ||
|
|
b194bc05b1 | ||
|
|
18207fd79e | ||
|
|
7c1e0a51eb | ||
|
|
7196658166 | ||
|
|
187fe67ffa | ||
|
|
7136098c3c | ||
|
|
7979238029 | ||
|
|
fc38dff3b0 | ||
|
|
c590204cf9 | ||
|
|
284e49c807 | ||
|
|
43fcd3d1e2 | ||
|
|
a9f2d7df40 | ||
|
|
21a03d2b85 | ||
|
|
f123b8654a | ||
|
|
51cbe538e8 | ||
|
|
e247a2c4eb | ||
|
|
490b6684ac | ||
|
|
6fdeadf171 | ||
|
|
a88d491012 | ||
|
|
fd34d68933 | ||
|
|
215e3aab7b | ||
|
|
cd4feb3bab | ||
|
|
6b0af421dd | ||
|
|
9aeb4f2a96 | ||
|
|
9b1e789e62 | ||
|
|
406b163bcc | ||
|
|
6f2cae95e1 | ||
|
|
594a29b2db | ||
|
|
0d12464e30 | ||
|
|
ccd9ab70ee | ||
|
|
27f2f87e29 | ||
|
|
635eb9d36d | ||
|
|
f3e7f9bb8f | ||
|
|
2f69665056 | ||
|
|
559f7bc1b7 | ||
|
|
693f00caf5 | ||
|
|
822173979c | ||
|
|
9bce66f307 | ||
|
|
7a71e758b1 | ||
|
|
72fe0ca3e7 | ||
|
|
fd0570f0c9 | ||
|
|
e0037a0c8b | ||
|
|
c7c83282ee | ||
|
|
8f98d9792b | ||
|
|
111f7bd15e | ||
|
|
787af8de18 | ||
|
|
db6ebfe71b | ||
|
|
1cb490039f | ||
|
|
a152a64f1c | ||
|
|
08f50e56ac | ||
|
|
9ae221ca0c | ||
|
|
aca8da5891 | ||
|
|
34896560ea | ||
|
|
3597fbe382 | ||
|
|
0b262baf97 | ||
|
|
78b51ebe59 | ||
|
|
08a92468fe | ||
|
|
b84ed964f9 | ||
|
|
8bfe7b6b9d | ||
|
|
dbae173399 | ||
|
|
4d4dbcda4d | ||
|
|
fd4584a49d | ||
|
|
ceb9f795cb | ||
|
|
443c254d46 | ||
|
|
87fa150ced | ||
|
|
4cfdb8233f | ||
|
|
5e42cf76eb | ||
|
|
dccf6193da | ||
|
|
ee4fdf3f39 | ||
|
|
918b7f96db | ||
|
|
ef55345665 | ||
|
|
0a41dbb443 | ||
|
|
934ad32e52 | ||
|
|
b582f0f880 | ||
|
|
face3de44a | ||
|
|
8a7b9d776f | ||
|
|
a8c5d1095d | ||
|
|
9fa5028f6c | ||
|
|
0de3c089d2 | ||
|
|
919bc0138a | ||
|
|
a0210af5c6 | ||
|
|
888291db9a | ||
|
|
4fce663795 | ||
|
|
fc3384fb95 | ||
|
|
f9a30ca08e | ||
|
|
cbbe691f70 | ||
|
|
afbf0809c3 | ||
|
|
db8d4b4347 | ||
|
|
904ee7dfec | ||
|
|
bef616632c | ||
|
|
1d2e5d182e | ||
|
|
568ee1fa85 | ||
|
|
66633a7728 | ||
|
|
18283b44b2 | ||
|
|
278696b28e | ||
|
|
26efd09d45 | ||
|
|
ec9aba79ae | ||
|
|
07e42d81f4 | ||
|
|
d272afc128 | ||
|
|
6cf9fa2208 | ||
|
|
a72451cdbe | ||
|
|
e82489b158 | ||
|
|
e4c7fa6a1c | ||
|
|
48ed24dabf | ||
|
|
9ef060a2f2 | ||
|
|
255c41607f | ||
|
|
22da026888 | ||
|
|
def0a63008 | ||
|
|
6e62e123e8 | ||
|
|
262df56024 | ||
|
|
81cdf071a8 | ||
|
|
c5ce75888e | ||
|
|
fb232896a8 | ||
|
|
5efa462f19 | ||
|
|
225f3ab125 | ||
|
|
664fbfeb6e | ||
|
|
66f8ca0501 | ||
|
|
e977d63f08 | ||
|
|
08fc3cab38 | ||
|
|
a01885536c | ||
|
|
f7343674ee | ||
|
|
fc2adf98ba | ||
|
|
a3c57a5077 | ||
|
|
28b5dd0163 | ||
|
|
c0b69e4e6f | ||
|
|
a9111d78d3 | ||
|
|
cc91e22038 | ||
|
|
4555f69733 | ||
|
|
da8ba56dd1 | ||
|
|
4d6d5620b0 | ||
|
|
f7cad98f3e | ||
|
|
d978c4c3bf | ||
|
|
1c4b7810b1 | ||
|
|
a6ec43f81c | ||
|
|
d258acfc49 | ||
|
|
d9072402db | ||
|
|
1a706e3842 | ||
|
|
a227897504 | ||
|
|
0833d68e91 | ||
|
|
99bab6796c | ||
|
|
0544187057 | ||
|
|
e12e7b4d76 | ||
|
|
7609ac6f1e | ||
|
|
3c946a91e8 | ||
|
|
2389ebd87e | ||
|
|
90ad497a6f | ||
|
|
1bb0b6fe03 | ||
|
|
c16c1df6fd | ||
|
|
e460641711 | ||
|
|
1e980651a9 | ||
|
|
028076e79c | ||
|
|
e5b6a88551 | ||
|
|
b18b4f236f | ||
|
|
605d4e99b9 | ||
|
|
ad00b6465a | ||
|
|
537ebd05f2 | ||
|
|
2ee8769752 | ||
|
|
8d7f534d54 | ||
|
|
47bb62b051 | ||
|
|
50b8213781 | ||
|
|
fd1fe53b49 | ||
|
|
378def0a58 | ||
|
|
ed5f48b353 | ||
|
|
fa6c2c7683 | ||
|
|
3fadc9b481 | ||
|
|
c3d7fa0d26 | ||
|
|
5c16357fe2 | ||
|
|
ef2e6079ba | ||
|
|
18633288fb | ||
|
|
67daef7bcc | ||
|
|
5eb29dcfc4 | ||
|
|
d2fc922ee7 | ||
|
|
d35010c1cb | ||
|
|
bd4b65225c | ||
|
|
be95a62965 | ||
|
|
4336d524af | ||
|
|
ae7976fc01 | ||
|
|
72f9dc4c7c | ||
|
|
84760648fe | ||
|
|
b0df40d9a6 | ||
|
|
139217914d | ||
|
|
efa5795193 | ||
|
|
4f499aed01 | ||
|
|
a8ba831f5e | ||
|
|
a90405c25a | ||
|
|
4546fda8e9 | ||
|
|
b02f771146 | ||
|
|
dc94b26e50 | ||
|
|
4c32f37ede | ||
|
|
336a8b3bc2 | ||
|
|
faecea39c8 | ||
|
|
d08d5362be | ||
|
|
d4292d81f2 | ||
|
|
62893e33e9 | ||
|
|
5b07ecbd01 | ||
|
|
b5c1b9178d | ||
|
|
65ff7ab1c3 | ||
|
|
35fd991fdc | ||
|
|
57e1acba79 | ||
|
|
620d351946 | ||
|
|
de3c2656ef | ||
|
|
3b2e9b2485 | ||
|
|
75a3005d74 | ||
|
|
448f606054 | ||
|
|
e35c6f29fb | ||
|
|
85a1a48b00 | ||
|
|
45427d3202 | ||
|
|
93c7a05dac | ||
|
|
a34c38c9b9 | ||
|
|
3100b77a1a | ||
|
|
c8429069a3 | ||
|
|
01172217dc | ||
|
|
14b0e409f2 | ||
|
|
2107bae8dd | ||
|
|
433676226c | ||
|
|
f0c88234a0 | ||
|
|
393d711e5f | ||
|
|
cb24bd9c2c | ||
|
|
abc5c5a374 | ||
|
|
54c94f181a | ||
|
|
6641e7f5d1 | ||
|
|
f4a00f2b0f | ||
|
|
05d41f81e4 | ||
|
|
fdc289d888 | ||
|
|
0a2243e033 | ||
|
|
9d877d764f | ||
|
|
f5df29cf34 | ||
|
|
8008823ea5 | ||
|
|
6515de4bc0 | ||
|
|
8c5f535b79 | ||
|
|
3714be4f16 | ||
|
|
f24f565e58 | ||
|
|
036186fc12 | ||
|
|
d3853bd4e9 | ||
|
|
7253bac48a | ||
|
|
973fbeeba5 | ||
|
|
f22cefba97 | ||
|
|
a5abc37b17 | ||
|
|
b95fa7be56 | ||
|
|
f6cf87a52d | ||
|
|
51f36b2281 | ||
|
|
ddebd494c6 | ||
|
|
d0a8d2b1cd | ||
|
|
1731a0daf6 | ||
|
|
3487b3ee9e | ||
|
|
5834efb709 | ||
|
|
52bc6d060d | ||
|
|
35ca5f3aed | ||
|
|
ac2b6ea8db | ||
|
|
9827caa3e3 | ||
|
|
ebe2d6196c | ||
|
|
e4689dd3f8 | ||
|
|
1dba611b8e | ||
|
|
97cbea6f4d | ||
|
|
d9963b0631 | ||
|
|
8418303ce2 | ||
|
|
40952df965 | ||
|
|
4f2c51c480 | ||
|
|
fd31a0d6b0 | ||
|
|
9ad725a272 | ||
|
|
2b6172ba63 | ||
|
|
c521f9299d | ||
|
|
1e646f8df1 | ||
|
|
b5a436c1b7 | ||
|
|
4f25c7a3ea | ||
|
|
eadb8ff65b | ||
|
|
6a80e941a0 | ||
|
|
7fc9b42b3a | ||
|
|
0d857999bf | ||
|
|
8ba71b048e | ||
|
|
e07f6c1703 | ||
|
|
e4f38121d2 | ||
|
|
255ee3f151 | ||
|
|
e72dbaabe1 | ||
|
|
b39662450f | ||
|
|
98da0c0bec | ||
|
|
45dde325fd | ||
|
|
2967a8f798 | ||
|
|
d4bdc73948 | ||
|
|
37c2f6bb17 | ||
|
|
a8e4d749e3 | ||
|
|
3df16ee449 | ||
|
|
332c2f959b | ||
|
|
a1b89c9a3b | ||
|
|
d6921fdac0 | ||
|
|
4cb3c22221 | ||
|
|
cc1536b6e1 | ||
|
|
92a178cbf9 | ||
|
|
fbebce0a49 | ||
|
|
b13bb9819a | ||
|
|
bb505b8ed9 | ||
|
|
cf030bc711 | ||
|
|
6cb95c5cfc | ||
|
|
a277a4fdd5 | ||
|
|
388799d39d | ||
|
|
4acf7edf95 | ||
|
|
e39346d51e | ||
|
|
5ae6de4f1d | ||
|
|
e0f1427af7 | ||
|
|
69297bdb58 | ||
|
|
0fa2f2c2ff | ||
|
|
9efce46fff | ||
|
|
c356ecb402 | ||
|
|
4c6887e7ec | ||
|
|
6d338cab15 | ||
|
|
354fdd412f | ||
|
|
f3f6141e6a | ||
|
|
f2b43b704c | ||
|
|
de1478d7ba | ||
|
|
b9592eeb8c | ||
|
|
6a62f9c082 | ||
|
|
2e4a8b2e23 | ||
|
|
c5112ffa11 |
10
.ci/travis-build.sh
Executable file
10
.ci/travis-build.sh
Executable file
@@ -0,0 +1,10 @@
|
||||
#!/bin/sh
|
||||
set -e -x
|
||||
|
||||
make -j2
|
||||
|
||||
if [ "$TEST" != "NO" ]
|
||||
then
|
||||
make -j2 tapfiles
|
||||
make -j2 -s test-results
|
||||
fi
|
||||
83
.ci/travis-prepare.sh
Executable file
83
.ci/travis-prepare.sh
Executable file
@@ -0,0 +1,83 @@
|
||||
#!/bin/sh
|
||||
set -e -x
|
||||
|
||||
cat << EOF > configure/RELEASE.local
|
||||
EPICS_BASE=$HOME/.source/epics-base
|
||||
EOF
|
||||
|
||||
install -d "$HOME/.source"
|
||||
cd "$HOME/.source"
|
||||
|
||||
git clone --quiet --depth 5 --branch "$BRBASE" https://github.com/${REPOBASE:-epics-base}/epics-base.git epics-base
|
||||
(cd epics-base && git log -n1 )
|
||||
|
||||
EPICS_HOST_ARCH=`sh epics-base/startup/EpicsHostArch`
|
||||
|
||||
# requires wine and g++-mingw-w64-i686
|
||||
if [ "$WINE" = "32" ]
|
||||
then
|
||||
echo "Cross mingw32"
|
||||
sed -i -e '/CMPLR_PREFIX/d' epics-base/configure/os/CONFIG_SITE.linux-x86.win32-x86-mingw
|
||||
cat << EOF >> epics-base/configure/os/CONFIG_SITE.linux-x86.win32-x86-mingw
|
||||
CMPLR_PREFIX=i686-w64-mingw32-
|
||||
EOF
|
||||
cat << EOF >> epics-base/configure/CONFIG_SITE
|
||||
CROSS_COMPILER_TARGET_ARCHS+=win32-x86-mingw
|
||||
EOF
|
||||
fi
|
||||
|
||||
if [ "$STATIC" = "YES" ]
|
||||
then
|
||||
echo "Build static libraries/executables"
|
||||
cat << EOF >> epics-base/configure/CONFIG_SITE
|
||||
SHARED_LIBRARIES=NO
|
||||
STATIC_BUILD=YES
|
||||
EOF
|
||||
fi
|
||||
|
||||
case "$CMPLR" in
|
||||
clang)
|
||||
echo "Host compiler is clang"
|
||||
cat << EOF >> epics-base/configure/os/CONFIG_SITE.Common.$EPICS_HOST_ARCH
|
||||
GNU = NO
|
||||
CMPLR_CLASS = clang
|
||||
CC = clang
|
||||
CCC = clang++
|
||||
EOF
|
||||
|
||||
# hack
|
||||
sed -i -e 's/CMPLR_CLASS = gcc/CMPLR_CLASS = clang/' epics-base/configure/CONFIG.gnuCommon
|
||||
|
||||
clang --version
|
||||
;;
|
||||
*)
|
||||
echo "Host compiler is default"
|
||||
gcc --version
|
||||
;;
|
||||
esac
|
||||
|
||||
cat <<EOF >> epics-base/configure/CONFIG_SITE
|
||||
USR_CPPFLAGS += $USR_CPPFLAGS
|
||||
USR_CFLAGS += $USR_CFLAGS
|
||||
USR_CXXFLAGS += $USR_CXXFLAGS
|
||||
EOF
|
||||
|
||||
# set RTEMS to eg. "4.9" or "4.10"
|
||||
# requires qemu, bison, flex, texinfo, install-info
|
||||
if [ -n "$RTEMS" ]
|
||||
then
|
||||
echo "Cross RTEMS${RTEMS} for pc386"
|
||||
curl -L "https://github.com/mdavidsaver/rsb/releases/download/20171203-${RTEMS}/i386-rtems${RTEMS}-trusty-20171203-${RTEMS}.tar.bz2" \
|
||||
| tar -C / -xmj
|
||||
|
||||
sed -i -e '/^RTEMS_VERSION/d' -e '/^RTEMS_BASE/d' epics-base/configure/os/CONFIG_SITE.Common.RTEMS
|
||||
cat << EOF >> epics-base/configure/os/CONFIG_SITE.Common.RTEMS
|
||||
RTEMS_VERSION=$RTEMS
|
||||
RTEMS_BASE=$HOME/.rtems
|
||||
EOF
|
||||
cat << EOF >> epics-base/configure/CONFIG_SITE
|
||||
CROSS_COMPILER_TARGET_ARCHS += RTEMS-pc386-qemu
|
||||
EOF
|
||||
fi
|
||||
|
||||
make -j2 -C epics-base $EXTRA
|
||||
17
.gitignore
vendored
Normal file
17
.gitignore
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
/cfg/
|
||||
/bin/
|
||||
/lib/
|
||||
/db/
|
||||
/dbd/
|
||||
/html/
|
||||
/include/
|
||||
/templates/
|
||||
/configure/*.local
|
||||
/configure/RELEASE.*
|
||||
/configure/CONFIG_SITE.*
|
||||
O.*/
|
||||
/QtC-*
|
||||
envPaths
|
||||
*.orig
|
||||
*.log
|
||||
.*.swp
|
||||
8
.hgflow
8
.hgflow
@@ -1,8 +0,0 @@
|
||||
[branchname]
|
||||
master = master
|
||||
develop = default
|
||||
feature = feature/
|
||||
release = release/
|
||||
hotfix = hotfix/
|
||||
support = support/
|
||||
|
||||
12
.hgignore
12
.hgignore
@@ -1,12 +0,0 @@
|
||||
QtC-
|
||||
bin/
|
||||
lib/
|
||||
doc/
|
||||
include/
|
||||
db/
|
||||
dbd/
|
||||
documentation/html
|
||||
documentation/*.tag
|
||||
envPaths
|
||||
configure/.*\.local
|
||||
/O\..*
|
||||
18
.hgtags
18
.hgtags
@@ -1,18 +0,0 @@
|
||||
459f10877e5628241704f31437b4cbd342df0798 test1
|
||||
6e8a22d01e824702088195c08bf50bfb6f293de5 1.0-BETA
|
||||
d29d84f4c3f389f2accd497185b106c8541f95c9 1.1-SNAPSHOT
|
||||
a29729ca0ecd60b66f2d997031d97911377e44a7 marchtest
|
||||
9c59737f56e71aef641b70d0f72aa768fd7f8414 1.0.1-BETA
|
||||
4559c3de0cb4e3420e26272817f58bab005063ec 1.1-BETA
|
||||
d70c5ad29163306f50979a95b5aebbe9a93cfe76 2.0-BETA
|
||||
4cecd4b200f88ab57bbb81978c45df2a67bbece1 3.0-pre1
|
||||
4cecd4b200f88ab57bbb81978c45df2a67bbece1 3.0.0
|
||||
2a289ff41e2ed3a0247877306c2db7b266f3b6b8 3.0.1
|
||||
58092712d092ee521d1e1c8fa596a67f7d113ee9 3.0.2
|
||||
40b681ffc5cd609320e3f8ffc8eb6aa3bfdfbf19 before_merge_changesAfter3_0_2
|
||||
260f35b9c6cad113f242c83c89be9cdac802f610 3.1.0
|
||||
1348c22b125861ecb9da95b23f20314b167ee155 4.0.0
|
||||
1348c22b125861ecb9da95b23f20314b167ee155 4.0.0
|
||||
9c62aaa83b9db6ad69740a6bb46d6529e0e60b78 4.0.0
|
||||
9c62aaa83b9db6ad69740a6bb46d6529e0e60b78 4.0.0
|
||||
f9f187685032ebf4b108c759be196fda055c9e42 4.0.0
|
||||
30
.travis.yml
Normal file
30
.travis.yml
Normal file
@@ -0,0 +1,30 @@
|
||||
sudo: false
|
||||
dist: trusty
|
||||
language: c++
|
||||
compiler:
|
||||
- gcc
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- libreadline6-dev
|
||||
- libncurses5-dev
|
||||
- perl
|
||||
- clang
|
||||
- g++-mingw-w64-i686
|
||||
- qemu-system-x86
|
||||
install:
|
||||
- ./.ci/travis-prepare.sh
|
||||
script:
|
||||
- ./.ci/travis-build.sh
|
||||
env:
|
||||
- BRBASE=7.0 CMPLR=gcc
|
||||
- BRBASE=7.0 CMPLR=clang
|
||||
- BRBASE=7.0 USR_CXXFLAGS=-std=c++11
|
||||
- BRBASE=7.0 CMPLR=clang USR_CXXFLAGS=-std=c++11
|
||||
- BRBASE=7.0 WINE=32 TEST=NO STATIC=YES
|
||||
- BRBASE=7.0 WINE=32 TEST=NO STATIC=NO
|
||||
- BRBASE=7.0 RTEMS=4.10 TEST=NO
|
||||
- BRBASE=7.0 RTEMS=4.9 TEST=NO
|
||||
- BRBASE=3.16
|
||||
- BRBASE=3.15
|
||||
- BRBASE=3.14
|
||||
12
COPYRIGHT
12
COPYRIGHT
@@ -1,12 +0,0 @@
|
||||
/****************************************************
|
||||
Copyright (c) 2008 All rights reserved
|
||||
Copyright (c) 2008 Martin R. Kraimer
|
||||
Copyright (c) 2006 The University of Chicago, as Operator of Argonne
|
||||
National Laboratory.
|
||||
Deutsches Elektronen-Synchroton, Member of the Helmholtz Association,
|
||||
(DESY), HAMBURG, GERMANY,
|
||||
BERLINER SPEICHERRING GESELLSCHAFT FUER SYNCHROTRONSTRAHLUNG M.B.H.
|
||||
(BESSY), BERLIN, GERMANY.
|
||||
COSYLAB (Control System Laboratory)
|
||||
(Cosylab) Ljubljana Slovenia
|
||||
*************************************************** */
|
||||
75
LICENSE
75
LICENSE
@@ -1,12 +1,17 @@
|
||||
Copyright and License Terms
|
||||
---------------------------
|
||||
|
||||
Copyright (c) 2008 Martin R. Kraimer
|
||||
Copyright (c) 2006 The University of Chicago, as Operator of Argonne
|
||||
Copyright (c) 2006-2016 Martin R. Kraimer
|
||||
Copyright (c) 2006-2016 UChicago Argonne LLC, as Operator of Argonne
|
||||
National Laboratory.
|
||||
Copyright (c) 2006 Deutsches Elektronen-Synchrotron,
|
||||
Member of the Helmholtz Association, (DESY), HAMBURG, GERMANY.
|
||||
Copyright (c) 2007 Control System Laboratory,
|
||||
Copyright (c) 2007-2016 Control System Laboratory,
|
||||
(COSYLAB) Ljubljana Slovenia
|
||||
|
||||
Copyright (c) 2010-2016 Brookhaven Science Associates, as Operator
|
||||
of Brookhaven National Laboratory
|
||||
Copyright (c) 2011-2016 Diamond Light Source Limited,
|
||||
(DLS) Didcot, United Kingdom
|
||||
|
||||
Permission is hereby granted, free of charge, to any person
|
||||
obtaining a copy of this software and associated documentation
|
||||
@@ -31,48 +36,30 @@ OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
________________________________________________________________________
|
||||
|
||||
This software is in part copyrighted by the University of Chicago (UofC)
|
||||
Additional Disclaimers
|
||||
----------------------
|
||||
|
||||
In no event shall UofC be liable to any party for direct, indirect,
|
||||
special, incidental, or consequential damages arising out of the use of
|
||||
this software, its documentation, or any derivatives thereof, even if
|
||||
UofC has been advised of the possibility of such damage.
|
||||
This software is copyright in part by these institutions:
|
||||
|
||||
UofC specifically disclaims any warranties, including, but not limited
|
||||
to, the implied warranties of merchantability, fitness for a particular
|
||||
purpose, and non-infringement. This software is provided on an "as is"
|
||||
basis, and UofC has no obligation to provide maintenance, support,
|
||||
updates, enhancements, or modifications.
|
||||
* Brookhaven Science Associates, as Operator of Brookhaven
|
||||
National Laboratory, New York, USA
|
||||
* Control System Laboratory, Ljubljana, Slovenia
|
||||
* Deutsches Elektronen-Synchroton, Member of the Helmholtz
|
||||
Association, Hamburg, Germany
|
||||
* Diamond Light Source Limited, Didcot, United Kingdom
|
||||
* Helmholtz-Zentrum Berlin fuer Materialien und Energie m.b.H.,
|
||||
Berlin, Germany.
|
||||
* UChicage Argonne LLC, as Operator of Argonne National Laboratory,
|
||||
Illinois, USA
|
||||
|
||||
________________________________________________________________________
|
||||
In no event shall these institutions be liable to any party for direct,
|
||||
indirect, special, incidental, or consequential damages arising out of
|
||||
the use of this software, its documentation, or any derivatives thereof,
|
||||
even if advised of the possibility of such damage.
|
||||
|
||||
This software is in part copyrighted by the BERLINER SPEICHERRING
|
||||
GESELLSCHAFT FUER SYNCHROTRONSTRAHLUNG M.B.H. (BESSY), BERLIN, GERMANY.
|
||||
These institutions specifically disclaim any warranties, including, but
|
||||
not limited to, the implied warranties of merchantability, fitness for a
|
||||
particular purpose, and non-infringement. This software is provided on
|
||||
an "as is" basis, and these institutions have no obligation to provide
|
||||
maintenance, support, updates, enhancements, or modifications.
|
||||
|
||||
In no event shall BESSY be liable to any party for direct, indirect,
|
||||
special, incidental, or consequential damages arising out of the use of
|
||||
this software, its documentation, or any derivatives thereof, even if
|
||||
BESSY has been advised of the possibility of such damage.
|
||||
|
||||
BESSY specifically disclaims any warranties, including, but not limited
|
||||
to, the implied warranties of merchantability, fitness for a particular
|
||||
purpose, and non-infringement. This software is provided on an "as is"
|
||||
basis, and BESSY has no obligation to provide maintenance, support,
|
||||
updates, enhancements, or modifications.
|
||||
|
||||
________________________________________________________________________
|
||||
|
||||
This software is in part copyrighted by the Deutsches Elektronen-Synchroton,
|
||||
Member of the Helmholtz Association, (DESY), HAMBURG, GERMANY.
|
||||
|
||||
In no event shall DESY be liable to any party for direct, indirect,
|
||||
special, incidental, or consequential damages arising out of the use of
|
||||
this software, its documentation, or any derivatives thereof, even if
|
||||
DESY has been advised of the possibility of such damage.
|
||||
|
||||
DESY specifically disclaims any warranties, including, but not limited
|
||||
to, the implied warranties of merchantability, fitness for a particular
|
||||
purpose, and non-infringement. This software is provided on an "as is"
|
||||
basis, and DESY has no obligation to provide maintenance, support,
|
||||
updates, enhancements, or modifications.
|
||||
________________________________________________________________________
|
||||
|
||||
3
Makefile
3
Makefile
@@ -11,4 +11,7 @@ src_DEPEND_DIRS = configure
|
||||
DIRS += testApp
|
||||
testApp_DEPEND_DIRS = src
|
||||
|
||||
DIRS += examples
|
||||
examples_DEPEND_DIRS = src
|
||||
|
||||
include $(TOP)/configure/RULES_TOP
|
||||
|
||||
82
README.html
82
README.html
@@ -1,82 +0,0 @@
|
||||
<?xml version="1.0" encoding="iso-8859-1"?>
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
|
||||
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<meta http-equiv="content-type" content="text/html; charset=iso-8859-1" />
|
||||
<title>EPICS pvData C++</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h1 style="text-align: center">EPICS pvData C++<br />
|
||||
Overview<br />
|
||||
2010.08.10</h1>
|
||||
CONTENTS
|
||||
<hr />
|
||||
|
||||
<h2 style="text-align: center">Introduction</h2>
|
||||
<hr />
|
||||
|
||||
<p>This project has the begining of the C++ implementation of pvData. The
|
||||
following is done:</p>
|
||||
<dl>
|
||||
<dt>introspection interfaces</dt>
|
||||
<dd>The introspection interfaces for clients are described.</dd>
|
||||
<dt>introspection implementation</dt>
|
||||
<dd>The following have been implemented: Type, ScalarType, Field,
|
||||
Scalar</dd>
|
||||
<dt>test</dt>
|
||||
<dd>A test of Scalar.</dd>
|
||||
<dt>As mentioned below there are problems with the current
|
||||
implementation.</dt>
|
||||
</dl>
|
||||
<hr />
|
||||
|
||||
<h2 style="text-align: center">Building</h2>
|
||||
<hr />
|
||||
|
||||
<p>The project is structured as an epics base client application. Edit
|
||||
configure/RELEASE so that it references your EPICS base and then just
|
||||
type:</p>
|
||||
<pre> make</pre>
|
||||
|
||||
<p>At the top. Then execute the test in the bin directory.</p>
|
||||
|
||||
<p>pvDataApp has the following sub directories:</p>
|
||||
<dl>
|
||||
<dt>pv</dt>
|
||||
<dd>pvData.h has the interface descriptions for client code.</dd>
|
||||
<dt>factory</dt>
|
||||
<dd>FieldCreateFactory.cpp has the current implementation</dd>
|
||||
<dt>test</dt>
|
||||
<dd>Has a test for the current implementation.</dd>
|
||||
</dl>
|
||||
<hr />
|
||||
|
||||
<h2 style="text-align: center">Questions about Classes</h2>
|
||||
<hr />
|
||||
|
||||
<p>The pure virtual classes defined in pvData.h now work. But there are still
|
||||
some things that are not so nice. Amoung these are:</p>
|
||||
<ul>
|
||||
<li>In FieldCreateFactory.cpp look for "WHY DO I". It asks why the derived
|
||||
class must also define methods defined in the base class. If it does not
|
||||
then a compilation error occurs. WHY??? </li>
|
||||
<li>The toString methods have an argument of "std::string &buf" instead
|
||||
of "std::string *". Does this seem correct?</li>
|
||||
<li>Can arguments and return descriptions be defined better?</li>
|
||||
<li>Is const present everywhere it should be? Remember that introspection
|
||||
classes are immutable.</li>
|
||||
<li>The code is NOT thread safe. When we decide for sure what thread/lock
|
||||
support to choose I will fix this.</li>
|
||||
</ul>
|
||||
|
||||
<p>HELP WILL BE GREATLY APPRECIATED. because I am still coming up to speed
|
||||
with c++</p>
|
||||
<hr />
|
||||
|
||||
<h2 style="text-align: center">Garbage Collection</h2>
|
||||
<hr />
|
||||
<p>Not yet implemented. Lets get class structure correct first.</p>
|
||||
</body>
|
||||
</html>
|
||||
16
README.md
Normal file
16
README.md
Normal file
@@ -0,0 +1,16 @@
|
||||
# pvaDataCPP
|
||||
|
||||
The EPICS **pvData** API provides a set of classes and utilities that form the core of the EPICS PVA implementation.
|
||||
|
||||
The pvDataCPP module is a part of the EPICS software toolkit that implements pvData structures as C++ class objects.
|
||||
|
||||
## Links
|
||||
|
||||
- General information about EPICS can be found at the
|
||||
[EPICS Controls website](https://epics-controls.org).
|
||||
- API documentation for this module can be found on its
|
||||
[Github Pages website](https://epics-base.github.io/pvDataCPP/).
|
||||
|
||||
## Building
|
||||
|
||||
This module is included as a submodule of a full EPICS 7 release and will be compiled during builds of that software.
|
||||
4
configure/CONFIG_PVDATA_VERSION
Normal file
4
configure/CONFIG_PVDATA_VERSION
Normal file
@@ -0,0 +1,4 @@
|
||||
EPICS_PVD_MAJOR_VERSION = 7
|
||||
EPICS_PVD_MINOR_VERSION = 1
|
||||
EPICS_PVD_MAINTENANCE_VERSION = 0
|
||||
EPICS_PVD_DEVELOPMENT_FLAG = 0
|
||||
@@ -20,8 +20,13 @@ CHECK_RELEASE = YES
|
||||
# INSTALL_LOCATION here.
|
||||
#INSTALL_LOCATION=</path/name/to/install/top>
|
||||
|
||||
USR_CPPFLAGS += -DPVD_INTERNAL
|
||||
|
||||
-include $(TOP)/../CONFIG_SITE.local
|
||||
-include $(TOP)/configure/CONFIG_SITE.local
|
||||
-include $(TOP)/../CONFIG.local
|
||||
|
||||
# MSVC - skip defining min()/max() macros
|
||||
USR_CPPFLAGS_WIN32 += -DNOMINMAX
|
||||
|
||||
ifdef WITH_COVERAGE
|
||||
USR_CPPFLAGS += --coverage
|
||||
|
||||
@@ -2,6 +2,8 @@ TOP=..
|
||||
|
||||
include $(TOP)/configure/CONFIG
|
||||
|
||||
CFG += CONFIG_PVDATA_VERSION
|
||||
|
||||
TARGETS = $(CONFIG_TARGETS)
|
||||
CONFIGS += $(subst ../,,$(wildcard $(CONFIG_INSTALLS)))
|
||||
|
||||
|
||||
@@ -1,25 +1,38 @@
|
||||
# pvDataCPP RELEASE - Location of external support modules
|
||||
# RELEASE - Location of external support modules
|
||||
#
|
||||
# IF YOU CHANGE this file or any file it includes you must
|
||||
# subsequently do a "gnumake rebuild" in the application's
|
||||
# top level directory.
|
||||
# IF YOU CHANGE ANY PATHS in this file or make API changes to
|
||||
# any modules it refers to, you should do a "make rebuild" in
|
||||
# this application's top level directory.
|
||||
#
|
||||
# The build process does not check dependencies against files
|
||||
# that are outside this application, thus you should also do a
|
||||
# "gnumake rebuild" in the top level directory after EPICS_BASE
|
||||
# or any other external module pointed to below is rebuilt.
|
||||
# The EPICS build process does not check dependencies against
|
||||
# any files from outside the application, so it is safest to
|
||||
# rebuild it completely if any modules it depends on change.
|
||||
#
|
||||
# Host- or target-specific settings can be given in files named
|
||||
# RELEASE.$(EPICS_HOST_ARCH).Common
|
||||
# RELEASE.Common.$(T_A)
|
||||
# RELEASE.$(EPICS_HOST_ARCH).$(T_A)
|
||||
|
||||
# EPICS V4 Developers: Do not edit the locations in this file!
|
||||
#
|
||||
# Create a file RELEASE.local pointing to your PVCOMMON
|
||||
# and EPICS_BASE build directories, e.g.
|
||||
# PVCOMMON = /home/install/epicsV4/pvCommonCPP
|
||||
# EPICS_BASE = /home/install/epics/base
|
||||
# This file is parsed by both GNUmake and an EPICS Perl script,
|
||||
# so it may ONLY contain definititions of paths to other support
|
||||
# modules, variable definitions that are used in module paths,
|
||||
# and include statements that pull in other RELEASE files.
|
||||
# Variables may be used before their values have been set.
|
||||
# Build variables that are NOT used in paths should be set in
|
||||
# the CONFIG_SITE file.
|
||||
|
||||
-include $(TOP)/configure/RELEASE.local
|
||||
# Variables and paths to dependent modules:
|
||||
#MODULES = /path/to/modules
|
||||
#MYMODULE = $(MODULES)/my-module
|
||||
|
||||
# If building the EPICS modules individually, set these:
|
||||
#EPICS_BASE = /path/to/base
|
||||
|
||||
# Set RULES here if you want to use build rules from elsewhere:
|
||||
#RULES = $(MODULES)/build-rules
|
||||
|
||||
# These allow developers to override the RELEASE variable settings
|
||||
# without having to modify the configure/RELEASE file itself.
|
||||
-include $(TOP)/../RELEASE.local
|
||||
-include $(TOP)/../RELEASE.$(EPICS_HOST_ARCH).local
|
||||
-include $(TOP)/configure/RELEASE.local
|
||||
|
||||
3
documentation/.gitignore
vendored
Normal file
3
documentation/.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
*.tag
|
||||
*.db
|
||||
html/
|
||||
File diff suppressed because it is too large
Load Diff
13
documentation/Makefile
Normal file
13
documentation/Makefile
Normal file
@@ -0,0 +1,13 @@
|
||||
all: gen
|
||||
|
||||
clean:
|
||||
rm -rf doxygen_sqlite3.db html
|
||||
|
||||
gen:
|
||||
doxygen
|
||||
|
||||
commit: gen
|
||||
touch html/.nojekyll
|
||||
./commit-gh.sh documentation/html/ html/.nojekyll html/*.* html/search/*.*
|
||||
|
||||
.PHONY: all clean gen commit
|
||||
@@ -1,58 +0,0 @@
|
||||
<h1>Release 4.0 IN DEVELOPMENT</h1>
|
||||
<p>The main changes since release 3.0.2 are:</p>
|
||||
<ul>
|
||||
<li>array semantics now enforce Copy On Write.</li>
|
||||
<li>String no longer defined.</li>
|
||||
<li>timeStamp and valueAlarm name changes</li>
|
||||
<li>toString replaced by stream I/O </li>
|
||||
<li>union is new type.</li>
|
||||
<li>copy is new.</li>
|
||||
<li>monitorPlugin is new.</li>
|
||||
</ul>
|
||||
<h2>New Semantics for Arrays</h2>
|
||||
<p>PVScalarArray, PVStructureArray, and PVUnionArray all enforce COW (Copy On Write) Semantics.
|
||||
In order to limit memory usage the storage for raw data is managed via a new shared_vector facility.
|
||||
This allows multiple instances of array data to use the shared raw data.
|
||||
COW is implemented via shared_vectors of const data, i. e. data that can not be modified.</p>
|
||||
<h2>String no longer defined</h2>
|
||||
<p>This is replaced by std::string.</p>
|
||||
<h2>timeStamp and valueAlarm name changes</h2>
|
||||
<p>In timeStamp nanoSeconds is changed to nanoseconds.</p>
|
||||
<p>In valueAlarm hystersis is changed to hysteresis</p>
|
||||
<h2>toString replaced by stream I/O</h2>
|
||||
<p>pvData.h and pvIntrospect no longer defines toString
|
||||
Instead they have stream support.
|
||||
pvIntrospect uses method dump and pvData uses dumpValue.
|
||||
For example:</p>
|
||||
<pre><code> PVDoublePtr pvValue;
|
||||
String buffer;
|
||||
pvValue->toString(&buffer);
|
||||
cout << buffer << endl;
|
||||
buffer.clear();
|
||||
pvValue->getField()->toString(&buffer);
|
||||
cout << buffer << evdl;
|
||||
</code></pre>
|
||||
<p>is replaced by</p>
|
||||
<pre><code> PVDoublePtr pvValue;
|
||||
cout << *pvValue << endl
|
||||
cout << *pvValue->getField() << endl;
|
||||
</code></pre>
|
||||
<h2>union is a new basic type.</h2>
|
||||
<p>There are two new basic types: union_t and unionArray.</p>
|
||||
<p>A union is like a structure that has a single subfield.
|
||||
There are two flavors:</p>
|
||||
<ul>
|
||||
<li><b>varient union</b> The field can have any type.</li>
|
||||
<li><b>union</b> The field can any of specified set of types.</li>
|
||||
</ul>
|
||||
<p>The field type can be dynamically changed.</p>
|
||||
<h2>copy</h2>
|
||||
<p>This consists of createRequest and pvCopy.
|
||||
createRequest was moved from pvAccess to here.
|
||||
pvCopy is moved from pvDatabaseCPP and now depends
|
||||
only on pvData, i. e. it no longer has any knowledge of PVRecord.</p>
|
||||
<h2>monitorPlugin</h2>
|
||||
<p>This is for is for use by code that implements pvAccess monitors.
|
||||
This is prototype and is subject to debate.</p>
|
||||
<h1>Release 3.0.2</h1>
|
||||
<p>This was the starting point for RELEASE_NOTES</p>
|
||||
@@ -1,89 +0,0 @@
|
||||
Release 4.0 IN DEVELOPMENT
|
||||
===========
|
||||
|
||||
The main changes since release 3.0.2 are:
|
||||
|
||||
* array semantics now enforce Copy On Write.
|
||||
* String no longer defined.
|
||||
* timeStamp and valueAlarm name changes
|
||||
* toString replaced by stream I/O
|
||||
* union is new type.
|
||||
* copy is new.
|
||||
* monitorPlugin is new.
|
||||
|
||||
New Semantics for Arrays
|
||||
--------
|
||||
|
||||
PVScalarArray, PVStructureArray, and PVUnionArray all enforce COW (Copy On Write) Semantics.
|
||||
In order to limit memory usage the storage for raw data is managed via a new shared_vector facility.
|
||||
This allows multiple instances of array data to use the shared raw data.
|
||||
COW is implemented via shared_vectors of const data, i. e. data that can not be modified.
|
||||
|
||||
|
||||
String no longer defined
|
||||
---------
|
||||
|
||||
This is replaced by std::string.
|
||||
|
||||
|
||||
timeStamp and valueAlarm name changes
|
||||
--------------
|
||||
|
||||
In timeStamp nanoSeconds is changed to nanoseconds.
|
||||
|
||||
In valueAlarm hystersis is changed to hysteresis
|
||||
|
||||
|
||||
toString replaced by stream I/O
|
||||
---------
|
||||
|
||||
pvData.h and pvIntrospect no longer defines toString
|
||||
Instead they have stream support.
|
||||
pvIntrospect uses method dump and pvData uses dumpValue.
|
||||
For example:
|
||||
|
||||
PVDoublePtr pvValue;
|
||||
String buffer;
|
||||
pvValue->toString(&buffer);
|
||||
cout << buffer << endl;
|
||||
buffer.clear();
|
||||
pvValue->getField()->toString(&buffer);
|
||||
cout << buffer << evdl;
|
||||
|
||||
is replaced by
|
||||
|
||||
PVDoublePtr pvValue;
|
||||
cout << *pvValue << endl
|
||||
cout << *pvValue->getField() << endl;
|
||||
|
||||
|
||||
union is a new basic type.
|
||||
------------
|
||||
|
||||
There are two new basic types: union_t and unionArray.
|
||||
|
||||
A union is like a structure that has a single subfield.
|
||||
There are two flavors:
|
||||
|
||||
* <b>varient union</b> The field can have any type.
|
||||
* <b>union</b> The field can any of specified set of types.
|
||||
|
||||
The field type can be dynamically changed.
|
||||
|
||||
copy
|
||||
----
|
||||
|
||||
This consists of createRequest and pvCopy.
|
||||
createRequest was moved from pvAccess to here.
|
||||
pvCopy is moved from pvDatabaseCPP and now depends
|
||||
only on pvData, i. e. it no longer has any knowledge of PVRecord.
|
||||
|
||||
monitorPlugin
|
||||
-------------
|
||||
|
||||
This is for is for use by code that implements pvAccess monitors.
|
||||
This is prototype and is subject to debate.
|
||||
|
||||
Release 3.0.2
|
||||
==========
|
||||
This was the starting point for RELEASE_NOTES
|
||||
@@ -1,17 +0,0 @@
|
||||
<h1>TODO</h1>
|
||||
<h2>printer</h2>
|
||||
<p>pv/printer.h is not used.</p>
|
||||
<h2>doxygen</h2>
|
||||
<p>There is a lot of public code that does not have doxygen tags.</p>
|
||||
<h2>postMonitor: PVUnion, PVUnionArray, and PVStructureArray</h2>
|
||||
<p>PVUnion, PVUnionArray, and PVStructureArray all have elements
|
||||
that are treated like a top level field.</p>
|
||||
<p>Currently if a subField of any of these is changed postMonitor is not called for the field itself.</p>
|
||||
<p>David asked if this could be changed so that it is called.
|
||||
Marty thinks this may not be a good idea.</p>
|
||||
<h2>valueAlarm</h2>
|
||||
<p>normativeTypes.html describes valueAlarm only for a value field that has type
|
||||
double.
|
||||
The implementation also supports all the numeric scalar types.</p>
|
||||
<h2>monitorPlugin</h2>
|
||||
<p>A debate is on-going about what semantics should be.</p>
|
||||
@@ -1,38 +1,14 @@
|
||||
TODO
|
||||
===========
|
||||
|
||||
printer
|
||||
------------
|
||||
|
||||
pv/printer.h is not used.
|
||||
|
||||
doxygen
|
||||
-------
|
||||
|
||||
There is a lot of public code that does not have doxygen tags.
|
||||
|
||||
|
||||
postMonitor: PVUnion, PVUnionArray, and PVStructureArray
|
||||
--------
|
||||
|
||||
PVUnion, PVUnionArray, and PVStructureArray all have elements
|
||||
that are treated like a top level field.
|
||||
|
||||
Currently if a subField of any of these is changed postMonitor is not called for the field itself.
|
||||
|
||||
David asked if this could be changed so that it is called.
|
||||
Marty thinks this may not be a good idea.
|
||||
|
||||
|
||||
valueAlarm
|
||||
---------
|
||||
|
||||
normativeTypes.html describes valueAlarm only for a value field that has type
|
||||
double.
|
||||
The implementation also supports all the numeric scalar types.
|
||||
|
||||
monitorPlugin
|
||||
-------------
|
||||
|
||||
A debate is on-going about what semantics should be.
|
||||
|
||||
|
||||
45
documentation/commit-gh.sh
Executable file
45
documentation/commit-gh.sh
Executable file
@@ -0,0 +1,45 @@
|
||||
#!/bin/sh
|
||||
set -e -x
|
||||
# Usage: commit-gh <sub-directory-prefix> <files...>
|
||||
#
|
||||
# Creates a commit containing only the files in the sub-directory provided as an argument
|
||||
#
|
||||
# Does not disturb the working copy or index
|
||||
|
||||
prefix="$1"
|
||||
shift
|
||||
|
||||
# Commit to this branch
|
||||
BRANCH=refs/heads/gh-pages
|
||||
|
||||
# Use the main branch description as the gh-pages commit message
|
||||
MSG=`git describe --tags --always`
|
||||
|
||||
# Scratch space
|
||||
TDIR=`mktemp -d -p $PWD`
|
||||
|
||||
# Automatic cleanup of scratch space
|
||||
trap 'rm -rf $TDIR' INT TERM QUIT EXIT
|
||||
|
||||
export GIT_INDEX_FILE="$TDIR/index"
|
||||
|
||||
# Add listed files to a new (empty) index
|
||||
git update-index --add "$@"
|
||||
|
||||
# Write the index into the repo, get tree hash
|
||||
TREE=`git write-tree --prefix="$prefix"`
|
||||
|
||||
echo "TREE $TREE"
|
||||
git cat-file -p $TREE
|
||||
|
||||
# Create a commit with our new tree
|
||||
# Reference current branch head as parent (if any)
|
||||
CMT=`git commit-tree -m "$MSG" $TREE`
|
||||
|
||||
echo "COMMIT $CMT"
|
||||
git cat-file -p $CMT
|
||||
|
||||
# Update the branch with the new commit tree hash
|
||||
git update-ref $BRANCH $CMT
|
||||
|
||||
echo "Done"
|
||||
@@ -1,679 +0,0 @@
|
||||
<?xml version="1.0" encoding="iso-8859-1"?>
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
|
||||
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<meta http-equiv="content-type" content="text/html; charset=iso-8859-1" />
|
||||
<title>EPICS pvDataCPP: copy and monitor</title>
|
||||
<link rel="stylesheet" type="text/css"
|
||||
href="http://epics-pvdata.sourceforge.net/base.css" />
|
||||
<link rel="stylesheet" type="text/css"
|
||||
href="http://epics-pvdata.sourceforge.net/epicsv4.css" />
|
||||
<style type="text/css">
|
||||
/*<![CDATA[*/
|
||||
.about { margin-left: 3em; margin-right: 3em; font-size: .83em}
|
||||
table { margin-left: auto; margin-right: auto }
|
||||
.diagram { text-align: center; margin: 2.5em 0 }
|
||||
span.opt { color: grey }
|
||||
span.nterm { font-style:italic }
|
||||
span.term { font-family:courier }
|
||||
span.user { font-family:courier }
|
||||
span.user:before { content:"<" }
|
||||
span.user:after { content:">" }
|
||||
.nonnorm { font-style:italic }
|
||||
p.ed { color: #AA0000 }
|
||||
span.ed { color: #AA0000 }
|
||||
p.ed.priv { display: inline; }
|
||||
span.ed.priv { display: inline; }
|
||||
/*]]>*/</style>
|
||||
<!-- Script that generates the Table of Contents -->
|
||||
<script type="text/javascript"
|
||||
src="http://epics-pvdata.sourceforge.net/script/tocgen.js">
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
|
||||
<div id="toc">
|
||||
<h2 class="nocount" style="page-break-before: always">Table of Contents</h2>
|
||||
</div>
|
||||
<div id="contents" class="contents">
|
||||
|
||||
<h2>support for copy and monitor</h2>
|
||||
<p><b>copy</b> and <b>monitor</b> are not used in this project.
|
||||
They are intended for use by pvAccess and by pvAccess servers.
|
||||
They are provided with this project because the code depends only on
|
||||
pvData itself.
|
||||
</p>
|
||||
<p>This document describes C++ specific code.
|
||||
<a href="http://epics-pvdata.sourceforge.net/informative/pvRequest.html">
|
||||
pvRequest.html</a>
|
||||
provides a language independent overview of <b>copy</b> and <b>monitor</b>.
|
||||
</p>
|
||||
<p>
|
||||
<b>NOTE:pvRequest.html</b> must be updated since it is based on an earlier version of pvCopy that
|
||||
had knowledge of PVRecord. The C++ version was implemented in pvDatabaseCPP
|
||||
and the Java version on pvIOCJava.
|
||||
At present only the C++ version of the new API for pvCopy is implemented.
|
||||
</p>
|
||||
<p>Copy provides:
|
||||
<dl>
|
||||
<dt>createRequest</dt>
|
||||
<dd>
|
||||
The Channel create methods in pvAccess all have an argument
|
||||
<b>PVStructure pvRequest</b>.<br />
|
||||
Given an ascii string createRequest creates a PVStructure that provides
|
||||
a pvData representation of the information from the ascii string.
|
||||
It is this structure that can be passed to the channel create methods.<br />
|
||||
The information in a pvRequest selects an arbitrary subset of the
|
||||
fields in a top level structure that resides in the server.
|
||||
In addition options can be specified. Both global and field specific
|
||||
options can be specified.
|
||||
</dd>
|
||||
<dt>pvCopy</dt>
|
||||
<dd>This is a facility used by channel providers.
|
||||
It provides client specific code that manages a copy of an arbitrary
|
||||
subset of the fields in a top level structure that resides in the
|
||||
provider. It also allows provider access to options specified
|
||||
by the client.
|
||||
</dd>
|
||||
</dl>
|
||||
Monitor provides:
|
||||
<dl>
|
||||
<dt>monitor</dt>
|
||||
<dd>This is support code for channel providers that implement channel
|
||||
monitor. It, together with the queue facility, provides support for
|
||||
monitor queues.
|
||||
</dd>
|
||||
<dt>monitorPlugin</dt>
|
||||
<dd>This is support for implementing monitor plugins.
|
||||
A monitor plugin can be developed that has no knowledge
|
||||
of pvAccess but only pvData.
|
||||
</dd>
|
||||
</dl>
|
||||
</p>
|
||||
|
||||
<h2>support for copy</h2>
|
||||
<p><b>copy</b> provides the ability to create a structure that has
|
||||
a copy of an arbitrary subset of the fields in an existing top level
|
||||
structure. In addition it allows global options and field specific options.
|
||||
It has two main components: <b>createRequest</b> and <b>pvCopy</b>.
|
||||
Given a string createRequest creates a pvRequest, which is a PVStructure
|
||||
that has the format expected by <b>pvCopy</b>.
|
||||
</p>
|
||||
|
||||
<h3>createRequest</h3>
|
||||
<p>This is mainly used by pvAccess clients. Given a request string it creates
|
||||
a pvRequest structure that can be passed to the pvAccess create methods.
|
||||
In turn pvAccess passes the pvRequest to a local channel provider which
|
||||
then passes it to pvCopy.
|
||||
</p>
|
||||
<p>The definition of the public members is:</p>
|
||||
<pre>
|
||||
class CreateRequest {
|
||||
...
|
||||
static CreateRequestPtr create();
|
||||
virtual PVStructurePtr createRequest(std::string const &request);
|
||||
std::string getMessage();
|
||||
};
|
||||
</pre>
|
||||
<p>An example of how it is used is:</p>
|
||||
<pre>
|
||||
CreateRequestPtr createRequest = CreateRequest::create();
|
||||
PVStructurePtr pvRequest = createRequest->createRequest(request);
|
||||
if(pvRequest==NULL) {
|
||||
std::string error = createRequest->getMessage();
|
||||
// take some action
|
||||
} else {
|
||||
//success do something
|
||||
}
|
||||
</pre>
|
||||
<h3>pvCopy</h3>
|
||||
<p>The definition of the public members is:</p>
|
||||
<pre>
|
||||
class epicsShareClass PVCopyTraverseMasterCallback
|
||||
{
|
||||
...
|
||||
virtual void nextMasterPVField(PVFieldPtr const &pvField);
|
||||
};
|
||||
|
||||
class class epicsShareClass PVCopy
|
||||
{
|
||||
...
|
||||
static PVCopyPtr create(
|
||||
PVStructurePtr const &pvMaster,
|
||||
PVStructurePtr const &pvRequest,
|
||||
std::string const & structureName);
|
||||
PVStructurePtr getPVMaster();
|
||||
void traverseMaster(PVCopyTraverseMasterCallbackPtr const & callback);
|
||||
StructureConstPtr getStructure();
|
||||
PVStructurePtr createPVStructure();
|
||||
size_t getCopyOffset(PVFieldPtr const &masterPVField);
|
||||
size_t getCopyOffset(
|
||||
PVStructurePtr const &masterPVStructure,
|
||||
PVFieldPtr const &masterPVField);
|
||||
PVFieldPtr getMasterPVField(std::size_t structureOffset);
|
||||
void initCopy(
|
||||
PVStructurePtr const &copyPVStructure,
|
||||
BitSetPtr const &bitSet);
|
||||
void updateCopySetBitSet(
|
||||
PVStructurePtr const &copyPVStructure,
|
||||
BitSetPtr const &bitSet);
|
||||
void updateCopyFromBitSet(
|
||||
PVStructurePtr const &copyPVStructure,
|
||||
BitSetPtr const &bitSet);
|
||||
void updateMaster(
|
||||
PVStructurePtr const &copyPVStructure,
|
||||
BitSetPtr const &bitSet);
|
||||
PVStructurePtr getOptions(std::size_t fieldOffset);
|
||||
...
|
||||
};
|
||||
</pre>
|
||||
where
|
||||
<dl>
|
||||
<dt>PVCopyTraverseMasterCallback::nextMasterPVField</dt>
|
||||
<dd>
|
||||
<b>PVCopyTraverseMasterCallback</b> is a callback which must
|
||||
be implemented by the code that uses pvCopy, normally
|
||||
the channel provider. It has the single method <b>nextMasterPVField</b>
|
||||
<br />
|
||||
<b>nextMasterPVField</b> is called for each field in the master
|
||||
as a result of a call to <b>traverseMaster</b>.
|
||||
</dd>
|
||||
<dt>create</dt>
|
||||
<dd>
|
||||
This is the method for creating a PVCopy instance.<br/>
|
||||
<dl>
|
||||
<dt>pvMaster</dt>
|
||||
<dd>the top level structure managed by the server.</dd>
|
||||
<dt>pvRequest</dt>
|
||||
<dd>selects the set of subfields desired
|
||||
and options for each field.</dd>
|
||||
<dt>structureName</dt>
|
||||
<dd>the name for the top level of any PVStructure created.
|
||||
</dd>
|
||||
</dl>
|
||||
</dd>
|
||||
<dt>getPVMaster</dt>
|
||||
<dd>
|
||||
Gets the top level structure from pvMaster.
|
||||
</dd>
|
||||
<dt>traverseMaster</dt>
|
||||
<dd>
|
||||
Traverse all fields of the top level structure of pvMaster.
|
||||
For each field the callback is called.
|
||||
</dd>
|
||||
<dt>getStructure</dt>
|
||||
<dd>
|
||||
Get the introspection interface for a PVStructure for e copy.
|
||||
</dd>
|
||||
<dt>createPVStructure</dt>
|
||||
<dd>Create a copy instance.
|
||||
Monitors keep a queue of monitor elements.
|
||||
Since each element needs a PVStructure, multiple top level structures
|
||||
will be created.
|
||||
</dd>
|
||||
<dt>getCopyOffset</dt>
|
||||
<dd>Given a field in pvMaster.
|
||||
return the offset in copy for the same field.
|
||||
A value of std::string::npos means that the copy does not have this field.
|
||||
Two overloaded methods are provided. The first is called if
|
||||
the field of master is not a structure. The second is for
|
||||
subfields of a structure.
|
||||
</dd>
|
||||
<dt>getMasterPVField</dt>
|
||||
<dd>
|
||||
Given a offset in the copy get the corresponding field in pvMaster.
|
||||
</dd>
|
||||
<dt>initCopy</dt>
|
||||
<dd>
|
||||
Initialize the fields in copyPVStructure
|
||||
by giving each field the value from the corresponding field in pvMaster.
|
||||
bitSet will be set to show that all fields are changed.
|
||||
This means that bit set will have the value <b>{0}</b>.
|
||||
</dd>
|
||||
<dt>updateCopySetBitSet</dt>
|
||||
<dd>
|
||||
Set all fields in copyPVStructure to the value of the corresponding field
|
||||
in pvMaster. Each field that is changed has it's corresponding
|
||||
bit set in bitSet.
|
||||
</dd>
|
||||
<dt>updateCopyFromBitSet</dt>
|
||||
<dd>
|
||||
For each set bit in bitSet set the field in copyPVStructure to the value
|
||||
of the corresponding field in pvMaster.
|
||||
</dd>
|
||||
<dt>updateMaster</dt>
|
||||
<dd>
|
||||
For each set bit in bitSet set the field in pvMaster to the value
|
||||
of the corresponding field in copyPVStructure.
|
||||
|
||||
</dd>
|
||||
<dt>getOptions</dt>
|
||||
<dd>
|
||||
Get the options for the field at the specified offset.
|
||||
A NULL is returned if no options were specified for the field.
|
||||
If options were specified,PVStructurePtr is
|
||||
a structure with a set of PVString subfields that specify name,value
|
||||
pairs. name is the subField name and value is the subField value.
|
||||
</dd>
|
||||
</dl>
|
||||
|
||||
|
||||
|
||||
|
||||
<h2>support for monitor</h2>
|
||||
<p>This consists of two components:
|
||||
<dl>
|
||||
<dt>monitor</dt>
|
||||
<dd>Used by code that implements pvAccess monitors.</dd>
|
||||
<dt>monitorPlugin</dt>
|
||||
<dd>Code that provides special semantics for monitors.</dd>
|
||||
</dl>
|
||||
</p>
|
||||
<h3>monitor</h3>
|
||||
<pre>
|
||||
class MonitorElement {
|
||||
MonitorElement(PVStructurePtr const & pvStructurePtr);
|
||||
PVStructurePtr pvStructurePtr;
|
||||
BitSetPtr changedBitSet;
|
||||
BitSetPtr overrunBitSet;
|
||||
};
|
||||
|
||||
class Monitor {
|
||||
virtual Status start() = 0;
|
||||
virtual Status stop() = 0;
|
||||
virtual MonitorElementPtr poll() = 0;
|
||||
virtual void release(MonitorElementPtr const & monitorElement) = 0;
|
||||
};
|
||||
|
||||
class MonitorRequester : public virtual Requester {
|
||||
virtual void monitorConnect(Status const & status,
|
||||
MonitorPtr const & monitor, StructureConstPtr const & structure) = 0;
|
||||
virtual void monitorEvent(MonitorPtr const & monitor) = 0;
|
||||
virtual void unlisten(MonitorPtr const & monitor) = 0;
|
||||
};
|
||||
</pre>
|
||||
<h4>monitorElement</h4>
|
||||
<p><b>MonitorElement</b> holds the data for one element of a monitor queue.
|
||||
It has the fields:
|
||||
<dl>
|
||||
<dt>pvStructurePtr</dt>
|
||||
<dd>A top level structure with data values at the time the monitors occurs.</dd>
|
||||
<dt>changedBitSet</dt>
|
||||
<dd>Shows which fields have changed since the previous monitor.</dd>
|
||||
<dt>overrunBitSet</dt>
|
||||
<dd>Shows which fields have changed more han once since the previous monitor.</dd>
|
||||
</dl>
|
||||
</p>
|
||||
<h4>monitorElement queue</h4>
|
||||
<p>
|
||||
A queue of monitor elements must be implemented by any channel provider that implements
|
||||
<b>Channel::createMonitor</b>.
|
||||
For an example implementation look at pvDatabaseCPP.
|
||||
It has the following:
|
||||
<pre>
|
||||
typedef Queue<MonitorElement> MonitorElementQueue;
|
||||
typedef std::tr1::shared_ptr<MonitorElementQueue> MonitorElementQueuePtr;
|
||||
|
||||
class MultipleElementQueue :
|
||||
public ElementQueue
|
||||
{
|
||||
public:
|
||||
POINTER_DEFINITIONS(MultipleElementQueue);
|
||||
virtual ~MultipleElementQueue(){}
|
||||
MultipleElementQueue(
|
||||
MonitorLocalPtr const &monitorLocal,
|
||||
MonitorElementQueuePtr const &queue,
|
||||
size_t nfields);
|
||||
virtual void destroy(){}
|
||||
virtual Status start();
|
||||
virtual Status stop();
|
||||
virtual bool dataChanged();
|
||||
virtual MonitorElementPtr poll();
|
||||
virtual void release(MonitorElementPtr const &monitorElement);
|
||||
...
|
||||
};
|
||||
</pre>
|
||||
<h4>Monitor</h4>
|
||||
<p><b>Monitor</b> must be implemented by any channel provider that implements
|
||||
<b>Channel::createMonitor</b>.
|
||||
Remote PVAccess also implements Monitor on the client side.
|
||||
Note that each client has it's own queue that is not shared with other client.
|
||||
</p>
|
||||
<p>Monitor has the following methods:</p>
|
||||
<dl>
|
||||
<dt>start</dt>
|
||||
<dd>
|
||||
Start monitoring.
|
||||
This will result in a an initial monitor that has the current value
|
||||
of all fields.
|
||||
</dd>
|
||||
<dt>stop</dt>
|
||||
<dd>
|
||||
Stop monitoring.
|
||||
</dd>
|
||||
<dt>poll</dt>
|
||||
<dd>
|
||||
Called to get a monitor element.
|
||||
If no new elements are available then a null pointer is returned.
|
||||
</dd>
|
||||
<dt>release</dt>
|
||||
<dd>
|
||||
Release the monitor element.
|
||||
The caller owns the monitor element between the calls to poll and release.
|
||||
</dd>
|
||||
<dl>
|
||||
</dl>
|
||||
<h4>MonitorRequester</h4>
|
||||
<p>This must be implemented by a pvAccess client.
|
||||
It has the methods:</p>
|
||||
<dl>
|
||||
<dt>monitorConnect</dt>
|
||||
<dd>
|
||||
A monitor has either connected of disconnected.
|
||||
</dd>
|
||||
<dt>monitorEvent</dt>
|
||||
<dd>
|
||||
A new monitor element is available.
|
||||
</dd>
|
||||
<dt>unlisten</dt>
|
||||
<dd>
|
||||
The channel is going away. The client cam no longer access the monitor.
|
||||
</dd>
|
||||
</dl>
|
||||
|
||||
<h3>monitorPlugin</h3>
|
||||
<pre>
|
||||
class MonitorPlugin
|
||||
{
|
||||
virtual std::string const & getName() = 0;
|
||||
virtual bool causeMonitor(
|
||||
PVFieldPtr const &pvField,
|
||||
PVStructurePtr const &pvTop,
|
||||
MonitorElementPtr const &monitorElement) = 0;
|
||||
virtual void monitorDone(
|
||||
MonitorElementPtr const &monitorElement);
|
||||
virtual void startMonitoring();
|
||||
virtual void stopMonitoring();
|
||||
virtual void beginGroupPut();
|
||||
virtual void endGroupPut();
|
||||
};
|
||||
|
||||
class MonitorPluginCreator
|
||||
{
|
||||
virtual MonitorPluginPtr create(
|
||||
FieldConstPtr const &field,
|
||||
StructureConstPtr const &top,
|
||||
PVStructurePtr const &pvFieldOptions) = 0;
|
||||
virtual std::string const & getName() = 0;
|
||||
}
|
||||
|
||||
class MonitorPluginManager
|
||||
{
|
||||
static MonitorPluginManagerPtr get();
|
||||
bool addPlugin(
|
||||
std::string const &pluginName,
|
||||
MonitorPluginCreatorPtr const &creator);
|
||||
MonitorPluginCreatorPtr findPlugin(std::string const &pluginName);
|
||||
void showNames();
|
||||
};
|
||||
|
||||
</pre>
|
||||
<h4>MonitorPlugin</h4>
|
||||
<p><b>MonitorPlugin</b> must be implemented by the plugin implementation.
|
||||
It has methods:</p>
|
||||
<dl>
|
||||
<dt>getName</dt>
|
||||
<dd>Get the name of the plugin.</dd>
|
||||
<dt>causeMonitor</dt>
|
||||
<dd>
|
||||
Should the value of pvField cause a monitor to be raised.
|
||||
pvField and pvTop are fields in the top level structure
|
||||
being monitored. monitorElement has the top level structure
|
||||
for the copy</b>.
|
||||
The implementation should <b>not</b> modify the fields in the structure
|
||||
being monitored.
|
||||
Called with pvTop locked.
|
||||
</dd>
|
||||
<dt>monitorDone</dt>
|
||||
<dd>
|
||||
Called just before monitorElement will be given to client.
|
||||
The plugin can change the data values and bitSets in monitorElement.
|
||||
Called with pvTop unlocked.
|
||||
</dd>
|
||||
<dt>startMonitoring</dt>
|
||||
<dd>
|
||||
Monitoring is starting.
|
||||
</dd>
|
||||
<dt>stopMonitoring</dt>
|
||||
<dd>
|
||||
Monitoring is being stopped.
|
||||
</dd>
|
||||
<dt>beginGroupPut</dt>
|
||||
<dd>
|
||||
A set of puts is starting.
|
||||
Called with pvTop locked.
|
||||
</dd>
|
||||
<dt>endGroupPut</dt>
|
||||
<dd>
|
||||
The set of puts is complete.
|
||||
Called with pvTop locked.
|
||||
</dd>
|
||||
</dl>
|
||||
<h4>MonitorPluginCreator</h4>
|
||||
<p><b>MonitorPluginCreator</b> must also be implemented by the plugin implementation.
|
||||
It is called for each field instance that has options of the from
|
||||
<b>[plugin=name...]</b> where <b>name</b> is the name of the plugin.
|
||||
Note that a plugin instance will belong to a single client.
|
||||
It has methods:</p>
|
||||
<dl>
|
||||
<dt>getName</dt>
|
||||
<dd>Get the name of the plugin.</dd>
|
||||
<dt>create</dt>
|
||||
<dd>
|
||||
Create a new plugin instance.
|
||||
If the arguments are not compatible with the plugin a NULL shared pointer is
|
||||
returned.<br/>
|
||||
pvFieldOptions is
|
||||
a structure with a set of PVString subfields that specify <b>name,value</b>
|
||||
pairs. name is the subField name and value is the subField value.<br/>
|
||||
Note that a plugin will below to a single client.
|
||||
</dd>
|
||||
<dl>
|
||||
<h4>MonitorPluginManager</h4>
|
||||
<p><b>MonitorPluginManager</b> has the methods:</p>
|
||||
<dl>
|
||||
<dt>get</dt>
|
||||
<dd>
|
||||
MonitorPluginManager is a singleton.
|
||||
The first call to get will create the single instance.
|
||||
Further calls will return the single instance.
|
||||
</dd>
|
||||
<dt>addPlugin</dt>
|
||||
<dd>
|
||||
Add a new plugin.
|
||||
</dd>
|
||||
<dt>findPlugin</dt>
|
||||
<dd>
|
||||
Find a plugin. A NULL shared pointer is returned if it has not been added.
|
||||
</dd>
|
||||
<dt>showNames</dt>
|
||||
<dd>
|
||||
Show the names of all plugins that have been added.
|
||||
</dd>
|
||||
</dl>
|
||||
<p><b>NOTE:</b>
|
||||
Should the method <b>causeMonitor</b>
|
||||
have arguments <b>pvField</b> and <b>pvTop</b>
|
||||
be defined so that they can not be modified.
|
||||
This would be possible if the following was defined:
|
||||
<pre>
|
||||
typedef std::tr1::shared_ptr<const PVField> PVFieldConstPtr;
|
||||
typedef std::tr1::shared_ptr<const PVStructure> PVStructureConstPtr;
|
||||
</pre>
|
||||
then the definition for causeMonitor could be:
|
||||
<pre>
|
||||
virtual bool causeMonitor(
|
||||
PVFieldConstPtr const &pvField,
|
||||
PVStructureConstPtr const &pvTop,
|
||||
MonitorElementPtr const &monitorElement) = 0;
|
||||
</pre>
|
||||
But just adding these definitions is not sufficient.
|
||||
In addition all methods defined in pvDataCPP must be checked.
|
||||
In particular many of the methods in <b>Convert</b> must have
|
||||
their arguments modified.
|
||||
Big job.
|
||||
</p>
|
||||
<h2>monitorPlugin example</h2>
|
||||
<h3>Example Plugin Overview</h3>
|
||||
<p>This section describes an example plugin that:</p>
|
||||
<ul>
|
||||
<li>Only raises monitors when a field changes value.<br />
|
||||
If no plugin is provided
|
||||
the default is to raise a monitor when a put is issued to a field.</li>
|
||||
<li>Optionally a change will not raise a monitor.<br />
|
||||
The change will, however,
|
||||
appear if a put to another field raise a monitor.</li>
|
||||
</ul>
|
||||
<p>As an example assume that a channel provided by pvAccess has a top level structure
|
||||
that represents a power supply.</p>
|
||||
<pre>
|
||||
structure powerSupply
|
||||
structure alarm
|
||||
structure timeStamp
|
||||
structure power
|
||||
double value
|
||||
structure alarm
|
||||
structure display
|
||||
structure voltage
|
||||
double value
|
||||
structure alarm
|
||||
structure display
|
||||
structure current
|
||||
double value
|
||||
structure alarm
|
||||
structure display
|
||||
</pre>
|
||||
<p>A pvAccess client wants to create a monitor on the powerSupply as follows:
|
||||
The client wants a top level structure that looks like:
|
||||
<pre>
|
||||
structure powerSupply
|
||||
structure alarm
|
||||
structure timeStamp
|
||||
structure power
|
||||
double value
|
||||
structure voltage
|
||||
double value
|
||||
structure current
|
||||
double value
|
||||
</pre>
|
||||
In addition the client wants monitors to occur only when one of the monitored
|
||||
fields changes value but not just because a put occured.
|
||||
Also if only the timeStamp changes value then that should not cause a monitor.
|
||||
</p>
|
||||
<p>The example monitor plugin implements the semantics the
|
||||
client wants. It can be attached to any field via the following options:
|
||||
<pre>
|
||||
[plugin=onChange,raiseMonitor=value]
|
||||
</pre>
|
||||
This plugin will trigger a monitor for the field only if the field changes
|
||||
value. In addition <b>value</b> equals <b>false</b> means do not raise a monitor
|
||||
for changes to this field.
|
||||
But if a change to another field does cause a monitor the change to this field
|
||||
will be passed to the client.
|
||||
</p>
|
||||
<p>
|
||||
Assume that the client has already connected to the channel.
|
||||
The client can then issue the commands:</p>
|
||||
<pre>
|
||||
std::string request("field(alarm[plugin=onChange]");
|
||||
request += ",timeStamp[plugin=onChange,raiseMonitor=false]";
|
||||
request += ",power.value[plugin=onChange";
|
||||
request += ",voltage.value[plugin=onChange";
|
||||
request += ",current.value[plugin=onChange";
|
||||
|
||||
PVStructurePtr pvRequest = createRequest->createRequest(request);
|
||||
|
||||
MonitorPtr monitor = channel->createMonitor(monitorRequester,pvRequest);
|
||||
</pre>
|
||||
<h3>Example Plugin Code</h3>
|
||||
<p>The header file to create the example has the definition:</p>
|
||||
<pre>
|
||||
class ExampleMonitorPlugin{
|
||||
public:
|
||||
static void create();
|
||||
};
|
||||
</pre>
|
||||
<p>The implementation is:</p>
|
||||
<pre>
|
||||
class OnChangePlugin : public MonitorPlugin
|
||||
{
|
||||
public:
|
||||
virtual ~OnChangePlugin(){}
|
||||
OnChangePlugin() {}
|
||||
bool init(
|
||||
FieldConstPtr const &field,
|
||||
StructureConstPtr const &top,
|
||||
PVStructurePtr const &pvFieldOptions)
|
||||
{
|
||||
pvField = getPVDataCreate()->createPVField(field);
|
||||
raiseMonitor = true;
|
||||
if(pvFieldOptions!=NULL) {
|
||||
PVStringPtr pvString =
|
||||
pvFieldOptions->getSubField<PVString>("raiseMonitor");
|
||||
if(pvString!=NULL) {
|
||||
std::string value = pvString->get();
|
||||
if(value.compare("false")==0) raiseMonitor = false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
virtual std::string &getName(){return pluginName;}
|
||||
virtual bool causeMonitor(
|
||||
PVFieldPtr const &pvNew,
|
||||
PVStructurePtr const &pvTop,
|
||||
MonitorElementPtr const &monitorElement)
|
||||
{
|
||||
bool isSame = convert->equals(pvNew,pvField);
|
||||
if(isSame) return false;
|
||||
convert->copy(pvNew,pvField);
|
||||
return raiseMonitor;
|
||||
}
|
||||
private:
|
||||
PVFieldPtr pvField;
|
||||
bool raiseMonitor;
|
||||
};
|
||||
class OnChangePluginCreator : public MonitorPluginCreator
|
||||
{
|
||||
public:
|
||||
virtual std::string &getName(){return pluginName;}
|
||||
virtual MonitorPluginPtr create(
|
||||
FieldConstPtr const &field,
|
||||
StructureConstPtr const &top,
|
||||
PVStructurePtr const &pvFieldOptions)
|
||||
{
|
||||
OnChangePluginPtr plugin(new OnChangePlugin());
|
||||
bool result = plugin->init(field,top,pvFieldOptions);
|
||||
if(!result) return MonitorPluginPtr();
|
||||
return plugin;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
void ExampleMonitorPlugin::create()
|
||||
{
|
||||
static OnChangePluginCreatorPtr plugin;
|
||||
static Mutex mutex;
|
||||
Lock xx(mutex);
|
||||
if(plugin==NULL) {
|
||||
plugin = OnChangePluginCreatorPtr(new OnChangePluginCreator());
|
||||
MonitorPluginManager::get()->addPlugin(pluginName,plugin);
|
||||
}
|
||||
}
|
||||
</pre>
|
||||
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
Binary file not shown.
57
documentation/mainpage.dox
Normal file
57
documentation/mainpage.dox
Normal file
@@ -0,0 +1,57 @@
|
||||
#ifndef MAINPAGE_H
|
||||
#define MAINPAGE_H
|
||||
/**
|
||||
@mainpage pvDataCPP documentation
|
||||
|
||||
- [Download](https://sourceforge.net/projects/epics-pvdata/files/)
|
||||
- @htmlonly <a href="modules.html">API components</a> @endhtmlonly
|
||||
- @ref release_notes
|
||||
|
||||
The epics::pvData namespace.
|
||||
See pv/pvData.h header.
|
||||
|
||||
@code
|
||||
#include <pv/pvData.h>
|
||||
#include <pv/createRequest.h>
|
||||
@endcode
|
||||
|
||||
- Type description epics::pvData::Field and sub-classes
|
||||
- Value container epics::pvData::PVField and sub-classes
|
||||
- POD array handling epics::pvData::shared_vector
|
||||
- pvRequest parsing epics::pvData::createRequest()
|
||||
|
||||
Define a structure type and create a container with default values.
|
||||
|
||||
@code
|
||||
namespace pvd = epics::pvData;
|
||||
pvd::StructureConstPtr stype(pvd::FieldBuilder::begin()
|
||||
->add("fld1", pvd::pvInt)
|
||||
->addNestedStructure("sub")
|
||||
->add("fld2", pvd::pvString)
|
||||
->endNested()
|
||||
->createStructure());
|
||||
|
||||
pvd::PVStructuretPtr value(stype->build());
|
||||
|
||||
value->getSubFieldT<pvd::PVInt>("fld1")->put(4); // store integer 4. would throw if not pvInt
|
||||
value->getSubFieldT<pvd::PVScalar>("sub.fld2")->putFrom(4.2); // convert and store string "4.2"
|
||||
@endcode
|
||||
|
||||
is equivalent to the following pseudo-code.
|
||||
|
||||
@code
|
||||
struct stype {
|
||||
pvd::int32 fld1;
|
||||
struct {
|
||||
std::string fld2;
|
||||
} sub;
|
||||
};
|
||||
stype value;
|
||||
value.fld1 = 4;
|
||||
value.fld2 = pvd::castUnsafe<std::string>(4.2);
|
||||
@endcode
|
||||
|
||||
*/
|
||||
|
||||
#endif /* MAINPAGE_H */
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,179 +0,0 @@
|
||||
pvDataCPP cookbook
|
||||
------------------
|
||||
|
||||
|
||||
Creating introspection data interfaces
|
||||
|
||||
// create a scalar
|
||||
getFieldCreate()->createScalar(pvDouble);
|
||||
|
||||
// create a scalar array
|
||||
getFieldCreate()->createScalarArray(pvDouble);
|
||||
|
||||
// create a structure
|
||||
getFieldCreate()->createFieldBuilder()->
|
||||
setId("enum_t")->
|
||||
add("index", pvDouble)->
|
||||
addArray("choices", pvString)->
|
||||
createStructure();
|
||||
|
||||
// create a structure (cntd.)
|
||||
StructureConstPtr enum_t =
|
||||
getFieldCreate()->createFieldBuilder()->
|
||||
setId("enum_t")->
|
||||
add("index", pvInt)->
|
||||
addArray("choices", pvString)->
|
||||
createStructure();
|
||||
|
||||
// create a structure (cntd.)
|
||||
StructureConstPtr ntEnum =
|
||||
getFieldCreate()->createFieldBuilder()->
|
||||
setId("epics:nt/NTEnum:1.0")->
|
||||
add("value", enum_t)->
|
||||
addNestedStructure("timeStamp")->
|
||||
setId("time_t")->
|
||||
add("secondsPastEpoch", pvLong)->
|
||||
add("nanoseconds", pvInt)->
|
||||
add("userTag", pvInt)->
|
||||
endNested()->
|
||||
createStructure();
|
||||
|
||||
// create an union == same as structure
|
||||
|
||||
---
|
||||
|
||||
Creating data containers
|
||||
|
||||
// create a scalar
|
||||
PVDouble::shared_pointer doubleValue = getPVDataCreate()->createPVScalar<PVDouble>();
|
||||
|
||||
// create a scalar array
|
||||
PVDoubleArray::shared_pointer doubleArrayValue = getPVDataCreate()->createPVScalarArray<PVDouble>();
|
||||
|
||||
// create a structure
|
||||
PVStructure::shared_pointer struct = getPVDataCreate()->createPVStructure(ntEnum);
|
||||
|
||||
// create an union
|
||||
PVUnion::shared_pointer pvUnion = getPVDataCreate()->createPVUnion(unionIF);
|
||||
|
||||
// create a structure array
|
||||
PVStructureArray::shared_pointer structArray = getPVDataCreate()->createPVStructureArray(ntEnum);
|
||||
|
||||
|
||||
// scalar usage
|
||||
PVInt::shared_pointer index = struct->getSubField<PVInt>("value.index");
|
||||
int32 ix = index->get();
|
||||
index->put(3);
|
||||
std::cout << *index << std::endl;
|
||||
|
||||
|
||||
// using <<=, >>= operators to get/set
|
||||
*doubleValue <<= 12.3;
|
||||
|
||||
double val;
|
||||
*doubleValue >>= val;
|
||||
|
||||
|
||||
|
||||
// array usage
|
||||
PVStringArray::shared_pointer choices = struct->getSubField<PVStringArray>("value.choices");
|
||||
|
||||
// use view() to access read-only data
|
||||
PVStringArray::const_svector data(choices->view());
|
||||
for (std::size_t i = 0; i < data.size(); i++)
|
||||
std::cout << data[i] << std::endl;
|
||||
|
||||
// use replace() to put new data
|
||||
PVStringArray::svector newdata;
|
||||
newdata.push_back("zero");
|
||||
newdata.push_back("one");
|
||||
newdata.push_back("two");
|
||||
choices->replace(freeze(newdata));
|
||||
|
||||
// (add more use-cases) here
|
||||
|
||||
// print entire array
|
||||
std::cout << *choices << std::endl;
|
||||
|
||||
// print elmenet at index == 1
|
||||
std::cout << format::array_at(1) << *choices << std::endl;
|
||||
|
||||
----
|
||||
|
||||
Union handling
|
||||
|
||||
|
||||
Union::const_shared_pointer punion =
|
||||
getFieldCreate()->createFieldBuilder()->
|
||||
add("doubleValue", pvDouble)->
|
||||
add("intValue", pvInt)->
|
||||
createUnion();
|
||||
|
||||
PVUnion::shared_pointer u = getPVDataCreate()->createPVUnion(punion);
|
||||
|
||||
// select and put
|
||||
// this create a new instance of PVDouble (everytime select() is called)
|
||||
PVDouble::shared_pointer doubleValue = u->select<PVDouble>("doubleValue");
|
||||
doubeValue->put(12);
|
||||
// select using index (and direct put)
|
||||
u->select<PVDouble>(0)->put(12);
|
||||
// select using existing PVField (PVUnion stores by-reference)
|
||||
u->set("doubleValue", doubleValue);
|
||||
|
||||
// get selected field name or index
|
||||
std::string selectedFN = u->getSelectedFieldName();
|
||||
int32 selectedIndex = u->getSelectedIndex();
|
||||
|
||||
// get currently selected (knowing it's PVDouble)
|
||||
PVDouble value = u->get<PVDouble>();
|
||||
|
||||
|
||||
|
||||
Variant Union handling
|
||||
|
||||
|
||||
PVUnion::shared_pointer any = getPVDataCreate()->createPVVariantUnion();
|
||||
|
||||
PVDouble::shared_pointer doubleValue = getPVDataCreate()->createPVScalar<PVDouble>();
|
||||
doubleValue->put(12.8);
|
||||
any->set(doubleValue);
|
||||
|
||||
PVDouble::shared_pointer doubleValue2 = any->get<PVDouble>();
|
||||
|
||||
// variant union work by-reference (pointers match also)
|
||||
// doubleValue.get() == doubleValue2.get()
|
||||
|
||||
|
||||
|
||||
------
|
||||
|
||||
Convert
|
||||
|
||||
|
||||
// convert to int
|
||||
int32 i = doubleValue->getAs<int32>();
|
||||
|
||||
// from int
|
||||
doubleValue->putFrom<int32>(i);
|
||||
|
||||
// from string
|
||||
doubleValue->putFrom<std::string>("12.3");
|
||||
|
||||
// from scalar field
|
||||
doubleValue->assign(pvScalar);
|
||||
|
||||
|
||||
|
||||
// convert to int array
|
||||
PVIntArray::const_svector intData;
|
||||
doubleArrayValue->getAs<int32>(intData);
|
||||
|
||||
// from string array
|
||||
PVStringArray::svector labels;
|
||||
labels.push_back("zero");
|
||||
labels.push_back("one");
|
||||
labels.push_back("two");
|
||||
doubleArrayValue->putFrom<std::string>(labels);
|
||||
|
||||
// from scalar array
|
||||
doubleArrayValue->assign(pvScalarArray);
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
410
documentation/release_notes.dox
Normal file
410
documentation/release_notes.dox
Normal file
@@ -0,0 +1,410 @@
|
||||
/**
|
||||
|
||||
@page release_notes Release Notes
|
||||
|
||||
Release 7.1.0 (Nov 2018)
|
||||
==========================
|
||||
|
||||
- Deprecations
|
||||
- BoundedString, BoundedScalarArray, and FixedScalarArray will be removed unless they are fixed.
|
||||
See https://github.com/epics-base/pvDataCPP/issues/52 for discussion.
|
||||
- pv/localStaticLock.h
|
||||
- pv/pvCopy.h (see epics::pvData::PVRequestMapper)
|
||||
- Removals
|
||||
- Remove previously deprecated executor.h, queue.h and timerFunction.h
|
||||
- Remove *HashFunction functors to "hash" Field sub-classes which were never fully implemented.
|
||||
- Fixes
|
||||
- Make thread safe getFieldCreate() and getPVDataCreate()
|
||||
- Workaround for MSVC pickyness that iterators be non-NULL, even when not de-referenced.
|
||||
- Fix alignment fault during (de)serialization on RTEMS/vxWorks.
|
||||
- Fix epics::pvData::shared_vector::swap() for void specialization.
|
||||
- Changes in several epics::pvData::Field sub-classes to return const ref. instead of a copy.
|
||||
- Additions
|
||||
- epics::pvData::shared_vector add c++11 move and construct for initializer list.
|
||||
- Add epics::pvData::AnyScalar::clear()
|
||||
- Add ctor epics::pvData::AnyScalar(ScalarType, const void*) to allow construction from an untyped buffer.
|
||||
- Add epics::pvData::Timer::close()
|
||||
- Allow epics::pvData::castUnsafe() from const char* without first allocating a std::string.
|
||||
- De-duplication of epics::pvData::Field instances is performed using a global hash table.
|
||||
Identical definitions will share a single instance. Allows O(0) comparision.
|
||||
- Add epics::pvData::PVRequestMapper to facilitate (partial) copying between PVStructure instances
|
||||
modified by a pvRequest.
|
||||
- Add shorthand notations epics::pvData::FieldBuilder::begin() and epics::pvData::Field::build()
|
||||
|
||||
Release 7.0.0 (Dec 2017)
|
||||
========================
|
||||
|
||||
- Removals
|
||||
- Remove requester.h, monitor.h, and destroyable.h.. Migrated to the pvAccessCPP module.
|
||||
- Previously deprecated monitorPlugin.h is removed.
|
||||
- Remove pv/messageQueue.h and epics::pvData::MessageQueue
|
||||
- Deprecate the following utility classes, to be removed in 8.0.
|
||||
- epics::pvData::Queue
|
||||
- epics::pvData::Executor
|
||||
- epics::pvData::TimeFunction
|
||||
- Additions
|
||||
- Add pv/pvdVersion.h which is included by pv/pvIntrospect.h
|
||||
- Add epics::pvData::createRequest() function. Alternative to epics::pvData::CreateRequest class which throws on error.
|
||||
- epics::pvData::FieldBuilder allow Structure defintion to be changed/appended
|
||||
- Add epics::pvData::ValueBuilder like FieldBuilder also sets initial values.
|
||||
- Can also be constructed using an existing PVStructure to allow "editing".
|
||||
- Add debugPtr.h wrapper with reference tracking to assist in troubleshooting shared_ptr related ref. loops.
|
||||
- Add @ref pvjson utilities
|
||||
- Add reftrack @ref pvd_reftrack
|
||||
- Add header typemap.h to facilitate boilerplate switch() over ScalarType
|
||||
- Add epics::auto_ptr typedef in help writing code supporting both c++98 and c++11 w/o copious deprecation warnings.
|
||||
|
||||
|
||||
Release 6.0.1
|
||||
=============
|
||||
|
||||
The changes since release 6.0.0 are:
|
||||
|
||||
* Fix "Problem building pvDataCPP for win32-x86-mingw" (issue #42)
|
||||
* In src/misc/bitSet.cpp #include "algorithm" required for MSVS 2015
|
||||
* In testApp/misc/testTypeCast.cpp print (u)int8 values as integers
|
||||
* Minor documentation updates
|
||||
|
||||
|
||||
Release 6.0.0 (Aug. 2016)
|
||||
=======================
|
||||
|
||||
The main changes since release 5.0.4 are:
|
||||
|
||||
* Linux shared library version added
|
||||
* Headers have been moved into pv directories
|
||||
* Bitset functions declared const where possible
|
||||
* Bitset::swap added
|
||||
* Requester::message has default implementation
|
||||
* Serialization/deserialization helpers added
|
||||
* Non-template getSubField char* overload added
|
||||
* MonitorPlugin deprecated
|
||||
* Field name validation performed
|
||||
* Now builds for Cygwin and MinGW targets
|
||||
* Fix for debug build issue.
|
||||
* New license file replaces LICENSE and COPYRIGHT
|
||||
|
||||
Shared library version added
|
||||
----------------------------
|
||||
|
||||
Linux shared library version numbers have been added by setting SHRLIB_VERSION
|
||||
(to 6.0 in this case). So shared object will be libpvData.so.6.0 instead of
|
||||
libpvData.so.
|
||||
|
||||
Headers have been moved into pv directories
|
||||
-------------------------------------------
|
||||
|
||||
E.g. src/property/alarm.h -> src/property/pv/alarm.h
|
||||
|
||||
This facilitates using some IDEs such as Qt Creator.
|
||||
|
||||
Requester::message has default implementation
|
||||
---------------------------------------------
|
||||
|
||||
Requester::message is no longer pure virtual. Default implementation sends
|
||||
string to std::cerr.
|
||||
|
||||
Serialization/deserialization helpers added
|
||||
-------------------------------------------
|
||||
|
||||
A helper function, serializeToVector, has been added which serializes a
|
||||
Serializable object into a standard vector of UInt8s.
|
||||
|
||||
Similarly a function deserializeFromVector deserializes a standard vector into
|
||||
a Deserializable object.
|
||||
|
||||
A function deserializeFromBuffer deserializes a ByteBuffer into a
|
||||
Deserializable object.
|
||||
|
||||
Field name validation performed
|
||||
-------------------------------
|
||||
|
||||
On creating a Structure or Union the field names are now validated.
|
||||
|
||||
Valid characters for a field name are upper or lowercase letters, numbers and
|
||||
underscores and intial numbers are invalid, i.e. names must be of the form
|
||||
[A-Za-z_][A-Za-z0-9_]*.
|
||||
|
||||
Now builds for Cygwin and MinGW targets
|
||||
---------------------------------------
|
||||
|
||||
Includes cross-compiling MinGW on Linux.
|
||||
|
||||
|
||||
Release 5.0.4
|
||||
=============
|
||||
|
||||
The changes since release 5.0.3 are:
|
||||
|
||||
* Fixed bitset serialization (issue #24)
|
||||
* Fixed truncation in BitSet::or_and (issue #27)
|
||||
|
||||
Fixed bitset serialization (issue #24)
|
||||
--------------------------------------
|
||||
|
||||
C++ bitset serialization was not consistent with the C++ deserialization and
|
||||
Java code in some instances (depending on the endianness of the serializer and
|
||||
deserializer) when the number of bits was 56-63 modulo 64. C++ serialization
|
||||
has been fixed.
|
||||
|
||||
Fix exposed issue in deserialization on 32-bit platforms which
|
||||
has also been corrected.
|
||||
|
||||
Fixed truncation in BitSet::or_and (issue #27)
|
||||
----------------------------------------------
|
||||
|
||||
If n, n1 and n2 words are used to store the values of the bitsets bitset,
|
||||
bitset1 and bitset2 respectively then max(n, min(n1,n2)) words are needed
|
||||
to store bitset.or_(bitset1, bitset2).
|
||||
|
||||
Previously min(n1,n2) words were used and the result would be truncated in
|
||||
some instances. This has been fixed.
|
||||
|
||||
|
||||
Release 5.0.3
|
||||
=============
|
||||
|
||||
The only change since release 5.0.2 is:
|
||||
|
||||
Fixed buffer overflow in PVUnion::serialize() (issue #20)
|
||||
---------------------------------------------------------
|
||||
|
||||
A PVUnion whose stored value was null was serialized without checking
|
||||
whether the buffer had sufficient capacity. This has been fixed by calling
|
||||
ensureBuffer().
|
||||
|
||||
|
||||
Release 5.0.2 (Sep. 2015)
|
||||
=========================
|
||||
|
||||
The main changes since release 4.0 are:
|
||||
|
||||
- Deprecated getXXXField() methods have been removed from PVStructure
|
||||
- Convert copy methods and equals operators (re)moved
|
||||
- Convert::copyUnion now always copies between subfields.
|
||||
- New method getSubFieldT, like getSubField except it throws an exception
|
||||
- findSubField method removed from PVStructure
|
||||
- New stream operators for Field and PVField are provided
|
||||
- New template versions of Structure::getField
|
||||
- Fixes for static initialisation order issues
|
||||
- CreateRequest prevents a possible SEGFAULT
|
||||
|
||||
|
||||
Deprecated getXXXField methods have been removed from PVStructure
|
||||
-------------------------------------------------------------------
|
||||
|
||||
The following methods have been removed from PVStructure
|
||||
|
||||
- getBooleanField
|
||||
- getByteField, getShortField, getIntField, getLongField
|
||||
- getUByteField, getUShortField, getUIntField, getULongField
|
||||
- getStringField
|
||||
- getStructureField, getUnionField
|
||||
- getScalarArrayField, getStructureArrayField, getUnionArrayField
|
||||
|
||||
Use template getSubField instead, e.g. use
|
||||
|
||||
getSubField< PVInt >(fieldName)
|
||||
|
||||
in place of
|
||||
|
||||
getIntField(fieldName)
|
||||
|
||||
|
||||
Convert copy methods and equals operators
|
||||
-----------------------------------------
|
||||
|
||||
Convert copy methods where moved and replaced with methods
|
||||
on PVField classes, i.e.
|
||||
|
||||
PVField::copy(const PVField& from)
|
||||
|
||||
Methods
|
||||
|
||||
PVField::copyUnchecked(const PVField& from)
|
||||
|
||||
were added to allow unchecked copies, to gain performance
|
||||
where checked are not needed (anymore).
|
||||
|
||||
In addition:
|
||||
- isCompatibleXXX methods were removed in favour of Field::operator==.
|
||||
- equals methods were remove in favour of PVField::operator==.
|
||||
- operator== methods where moved to pvIntrospect.h and pvData.h
|
||||
|
||||
|
||||
Convert::copyUnion
|
||||
-----------------
|
||||
|
||||
Before this method, depending on types for to and from,
|
||||
sometimes did a shallow copy, i.e. just made to shared_ptr for to
|
||||
share the same data as from.
|
||||
Now it always copies between the subfield of to and from.
|
||||
|
||||
|
||||
New method getSubFieldT, like getSubField except it throws an exception
|
||||
--------------------
|
||||
|
||||
PVStructure has a new template member
|
||||
|
||||
getSubFieldT(std::string const &fieldName)
|
||||
|
||||
that is like `getSubField()` except that it throws a runtime_error
|
||||
instead of returning null.
|
||||
|
||||
|
||||
findSubField method removed from PVStructure
|
||||
--------------------------------------------
|
||||
|
||||
This was mainly used in the implementation of getSubField. With a change to
|
||||
the latter, findSubField was removed.
|
||||
|
||||
|
||||
New stream operators
|
||||
--------------------
|
||||
|
||||
New steam operators are available for Field and PVField.
|
||||
Before to print a Field (or any extension) or a PVField (or any extension)
|
||||
it was necessary to have code like:
|
||||
|
||||
void print(StructureConstPtr struc, PVStructurePtr pv)
|
||||
{
|
||||
if(struc) {
|
||||
cout << *struc << endl;
|
||||
} else {
|
||||
cout << "nullptr\n"
|
||||
}
|
||||
if(pv) {
|
||||
cout << *.struc << endl;
|
||||
} else {
|
||||
cout << "nullptr\n"
|
||||
}
|
||||
}
|
||||
|
||||
Now it can be done as follows:
|
||||
|
||||
void print(StructureConstPtr struc, PVStructurePtr pv)
|
||||
{
|
||||
cout << struc << endl;
|
||||
cout << pv << endl;
|
||||
}
|
||||
|
||||
|
||||
New template version of Structure::getField
|
||||
--------------------------------------------
|
||||
|
||||
A new template getField method has been added to Structure
|
||||
|
||||
template<typename FT >
|
||||
std::tr1::shared_ptr< const FT > getField(std::string const &fieldName) const
|
||||
|
||||
Can be used, for example, as follows:
|
||||
|
||||
StructurePtr tsStruc = struc->getField<Structure>("timeStamp");
|
||||
|
||||
|
||||
Fixes for static initialisation order issues
|
||||
--------------------------------------------
|
||||
|
||||
Certain static builds (in particular Windows builds) of applications using
|
||||
pvData had issues due to PVStructure::DEFAULT_ID being used before being initialised. This has been fixed.
|
||||
|
||||
|
||||
CreateRequest change
|
||||
--------------------
|
||||
|
||||
createRequest could cause a SEGFAULT if passed a bad argument.
|
||||
This has been changed so the it returns a null pvStructure
|
||||
and provides an error.
|
||||
|
||||
|
||||
Release 4.0.3
|
||||
=============
|
||||
|
||||
The main changes since release 3.0.2 are:
|
||||
|
||||
- array semantics now enforce Copy On Write.
|
||||
- String no longer defined.
|
||||
- timeStamp and valueAlarm name changes
|
||||
- toString replaced by stream I/O
|
||||
- union is new type.
|
||||
- copy is new.
|
||||
- monitorPlugin is new.
|
||||
|
||||
New Semantics for Arrays
|
||||
--------
|
||||
|
||||
PVScalarArray, PVStructureArray, and PVUnionArray all enforce COW (Copy On Write) Semantics.
|
||||
In order to limit memory usage the storage for raw data is managed via a new shared_vector facility.
|
||||
This allows multiple instances of array data to use the shared raw data.
|
||||
COW is implemented via shared_vectors of const data, i. e. data that can not be modified.
|
||||
|
||||
|
||||
String no longer defined
|
||||
---------
|
||||
|
||||
This is replaced by std::string.
|
||||
|
||||
|
||||
timeStamp and valueAlarm name changes
|
||||
--------------
|
||||
|
||||
In timeStamp nanoSeconds is changed to nanoseconds.
|
||||
|
||||
In valueAlarm hysteresis is changed to hysteresis
|
||||
|
||||
|
||||
toString replaced by stream I/O
|
||||
---------
|
||||
|
||||
pvData.h and pvIntrospect no longer defines toString
|
||||
Instead they have stream support.
|
||||
pvIntrospect uses method dump and pvData uses dumpValue.
|
||||
For example:
|
||||
|
||||
PVDoublePtr pvValue;
|
||||
String buffer;
|
||||
pvValue->toString(&buffer);
|
||||
cout << buffer << endl;
|
||||
buffer.clear();
|
||||
pvValue->getField()->toString(&buffer);
|
||||
cout << buffer << evdl;
|
||||
|
||||
is replaced by
|
||||
|
||||
PVDoublePtr pvValue;
|
||||
cout << *pvValue << endl
|
||||
cout << *pvValue->getField() << endl;
|
||||
|
||||
|
||||
union is a new basic type.
|
||||
------------
|
||||
|
||||
There are two new basic types: union_t and unionArray.
|
||||
|
||||
A union is like a structure that has a single subfield.
|
||||
There are two flavors:
|
||||
|
||||
- *variant union* The field can have any type.
|
||||
- *union* The field can any of specified set of types.
|
||||
|
||||
The field type can be dynamically changed.
|
||||
|
||||
copy
|
||||
----
|
||||
|
||||
This consists of createRequest and pvCopy.
|
||||
createRequest was moved from pvAccess to here.
|
||||
pvCopy is moved from pvDatabaseCPP and now depends
|
||||
only on pvData, i.e. it no longer has any knowledge of PVRecord.
|
||||
|
||||
monitorPlugin
|
||||
-------------
|
||||
|
||||
This is for is for use by code that implements pvAccess monitors.
|
||||
This is prototype and is subject to debate.
|
||||
|
||||
Release 3.0.2
|
||||
==========
|
||||
This was the starting point for RELEASE_NOTES
|
||||
|
||||
*/
|
||||
14
examples/Makefile
Normal file
14
examples/Makefile
Normal file
@@ -0,0 +1,14 @@
|
||||
# Makefile for the examples
|
||||
# make sure they compile
|
||||
|
||||
TOP = ..
|
||||
include $(TOP)/configure/CONFIG
|
||||
|
||||
PROD_LIBS += pvData Com
|
||||
|
||||
TESTPROD_HOST += unittest
|
||||
unittest_SRCS += unittest.cpp
|
||||
|
||||
include $(TOP)/configure/RULES
|
||||
|
||||
|
||||
30
examples/unittest.cpp
Normal file
30
examples/unittest.cpp
Normal file
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright information and license terms for this software can be
|
||||
* found in the file LICENSE that is included with the distribution
|
||||
*/
|
||||
/* c++ unittest skeleton */
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
#include <testMain.h>
|
||||
|
||||
#include <pv/pvUnitTest.h>
|
||||
#include <pv/epicsException.h>
|
||||
|
||||
namespace {
|
||||
void testCase1() {
|
||||
testEqual(1+1, 2);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
MAIN(testUnitTest)
|
||||
{
|
||||
testPlan(1);
|
||||
try {
|
||||
testCase1();
|
||||
}catch(std::exception& e){
|
||||
PRINT_EXCEPTION(e); // print stack trace if thrown with THROW_EXCEPTION()
|
||||
testAbort("Unhandled exception: %s", e.what());
|
||||
}
|
||||
return testDone();
|
||||
}
|
||||
@@ -4,10 +4,33 @@
|
||||
# Jenkins invokes scripts with the "-ex" option. So the build is considered a failure
|
||||
# if any of the commands exits with a non-zero exit code.
|
||||
#
|
||||
# Author: Ralph Lange <Ralph.Lange@gmx.de>
|
||||
# Author: Ralph Lange <ralph.lange@gmx.de>
|
||||
# Copyright (C) 2013 Helmholtz-Zentrum Berlin für Materialien und Energie GmbH
|
||||
# Copyright (C) 2014-2016 ITER Organization.
|
||||
# All rights reserved. Use is subject to license terms.
|
||||
|
||||
installTool () {
|
||||
local module=$1
|
||||
local version=$2
|
||||
|
||||
wget -nv https://openepics.ci.cloudbees.com/job/${module}-${version}_Build/lastSuccessfulBuild/artifact/${module,,}-${version}.CB-dist.tar.gz
|
||||
tar -xzf ${module,,}-${version}.CB-dist.tar.gz
|
||||
}
|
||||
|
||||
installE4 () {
|
||||
local module=$1
|
||||
local branch=$2
|
||||
|
||||
wget -nv https://openepics.ci.cloudbees.com/job/e4-cpp-${module}-${branch}-build/BASE=${BASE}/lastSuccessfulBuild/artifact/${module}.CB-dist.tar.gz
|
||||
tar -xzf ${module}.CB-dist.tar.gz
|
||||
}
|
||||
|
||||
###########################################
|
||||
# Defaults for EPICS Base
|
||||
|
||||
DEFAULT_BASE=3.15.4
|
||||
BASE=${BASE:-${DEFAULT_BASE}}
|
||||
|
||||
###########################################
|
||||
# Fetch and unpack dependencies
|
||||
|
||||
@@ -17,10 +40,8 @@ rm -fr ${STUFF}
|
||||
mkdir -p ${STUFF}
|
||||
cd ${STUFF}
|
||||
|
||||
wget -nv https://openepics.ci.cloudbees.com/job/Base-3.14.12.3_Build/lastSuccessfulBuild/artifact/baseR3.14.12.3.CB-dist.tar.gz
|
||||
wget -nv https://openepics.ci.cloudbees.com/job/Doxygen-1.8.3_Build/lastSuccessfulBuild/artifact/doxygen-1.8.3.CB-dist.tar.gz
|
||||
tar -xzf baseR3.14.12.3.CB-dist.tar.gz
|
||||
tar -xzf doxygen-1.8.3.CB-dist.tar.gz
|
||||
installTool Boost 1.61.0
|
||||
installTool Base ${BASE}
|
||||
|
||||
###########################################
|
||||
# Build
|
||||
@@ -37,20 +58,13 @@ EPICS_BASE=${EPICS_BASE}
|
||||
EOF
|
||||
|
||||
make distclean all
|
||||
doxygen
|
||||
|
||||
###########################################
|
||||
# Test
|
||||
|
||||
# EPICS Test Harness tests
|
||||
make runtests
|
||||
|
||||
###########################################
|
||||
# Create distribution
|
||||
# Create cache
|
||||
|
||||
tar czf pvData.CB-dist.tar.gz lib include COPYRIGHT LICENSE
|
||||
|
||||
###########################################
|
||||
# Publish documentation
|
||||
|
||||
rsync -aP --delete -e ssh documentation epics-jenkins@web.sourceforge.net:/home/project-web/epics-pvdata/htdocs/docbuild/pvDataCPP/tip
|
||||
tar czf pvData.CB-dist.tar.gz lib include cfg LICENSE
|
||||
|
||||
66
jenkins/cloudbees_doc
Normal file
66
jenkins/cloudbees_doc
Normal file
@@ -0,0 +1,66 @@
|
||||
# pvData C++ implementation
|
||||
# Jenkins @ Cloudbees documentation generation and deployment
|
||||
#
|
||||
# Jenkins invokes scripts with the "-ex" option. So the build is considered a failure
|
||||
# if any of the commands exits with a non-zero exit code.
|
||||
#
|
||||
# Author: Ralph Lange <ralph.lange@gmx.de>
|
||||
# Copyright (C) 2013 Helmholtz-Zentrum Berlin für Materialien und Energie GmbH
|
||||
# Copyright (C) 2014-2016 ITER Organization.
|
||||
# All rights reserved. Use is subject to license terms.
|
||||
|
||||
installTool () {
|
||||
local module=$1
|
||||
local version=$2
|
||||
|
||||
wget -nv https://openepics.ci.cloudbees.com/job/${module}-${version}_Build/lastSuccessfulBuild/artifact/${module,,}-${version}.CB-dist.tar.gz
|
||||
tar -xzf ${module,,}-${version}.CB-dist.tar.gz
|
||||
}
|
||||
|
||||
installE4 () {
|
||||
local module=$1
|
||||
local branch=$2
|
||||
|
||||
wget -nv https://openepics.ci.cloudbees.com/job/e4-cpp-${module}-${branch}-build/BASE=${BASE}/lastSuccessfulBuild/artifact/${module}.CB-dist.tar.gz
|
||||
tar -xzf ${module}.CB-dist.tar.gz
|
||||
}
|
||||
|
||||
###########################################
|
||||
# Defaults for EPICS Base and parameters
|
||||
|
||||
BASE=3.15.4
|
||||
PUBLISH=${PUBLISH:-NO}
|
||||
BRANCH=${BRANCH:-master}
|
||||
|
||||
###########################################
|
||||
# Fetch and unpack dependencies
|
||||
|
||||
export STUFF=/tmp/stuff
|
||||
|
||||
rm -fr ${STUFF}
|
||||
mkdir -p ${STUFF}
|
||||
cd ${STUFF}
|
||||
|
||||
installTool Doxygen 1.8.11
|
||||
|
||||
###########################################
|
||||
# Generate
|
||||
|
||||
cd ${WORKSPACE}
|
||||
|
||||
installE4 pvData ${BRANCH}
|
||||
|
||||
export PATH=${STUFF}/bin:${PATH}
|
||||
|
||||
doxygen
|
||||
|
||||
###########################################
|
||||
# Publish
|
||||
|
||||
if [ "${PUBLISH}" != "NO" ]; then
|
||||
# Upload explicit dummy to ensure target directory exists
|
||||
echo "Created by CloudBees Jenkins upload job. Should be deleted as part of the job." > DUMMY
|
||||
rsync -q -e ssh DUMMY epics-jenkins@web.sourceforge.net:/home/project-web/epics-pvdata/htdocs/docbuild/pvDataCPP/${PUBLISH}/
|
||||
|
||||
rsync -aqP --delete -e ssh documentation epics-jenkins@web.sourceforge.net:/home/project-web/epics-pvdata/htdocs/docbuild/pvDataCPP/${PUBLISH}/
|
||||
fi
|
||||
@@ -1,20 +0,0 @@
|
||||
# pvData C++ implementation
|
||||
# Jenkins @ Cloudbees hgweb sync script
|
||||
#
|
||||
# Jenkins invokes scripts with the "-ex" option. So the build is considered a failure
|
||||
# if any of the commands exits with a non-zero exit code.
|
||||
#
|
||||
# Author: Ralph Lange <Ralph.Lange@gmx.de>
|
||||
# Copyright (C) 2013 Helmholtz-Zentrum Berlin für Materialien und Energie GmbH
|
||||
# All rights reserved. Use is subject to license terms.
|
||||
|
||||
###########################################
|
||||
# Fetch complete repo
|
||||
|
||||
rm -fr hgweb
|
||||
hg clone -U http://hg.code.sf.net/p/epics-pvdata/pvDataCPP hgweb
|
||||
|
||||
###########################################
|
||||
# Sync into SF webspace
|
||||
|
||||
rsync -aqP --delete --exclude=\.hg/hgrc -e ssh hgweb/.hg epics-jenkins@web.sourceforge.net:/home/project-web/epics-pvdata/repos/pvDataCPP
|
||||
@@ -1 +0,0 @@
|
||||
// ADD PREDEFINED MACROS HERE!
|
||||
@@ -1 +0,0 @@
|
||||
[General]
|
||||
491
pvDataCPP.files
491
pvDataCPP.files
@@ -1,491 +0,0 @@
|
||||
configure/CONFIG
|
||||
configure/CONFIG_SITE
|
||||
configure/RELEASE
|
||||
configure/RELEASE.local
|
||||
configure/RULES
|
||||
configure/RULES.ioc
|
||||
configure/RULES_DIRS
|
||||
configure/RULES_TOP
|
||||
documentation/pvDataCPP.html
|
||||
documentation/pvDataCPP_20120927.html
|
||||
documentation/pvDataCPP_20121001.html
|
||||
documentation/pvDataCPP_20121026.html
|
||||
documentation/pvDataCPP_20121212.html
|
||||
jenkins/cloudbees_build
|
||||
pvDataApp/factory/Compare.cpp
|
||||
pvDataApp/factory/Convert.cpp
|
||||
pvDataApp/factory/factory.h
|
||||
pvDataApp/factory/FieldCreateFactory.cpp
|
||||
pvDataApp/factory/PVArray.cpp
|
||||
pvDataApp/factory/PVAuxInfoImpl.cpp
|
||||
pvDataApp/factory/PVDataCreateFactory.cpp
|
||||
pvDataApp/factory/PVDataCreateFactory.cpp.orig
|
||||
pvDataApp/factory/PVField.cpp
|
||||
pvDataApp/factory/PVScalar.cpp
|
||||
pvDataApp/factory/PVScalarArray.cpp
|
||||
pvDataApp/factory/PVStructure.cpp
|
||||
pvDataApp/factory/PVStructureArray.cpp
|
||||
pvDataApp/factory/StandardField.cpp
|
||||
pvDataApp/factory/StandardPVField.cpp
|
||||
pvDataApp/factory/TypeFunc.cpp
|
||||
pvDataApp/misc/bitSet.cpp
|
||||
pvDataApp/misc/bitSet.h
|
||||
pvDataApp/misc/byteBuffer.cpp
|
||||
pvDataApp/misc/byteBuffer.h
|
||||
pvDataApp/misc/destroyable.h
|
||||
pvDataApp/misc/epicsException.cpp
|
||||
pvDataApp/misc/epicsException.h
|
||||
pvDataApp/misc/event.cpp
|
||||
pvDataApp/misc/event.h
|
||||
pvDataApp/misc/executor.cpp
|
||||
pvDataApp/misc/executor.h
|
||||
pvDataApp/misc/localStaticLock.cpp
|
||||
pvDataApp/misc/localStaticLock.h
|
||||
pvDataApp/misc/localStaticLock.h.orig
|
||||
pvDataApp/misc/lock.h
|
||||
pvDataApp/misc/messageQueue.cpp
|
||||
pvDataApp/misc/messageQueue.h
|
||||
pvDataApp/misc/noDefaultMethods.h
|
||||
pvDataApp/misc/queue.h
|
||||
pvDataApp/misc/requester.cpp
|
||||
pvDataApp/misc/requester.h
|
||||
pvDataApp/misc/requester.h.orig
|
||||
pvDataApp/misc/serialize.h
|
||||
pvDataApp/misc/serializeHelper.cpp
|
||||
pvDataApp/misc/serializeHelper.h
|
||||
pvDataApp/misc/sharedPtr.h
|
||||
pvDataApp/misc/status.cpp
|
||||
pvDataApp/misc/status.h
|
||||
pvDataApp/misc/thread.h
|
||||
pvDataApp/misc/timeFunction.cpp
|
||||
pvDataApp/misc/timeFunction.h
|
||||
pvDataApp/misc/timer.cpp
|
||||
pvDataApp/misc/timer.h
|
||||
pvDataApp/monitor/monitor.h
|
||||
pvDataApp/property/alarm.cpp
|
||||
pvDataApp/property/alarm.h
|
||||
pvDataApp/property/alarm.h.orig
|
||||
pvDataApp/property/control.h
|
||||
pvDataApp/property/display.h
|
||||
pvDataApp/property/pvAlarm.cpp
|
||||
pvDataApp/property/pvAlarm.h
|
||||
pvDataApp/property/pvControl.cpp
|
||||
pvDataApp/property/pvControl.h
|
||||
pvDataApp/property/pvDisplay.cpp
|
||||
pvDataApp/property/pvDisplay.h
|
||||
pvDataApp/property/pvEnumerated.cpp
|
||||
pvDataApp/property/pvEnumerated.h
|
||||
pvDataApp/property/pvTimeStamp.cpp
|
||||
pvDataApp/property/pvTimeStamp.h
|
||||
pvDataApp/property/timeStamp.cpp
|
||||
pvDataApp/property/timeStamp.h
|
||||
pvDataApp/property/timeStamp.h.orig
|
||||
pvDataApp/pv/convert.h
|
||||
pvDataApp/pv/pvData.h
|
||||
pvDataApp/pv/pvIntrospect.h
|
||||
pvDataApp/pv/pvType.h
|
||||
pvDataApp/pv/standardField.h
|
||||
pvDataApp/pv/standardPVField.h
|
||||
pvDataApp/pvMisc/bitSetUtil.cpp
|
||||
pvDataApp/pvMisc/bitSetUtil.h
|
||||
testApp/capi/testc.cpp
|
||||
testApp/capi/testc.py
|
||||
testApp/misc/testBaseException.cpp
|
||||
testApp/misc/testBitSet.cpp
|
||||
testApp/misc/testByteBuffer.cpp
|
||||
testApp/misc/testByteOrder.cpp
|
||||
testApp/misc/testMessageQueue.cpp
|
||||
testApp/misc/testQueue.cpp
|
||||
testApp/misc/testSerialization.cpp
|
||||
testApp/misc/testThread.cpp
|
||||
testApp/misc/testTimer.cpp
|
||||
testApp/misc/testTimeStamp.cpp
|
||||
testApp/monitor/testMonitor.cpp
|
||||
testApp/property/testProperty.cpp
|
||||
testApp/pv/testConvert.cpp
|
||||
testApp/pv/testIntrospect.cpp
|
||||
testApp/pv/testOperators.cpp
|
||||
testApp/pv/testPVAppend.cpp
|
||||
testApp/pv/testPVAuxInfo.cpp
|
||||
testApp/pv/testPVData.cpp
|
||||
testApp/pv/testPVScalarArray.cpp
|
||||
testApp/pv/testPVStructureArray.cpp
|
||||
testApp/pv/testPVType.cpp
|
||||
testApp/pv/testStandardField.cpp
|
||||
testApp/pv/testStandardPVField.cpp
|
||||
COPYRIGHT
|
||||
Doxyfile
|
||||
LICENSE
|
||||
README.html
|
||||
src/copy/createRequest.cpp
|
||||
src/copy/createRequest.h
|
||||
src/copy/pvCopy.cpp
|
||||
src/copy/pvCopy.h
|
||||
src/factory/Compare.cpp
|
||||
src/factory/Convert.cpp
|
||||
src/factory/factory.h
|
||||
src/factory/FieldCreateFactory.cpp
|
||||
src/factory/printer.cpp
|
||||
src/factory/PVArray.cpp
|
||||
src/factory/PVDataCreateFactory.cpp
|
||||
src/factory/PVField.cpp
|
||||
src/factory/PVScalar.cpp
|
||||
src/factory/PVScalarArray.cpp
|
||||
src/factory/PVStructure.cpp
|
||||
src/factory/PVStructureArray.cpp
|
||||
src/factory/pvSubArrayCopy.cpp
|
||||
src/factory/PVUnion.cpp
|
||||
src/factory/PVUnionArray.cpp
|
||||
src/factory/StandardField.cpp
|
||||
src/factory/StandardPVField.cpp
|
||||
src/factory/TypeFunc.cpp
|
||||
src/misc/bitSet.cpp
|
||||
src/misc/bitSet.h
|
||||
src/misc/byteBuffer.cpp
|
||||
src/misc/byteBuffer.h
|
||||
src/misc/current_function.h
|
||||
src/misc/destroyable.h
|
||||
src/misc/epicsException.cpp
|
||||
src/misc/epicsException.h
|
||||
src/misc/event.cpp
|
||||
src/misc/event.h
|
||||
src/misc/executor.cpp
|
||||
src/misc/executor.h
|
||||
src/misc/localStaticLock.cpp
|
||||
src/misc/localStaticLock.h
|
||||
src/misc/lock.h
|
||||
src/misc/messageQueue.cpp
|
||||
src/misc/messageQueue.h
|
||||
src/misc/noDefaultMethods.h
|
||||
src/misc/parseToPOD.cpp
|
||||
src/misc/queue.h
|
||||
src/misc/requester.cpp
|
||||
src/misc/requester.h
|
||||
src/misc/serialize.h
|
||||
src/misc/serializeHelper.cpp
|
||||
src/misc/serializeHelper.h
|
||||
src/misc/sharedPtr.h
|
||||
src/misc/sharedVector.h
|
||||
src/misc/status.cpp
|
||||
src/misc/status.h
|
||||
src/misc/templateMeta.h
|
||||
src/misc/thread.h
|
||||
src/misc/timeFunction.cpp
|
||||
src/misc/timeFunction.h
|
||||
src/misc/timer.cpp
|
||||
src/misc/timer.h
|
||||
src/misc/typeCast.cpp
|
||||
src/misc/typeCast.h
|
||||
src/monitor/monitor.cpp
|
||||
src/monitor/monitor.h
|
||||
src/monitor/monitorPlugin.cpp
|
||||
src/monitor/monitorPlugin.h
|
||||
src/property/alarm.cpp
|
||||
src/property/alarm.h
|
||||
src/property/control.h
|
||||
src/property/display.h
|
||||
src/property/pvAlarm.cpp
|
||||
src/property/pvAlarm.h
|
||||
src/property/pvControl.cpp
|
||||
src/property/pvControl.h
|
||||
src/property/pvDisplay.cpp
|
||||
src/property/pvDisplay.h
|
||||
src/property/pvEnumerated.cpp
|
||||
src/property/pvEnumerated.h
|
||||
src/property/pvTimeStamp.cpp
|
||||
src/property/pvTimeStamp.h
|
||||
src/property/timeStamp.cpp
|
||||
src/property/timeStamp.h
|
||||
src/pv/convert.h
|
||||
src/pv/printer.h
|
||||
src/pv/pvData.h
|
||||
src/pv/pvIntrospect.h
|
||||
src/pv/pvSubArrayCopy.h
|
||||
src/pv/pvType.h
|
||||
src/pv/standardField.h
|
||||
src/pv/standardPVField.h
|
||||
src/pvMisc/bitSetUtil.cpp
|
||||
src/pvMisc/bitSetUtil.h
|
||||
testApp/copy/testCreateRequest.cpp
|
||||
testApp/copy/testPVCopy.cpp
|
||||
testApp/misc/testBaseException.cpp
|
||||
testApp/misc/testBitSet.cpp
|
||||
testApp/misc/testByteBuffer.cpp
|
||||
testApp/misc/testByteOrder.cpp
|
||||
testApp/misc/testMessageQueue.cpp
|
||||
testApp/misc/testOverrunBitSet.cpp
|
||||
testApp/misc/testQueue.cpp
|
||||
testApp/misc/testSerialization.cpp
|
||||
testApp/misc/testSharedVector.cpp
|
||||
testApp/misc/testThread.cpp
|
||||
testApp/misc/testTimer.cpp
|
||||
testApp/misc/testTimeStamp.cpp
|
||||
testApp/misc/testTypeCast.cpp
|
||||
testApp/property/testProperty.cpp
|
||||
testApp/pv/testBitSetUtil.cpp
|
||||
testApp/pv/testConvert.cpp
|
||||
testApp/pv/testFieldBuilder.cpp
|
||||
testApp/pv/testIntrospect.cpp
|
||||
testApp/pv/testOperators.cpp
|
||||
testApp/pv/testPVData.cpp
|
||||
testApp/pv/testPVScalarArray.cpp
|
||||
testApp/pv/testPVStructureArray.cpp
|
||||
testApp/pv/testPVType.cpp
|
||||
testApp/pv/testStandardField.cpp
|
||||
testApp/pv/testStandardPVField.cpp
|
||||
src/factory/printer.cpp
|
||||
configure/O.darwin-x86/Makefile
|
||||
configure/Makefile
|
||||
include/pv/alarm.h
|
||||
include/pv/bitSet.h
|
||||
include/pv/bitSetUtil.h
|
||||
include/pv/byteBuffer.h
|
||||
include/pv/control.h
|
||||
include/pv/convert.h
|
||||
include/pv/createRequest.h
|
||||
include/pv/current_function.h
|
||||
include/pv/destroyable.h
|
||||
include/pv/display.h
|
||||
include/pv/epicsException.h
|
||||
include/pv/event.h
|
||||
include/pv/executor.h
|
||||
include/pv/factory.h
|
||||
include/pv/localStaticLock.h
|
||||
include/pv/lock.h
|
||||
include/pv/messageQueue.h
|
||||
include/pv/monitor.h
|
||||
include/pv/monitorPlugin.h
|
||||
include/pv/noDefaultMethods.h
|
||||
include/pv/printer.h
|
||||
include/pv/pvAlarm.h
|
||||
include/pv/pvControl.h
|
||||
include/pv/pvCopy.h
|
||||
include/pv/pvData.h
|
||||
include/pv/pvDisplay.h
|
||||
include/pv/pvEnumerated.h
|
||||
include/pv/pvIntrospect.h
|
||||
include/pv/pvSubArrayCopy.h
|
||||
include/pv/pvTimeStamp.h
|
||||
include/pv/pvType.h
|
||||
include/pv/queue.h
|
||||
include/pv/requester.h
|
||||
include/pv/serialize.h
|
||||
include/pv/serializeHelper.h
|
||||
include/pv/sharedPtr.h
|
||||
include/pv/sharedVector.h
|
||||
include/pv/standardField.h
|
||||
include/pv/standardPVField.h
|
||||
include/pv/status.h
|
||||
include/pv/templateMeta.h
|
||||
include/pv/thread.h
|
||||
include/pv/timeFunction.h
|
||||
include/pv/timer.h
|
||||
include/pv/timeStamp.h
|
||||
include/pv/typeCast.h
|
||||
src/copy/createRequest.cpp
|
||||
src/copy/createRequest.h
|
||||
src/copy/Makefile
|
||||
src/copy/pvCopy.cpp
|
||||
src/copy/pvCopy.h
|
||||
src/factory/Compare.cpp
|
||||
src/factory/Convert.cpp
|
||||
src/factory/factory.h
|
||||
src/factory/FieldCreateFactory.cpp
|
||||
src/factory/Makefile
|
||||
src/factory/printer.cpp
|
||||
src/factory/PVArray.cpp
|
||||
src/factory/PVDataCreateFactory.cpp
|
||||
src/factory/PVField.cpp
|
||||
src/factory/PVScalar.cpp
|
||||
src/factory/PVScalarArray.cpp
|
||||
src/factory/PVStructure.cpp
|
||||
src/factory/PVStructureArray.cpp
|
||||
src/factory/pvSubArrayCopy.cpp
|
||||
src/factory/PVUnion.cpp
|
||||
src/factory/PVUnionArray.cpp
|
||||
src/factory/StandardField.cpp
|
||||
src/factory/StandardPVField.cpp
|
||||
src/factory/TypeFunc.cpp
|
||||
src/misc/bitSet.cpp
|
||||
src/misc/bitSet.h
|
||||
src/misc/byteBuffer.cpp
|
||||
src/misc/byteBuffer.h
|
||||
src/misc/current_function.h
|
||||
src/misc/destroyable.h
|
||||
src/misc/epicsException.cpp
|
||||
src/misc/epicsException.h
|
||||
src/misc/event.cpp
|
||||
src/misc/event.h
|
||||
src/misc/executor.cpp
|
||||
src/misc/executor.h
|
||||
src/misc/localStaticLock.cpp
|
||||
src/misc/localStaticLock.h
|
||||
src/misc/lock.h
|
||||
src/misc/Makefile
|
||||
src/misc/messageQueue.cpp
|
||||
src/misc/messageQueue.h
|
||||
src/misc/noDefaultMethods.h
|
||||
src/misc/parseToPOD.cpp
|
||||
src/misc/queue.h
|
||||
src/misc/requester.cpp
|
||||
src/misc/requester.h
|
||||
src/misc/serialize.h
|
||||
src/misc/serializeHelper.cpp
|
||||
src/misc/serializeHelper.h
|
||||
src/misc/sharedPtr.h
|
||||
src/misc/sharedVector.h
|
||||
src/misc/status.cpp
|
||||
src/misc/status.h
|
||||
src/misc/templateMeta.h
|
||||
src/misc/thread.h
|
||||
src/misc/timeFunction.cpp
|
||||
src/misc/timeFunction.h
|
||||
src/misc/timer.cpp
|
||||
src/misc/timer.h
|
||||
src/misc/typeCast.cpp
|
||||
src/misc/typeCast.h
|
||||
src/monitor/Makefile
|
||||
src/monitor/monitor.cpp
|
||||
src/monitor/monitor.h
|
||||
src/monitor/monitorPlugin.cpp
|
||||
src/monitor/monitorPlugin.h
|
||||
src/O.darwin-x86/Makefile
|
||||
src/property/alarm.cpp
|
||||
src/property/alarm.h
|
||||
src/property/control.h
|
||||
src/property/display.h
|
||||
src/property/Makefile
|
||||
src/property/pvAlarm.cpp
|
||||
src/property/pvAlarm.h
|
||||
src/property/pvControl.cpp
|
||||
src/property/pvControl.h
|
||||
src/property/pvDisplay.cpp
|
||||
src/property/pvDisplay.h
|
||||
src/property/pvEnumerated.cpp
|
||||
src/property/pvEnumerated.h
|
||||
src/property/pvTimeStamp.cpp
|
||||
src/property/pvTimeStamp.h
|
||||
src/property/timeStamp.cpp
|
||||
src/property/timeStamp.h
|
||||
src/pv/convert.h
|
||||
src/pv/Makefile
|
||||
src/pv/printer.h
|
||||
src/pv/pvData.h
|
||||
src/pv/pvIntrospect.h
|
||||
src/pv/pvSubArrayCopy.h
|
||||
src/pv/pvType.h
|
||||
src/pv/standardField.h
|
||||
src/pv/standardPVField.h
|
||||
src/pvMisc/bitSetUtil.cpp
|
||||
src/pvMisc/bitSetUtil.h
|
||||
src/pvMisc/Makefile
|
||||
src/Makefile
|
||||
testApp/copy/epicsRunPVDataTests.c
|
||||
testApp/copy/Makefile
|
||||
testApp/copy/testCreateRequest.cpp
|
||||
testApp/copy/testPVCopy.cpp
|
||||
testApp/misc/epicsRunPVDataTests.c
|
||||
testApp/misc/Makefile
|
||||
testApp/misc/testBaseException.cpp
|
||||
testApp/misc/testBitSet.cpp
|
||||
testApp/misc/testByteBuffer.cpp
|
||||
testApp/misc/testByteOrder.cpp
|
||||
testApp/misc/testMessageQueue.cpp
|
||||
testApp/misc/testOverrunBitSet.cpp
|
||||
testApp/misc/testQueue.cpp
|
||||
testApp/misc/testSerialization.cpp
|
||||
testApp/misc/testSharedVector.cpp
|
||||
testApp/misc/testThread.cpp
|
||||
testApp/misc/testTimer.cpp
|
||||
testApp/misc/testTimeStamp.cpp
|
||||
testApp/misc/testTypeCast.cpp
|
||||
testApp/property/epicsRunPVDataTests.c
|
||||
testApp/property/Makefile
|
||||
testApp/property/testProperty.cpp
|
||||
testApp/pv/epicsRunPVDataTests.c
|
||||
testApp/pv/Makefile
|
||||
testApp/pv/testBitSetUtil.cpp
|
||||
testApp/pv/testConvert.cpp
|
||||
testApp/pv/testFieldBuilder.cpp
|
||||
testApp/pv/testIntrospect.cpp
|
||||
testApp/pv/testOperators.cpp
|
||||
testApp/pv/testPVData.cpp
|
||||
testApp/pv/testPVScalarArray.cpp
|
||||
testApp/pv/testPVStructureArray.cpp
|
||||
testApp/pv/testPVType.cpp
|
||||
testApp/pv/testPVUnion.cpp
|
||||
testApp/pv/testStandardField.cpp
|
||||
testApp/pv/testStandardPVField.cpp
|
||||
testApp/Makefile
|
||||
Makefile
|
||||
testApp/copy/epicsRunPVDataTests.c
|
||||
testApp/copy/Makefile
|
||||
testApp/copy/testCreateRequest.cpp
|
||||
testApp/copy/testPVCopy.cpp
|
||||
testApp/misc/epicsRunPVDataTests.c
|
||||
testApp/misc/Makefile
|
||||
testApp/misc/testBaseException.cpp
|
||||
testApp/misc/testBitSet.cpp
|
||||
testApp/misc/testByteBuffer.cpp
|
||||
testApp/misc/testByteOrder.cpp
|
||||
testApp/misc/testMessageQueue.cpp
|
||||
testApp/misc/testOverrunBitSet.cpp
|
||||
testApp/misc/testQueue.cpp
|
||||
testApp/misc/testSerialization.cpp
|
||||
testApp/misc/testSharedVector.cpp
|
||||
testApp/misc/testThread.cpp
|
||||
testApp/misc/testTimer.cpp
|
||||
testApp/misc/testTimeStamp.cpp
|
||||
testApp/misc/testTypeCast.cpp
|
||||
testApp/O.darwin-x86/Makefile
|
||||
testApp/property/epicsRunPVDataTests.c
|
||||
testApp/property/Makefile
|
||||
testApp/property/testProperty.cpp
|
||||
testApp/pv/epicsRunPVDataTests.c
|
||||
testApp/pv/Makefile
|
||||
testApp/pv/testBitSetUtil.cpp
|
||||
testApp/pv/testConvert.cpp
|
||||
testApp/pv/testFieldBuilder.cpp
|
||||
testApp/pv/testIntrospect.cpp
|
||||
testApp/pv/testOperators.cpp
|
||||
testApp/pv/testPVData.cpp
|
||||
testApp/pv/testPVScalarArray.cpp
|
||||
testApp/pv/testPVStructureArray.cpp
|
||||
testApp/pv/testPVType.cpp
|
||||
testApp/pv/testPVUnion.cpp
|
||||
testApp/pv/testStandardField.cpp
|
||||
testApp/pv/testStandardPVField.cpp
|
||||
testApp/Makefile
|
||||
testApp/pvDataAllTests.c
|
||||
testApp/copy/testCreateRequest.cpp
|
||||
testApp/copy/testPVCopy.cpp
|
||||
testApp/misc/testBaseException.cpp
|
||||
testApp/misc/testBitSet.cpp
|
||||
testApp/misc/testByteBuffer.cpp
|
||||
testApp/misc/testByteOrder.cpp
|
||||
testApp/misc/testMessageQueue.cpp
|
||||
testApp/misc/testOverrunBitSet.cpp
|
||||
testApp/misc/testQueue.cpp
|
||||
testApp/misc/testSerialization.cpp
|
||||
testApp/misc/testSharedVector.cpp
|
||||
testApp/misc/testThread.cpp
|
||||
testApp/misc/testTimer.cpp
|
||||
testApp/misc/testTimeStamp.cpp
|
||||
testApp/misc/testTypeCast.cpp
|
||||
testApp/property/testProperty.cpp
|
||||
testApp/pv/testBitSetUtil.cpp
|
||||
testApp/pv/testConvert.cpp
|
||||
testApp/pv/testFieldBuilder.cpp
|
||||
testApp/pv/testIntrospect.cpp
|
||||
testApp/pv/testOperators.cpp
|
||||
testApp/pv/testPVData.cpp
|
||||
testApp/pv/testPVScalarArray.cpp
|
||||
testApp/pv/testPVStructureArray.cpp
|
||||
testApp/pv/testPVType.cpp
|
||||
testApp/pv/testPVUnion.cpp
|
||||
testApp/pv/testStandardField.cpp
|
||||
testApp/pv/testStandardPVField.cpp
|
||||
testApp/pvDataAllTests.c
|
||||
testApp/rtemsConfig.c
|
||||
testApp/rtemsNetworking.h
|
||||
testApp/rtemsTestHarness.c
|
||||
@@ -1,11 +0,0 @@
|
||||
/home/msekoranja/epicsV4/pvDataCPP/pvDataApp/factory
|
||||
/home/msekoranja/epicsV4/pvDataCPP/pvDataApp/misc
|
||||
/home/msekoranja/epicsV4/pvDataCPP/pvDataApp/monitor
|
||||
/home/msekoranja/epicsV4/pvDataCPP/pvDataApp/property
|
||||
/home/msekoranja/epicsV4/pvDataCPP/pvDataApp/pv
|
||||
/home/msekoranja/epicsV4/pvDataCPP/pvDataApp/pvMisc
|
||||
testApp/pv
|
||||
testApp/copy
|
||||
testApp/misc
|
||||
testApp
|
||||
testApp/property
|
||||
20
src/Makefile
20
src/Makefile
@@ -3,9 +3,6 @@
|
||||
TOP = ..
|
||||
include $(TOP)/configure/CONFIG
|
||||
|
||||
INSTALL_INCLUDE = $(INSTALL_LOCATION)/include/pv
|
||||
USR_INCLUDES += -I$(INSTALL_LOCATION)/include
|
||||
|
||||
PVDATA_SRC = $(TOP)/src
|
||||
|
||||
include $(PVDATA_SRC)/misc/Makefile
|
||||
@@ -14,11 +11,26 @@ include $(PVDATA_SRC)/factory/Makefile
|
||||
include $(PVDATA_SRC)/property/Makefile
|
||||
include $(PVDATA_SRC)/copy/Makefile
|
||||
include $(PVDATA_SRC)/pvMisc/Makefile
|
||||
include $(PVDATA_SRC)/monitor/Makefile
|
||||
include $(PVDATA_SRC)/json/Makefile
|
||||
|
||||
LIBRARY = pvData
|
||||
|
||||
pvData_LIBS += Com
|
||||
|
||||
EXPANDVARS += EPICS_PVD_MAJOR_VERSION
|
||||
EXPANDVARS += EPICS_PVD_MINOR_VERSION
|
||||
EXPANDVARS += EPICS_PVD_MAINTENANCE_VERSION
|
||||
EXPANDVARS += EPICS_PVD_DEVELOPMENT_FLAG
|
||||
|
||||
EXPANDFLAGS += $(foreach var,$(EXPANDVARS),-D$(var)="$(strip $($(var)))")
|
||||
|
||||
# shared library ABI version.
|
||||
SHRLIB_VERSION = $(EPICS_PVD_MAJOR_VERSION).$(EPICS_PVD_MINOR_VERSION).$(EPICS_PVD_MAINTENANCE_VERSION)
|
||||
|
||||
include $(TOP)/configure/RULES
|
||||
|
||||
# Can't use EXPAND as generated headers must appear
|
||||
# in O.Common, but EXPAND emits rules for O.$(T_A)
|
||||
../O.Common/pv/pvdVersionNum.h: ../pv/pvdVersionNum.h@
|
||||
$(MKDIR) $(COMMON_DIR)/pv
|
||||
$(EXPAND_TOOL) $(EXPANDFLAGS) $($@_EXPANDFLAGS) $< $@
|
||||
|
||||
@@ -2,8 +2,9 @@
|
||||
|
||||
SRC_DIRS += $(PVDATA_SRC)/copy
|
||||
|
||||
INC += createRequest.h
|
||||
INC += pvCopy.h
|
||||
INC += pv/createRequest.h
|
||||
INC += pv/pvCopy.h
|
||||
|
||||
LIBSRCS += createRequest.cpp
|
||||
LIBSRCS += requestmapper.cpp
|
||||
LIBSRCS += pvCopy.cpp
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
/**
|
||||
* Copyright - See the COPYRIGHT that is included with this distribution.
|
||||
* pvAccessCPP is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
/*
|
||||
* Copyright information and license terms for this software can be
|
||||
* found in the file LICENSE that is included with the distribution
|
||||
*/
|
||||
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
|
||||
#include <epicsMutex.h>
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
|
||||
#include <pv/pvData.h>
|
||||
@@ -21,13 +22,13 @@ using std::endl;
|
||||
using std::string;
|
||||
using std::vector;
|
||||
|
||||
namespace epics { namespace pvData {
|
||||
namespace {
|
||||
using namespace epics::pvData;
|
||||
|
||||
static PVDataCreatePtr pvDataCreate = getPVDataCreate();
|
||||
static FieldCreatePtr fieldCreate = getFieldCreate();
|
||||
|
||||
class CreateRequestImpl : public CreateRequest {
|
||||
private:
|
||||
struct CreateRequestImpl {
|
||||
|
||||
struct Node
|
||||
{
|
||||
@@ -53,12 +54,7 @@ private:
|
||||
string fullFieldName;
|
||||
|
||||
|
||||
public:
|
||||
CreateRequestImpl()
|
||||
{
|
||||
fullFieldName = "";
|
||||
}
|
||||
private:
|
||||
CreateRequestImpl() {}
|
||||
|
||||
|
||||
void removeBlanks(string& str)
|
||||
@@ -74,8 +70,7 @@ private:
|
||||
size_t openBrace = request.find('{', index+1);
|
||||
size_t closeBrace = request.find('}', index+1);
|
||||
if(openBrace == string::npos && closeBrace == string::npos){
|
||||
message = request + " mismatched {}";
|
||||
throw std::logic_error("message");
|
||||
throw std::runtime_error(request + " mismatched {}");
|
||||
}
|
||||
if (openBrace != string::npos && openBrace!=0) {
|
||||
if(openBrace<closeBrace) return findMatchingBrace(request,openBrace,numOpen+1);
|
||||
@@ -90,14 +85,12 @@ private:
|
||||
for(size_t i=index+1; i< request.size(); ++i) {
|
||||
if(request[i] == ']') {
|
||||
if(i==index+1) {
|
||||
message = request + " mismatched []";
|
||||
throw std::logic_error("message");
|
||||
throw std::runtime_error(request + " mismatched []");
|
||||
}
|
||||
return i;
|
||||
}
|
||||
}
|
||||
message = request + " missing ]";
|
||||
throw std::logic_error("message");
|
||||
throw std::runtime_error(request + " missing ]");
|
||||
}
|
||||
|
||||
size_t findEndField(string& request) {
|
||||
@@ -152,7 +145,7 @@ private:
|
||||
string const & request)
|
||||
{
|
||||
if(request.length()<=1) {
|
||||
throw std::logic_error("logic error empty options");
|
||||
throw std::runtime_error("logic error empty options");
|
||||
}
|
||||
vector<Node> top;
|
||||
vector<string> items = split(request);
|
||||
@@ -162,8 +155,7 @@ private:
|
||||
string item = items[j];
|
||||
size_t equals = item.find('=');
|
||||
if(equals==string::npos || equals==0) {
|
||||
message = item + " illegal option " + request;
|
||||
throw std::logic_error("message");
|
||||
throw std::runtime_error(item + " illegal option " + request);
|
||||
}
|
||||
top.push_back(Node(item.substr(0,equals)));
|
||||
string name = fullFieldName + "._options." + item.substr(0,equals);
|
||||
@@ -212,7 +204,7 @@ private:
|
||||
if(end==0) end = request.size();
|
||||
string name = request.substr(0,end);
|
||||
if(name.size()<1) {
|
||||
throw std::logic_error("null field name " + request);
|
||||
throw std::runtime_error("null field name " + request);
|
||||
}
|
||||
string saveFullName = fullFieldName;
|
||||
fullFieldName += "." + name;
|
||||
@@ -235,7 +227,7 @@ private:
|
||||
if(chr=='.') {
|
||||
request = request.substr(end+1);
|
||||
if(request.size()==string::npos || request.size()<1) {
|
||||
throw std::logic_error("null field name " + request);
|
||||
throw std::runtime_error("null field name " + request);
|
||||
}
|
||||
Node subNode(name);
|
||||
if(optionNode.name.size()>0) subNode.nodes.push_back(optionNode);
|
||||
@@ -256,11 +248,11 @@ private:
|
||||
if(chr=='{') {
|
||||
size_t endBrace = findEndField(request);
|
||||
if((end+1)>=(endBrace-1)) {
|
||||
throw std::logic_error("illegal syntax " + request);
|
||||
throw std::runtime_error("illegal syntax " + request);
|
||||
}
|
||||
string subRequest = request.substr(end+1,endBrace-1 -end -1);
|
||||
if(subRequest.size()<1) {
|
||||
throw std::logic_error("empty {} " + request);
|
||||
throw std::runtime_error("empty {} " + request);
|
||||
}
|
||||
Node subNode(name);
|
||||
if(optionNode.name.size()>0) subNode.nodes.push_back(optionNode);
|
||||
@@ -276,7 +268,7 @@ private:
|
||||
createSubNode(node,request);
|
||||
return;
|
||||
}
|
||||
throw std::logic_error("logic error");
|
||||
throw std::runtime_error("logic error");
|
||||
}
|
||||
|
||||
FieldConstPtr createSubStructure(vector<Node> & nodes)
|
||||
@@ -317,161 +309,178 @@ private:
|
||||
return structure;
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
virtual PVStructurePtr createRequest(
|
||||
PVStructurePtr createRequest(
|
||||
string const & crequest)
|
||||
{
|
||||
string request = crequest;
|
||||
if (!request.empty()) removeBlanks(request);
|
||||
if (request.empty())
|
||||
{
|
||||
return pvDataCreate->createPVStructure(fieldCreate->createStructure());
|
||||
}
|
||||
size_t offsetRecord = request.find("record[");
|
||||
size_t offsetField = request.find("field(");
|
||||
size_t offsetPutField = request.find("putField(");
|
||||
size_t offsetGetField = request.find("getField(");
|
||||
if(offsetRecord==string::npos
|
||||
&& offsetField==string::npos
|
||||
&& offsetPutField==string::npos
|
||||
&& offsetGetField==string::npos)
|
||||
{
|
||||
request = "field(" + request + ")";
|
||||
offsetField = request.find("field(");
|
||||
}
|
||||
int numParan = 0;
|
||||
int numBrace = 0;
|
||||
int numBracket = 0;
|
||||
for(size_t i=0; i< request.length() ; ++i) {
|
||||
char chr = request[i];
|
||||
if(chr=='(') numParan++;
|
||||
if(chr==')') numParan--;
|
||||
if(chr=='{') numBrace++;
|
||||
if(chr=='}') numBrace--;
|
||||
if(chr=='[') numBracket++;
|
||||
if(chr==']') numBracket--;
|
||||
}
|
||||
if(numParan!=0) {
|
||||
ostringstream oss;
|
||||
oss << "mismatched () " << numParan;
|
||||
message = oss.str();
|
||||
return PVStructurePtr();
|
||||
}
|
||||
if(numBrace!=0) {
|
||||
ostringstream oss;
|
||||
oss << "mismatched {} " << numBrace;
|
||||
message = oss.str();
|
||||
return PVStructurePtr();
|
||||
}
|
||||
if(numBracket!=0) {
|
||||
ostringstream oss;
|
||||
oss << "mismatched [] " << numBracket;
|
||||
message = oss.str();
|
||||
return PVStructurePtr();
|
||||
}
|
||||
vector<Node> top;
|
||||
try {
|
||||
if(offsetRecord!=string::npos) {
|
||||
fullFieldName = "record";
|
||||
size_t openBracket = request.find('[', offsetRecord);
|
||||
size_t closeBracket = request.find(']', openBracket);
|
||||
if(closeBracket==string::npos) {
|
||||
message = request.substr(offsetRecord) +
|
||||
"record[ does not have matching ]";
|
||||
return PVStructurePtr();
|
||||
string request = crequest;
|
||||
if (!request.empty()) removeBlanks(request);
|
||||
if (request.empty())
|
||||
{
|
||||
return fieldCreate->createStructure()->build();
|
||||
}
|
||||
size_t offsetRecord = request.find("record[");
|
||||
size_t offsetField = request.find("field(");
|
||||
size_t offsetPutField = request.find("putField(");
|
||||
size_t offsetGetField = request.find("getField(");
|
||||
if(offsetRecord==string::npos
|
||||
&& offsetField==string::npos
|
||||
&& offsetPutField==string::npos
|
||||
&& offsetGetField==string::npos)
|
||||
{
|
||||
request = "field(" + request + ")";
|
||||
offsetField = request.find("field(");
|
||||
}
|
||||
int numParan = 0;
|
||||
int numBrace = 0;
|
||||
int numBracket = 0;
|
||||
for(size_t i=0; i< request.length() ; ++i) {
|
||||
char chr = request[i];
|
||||
if(chr=='(') numParan++;
|
||||
if(chr==')') numParan--;
|
||||
if(chr=='{') numBrace++;
|
||||
if(chr=='}') numBrace--;
|
||||
if(chr=='[') numBracket++;
|
||||
if(chr==']') numBracket--;
|
||||
}
|
||||
if(numParan!=0) {
|
||||
ostringstream oss;
|
||||
oss << "mismatched () " << numParan;
|
||||
throw std::runtime_error(oss.str());
|
||||
}
|
||||
if(numBrace!=0) {
|
||||
ostringstream oss;
|
||||
oss << "mismatched {} " << numBrace;
|
||||
throw std::runtime_error(oss.str());
|
||||
}
|
||||
if(numBracket!=0) {
|
||||
ostringstream oss;
|
||||
oss << "mismatched [] " << numBracket;
|
||||
throw std::runtime_error(oss.str());
|
||||
}
|
||||
vector<Node> top;
|
||||
try {
|
||||
if(offsetRecord!=string::npos) {
|
||||
fullFieldName = "record";
|
||||
size_t openBracket = request.find('[', offsetRecord);
|
||||
size_t closeBracket = request.find(']', openBracket);
|
||||
if(closeBracket==string::npos) {
|
||||
throw std::runtime_error(request.substr(offsetRecord) +
|
||||
"record[ does not have matching ]");
|
||||
}
|
||||
if(closeBracket-openBracket > 3) {
|
||||
Node node("record");
|
||||
Node optNode = createRequestOptions(
|
||||
request.substr(openBracket+1,closeBracket-openBracket-1));
|
||||
node.nodes.push_back(optNode);
|
||||
top.push_back(node);
|
||||
}
|
||||
}
|
||||
if(closeBracket-openBracket > 3) {
|
||||
Node node("record");
|
||||
Node optNode = createRequestOptions(
|
||||
request.substr(openBracket+1,closeBracket-openBracket-1));
|
||||
node.nodes.push_back(optNode);
|
||||
if(offsetField!=string::npos) {
|
||||
fullFieldName = "field";
|
||||
Node node("field");
|
||||
size_t openParan = request.find('(', offsetField);
|
||||
size_t closeParan = request.find(')', openParan);
|
||||
if(closeParan==string::npos) {
|
||||
throw std::runtime_error(request.substr(offsetField)
|
||||
+ " field( does not have matching )");
|
||||
}
|
||||
if(closeParan>openParan+1) {
|
||||
createSubNode(node,request.substr(openParan+1,closeParan-openParan-1));
|
||||
}
|
||||
top.push_back(node);
|
||||
}
|
||||
if(offsetGetField!=string::npos) {
|
||||
fullFieldName = "getField";
|
||||
Node node("getField");
|
||||
size_t openParan = request.find('(', offsetGetField);
|
||||
size_t closeParan = request.find(')', openParan);
|
||||
if(closeParan==string::npos) {
|
||||
throw std::runtime_error(request.substr(offsetField)
|
||||
+ " getField( does not have matching )");
|
||||
}
|
||||
if(closeParan>openParan+1) {
|
||||
createSubNode(node,request.substr(openParan+1,closeParan-openParan-1));
|
||||
}
|
||||
top.push_back(node);
|
||||
}
|
||||
if(offsetPutField!=string::npos) {
|
||||
fullFieldName = "putField";
|
||||
Node node("putField");
|
||||
size_t openParan = request.find('(', offsetPutField);
|
||||
size_t closeParan = request.find(')', openParan);
|
||||
if(closeParan==string::npos) {
|
||||
throw std::runtime_error(request.substr(offsetField)
|
||||
+ " putField( does not have matching )");
|
||||
}
|
||||
if(closeParan>openParan+1) {
|
||||
createSubNode(node,request.substr(openParan+1,closeParan-openParan-1));
|
||||
}
|
||||
top.push_back(node);
|
||||
}
|
||||
} catch (std::exception &e) {
|
||||
throw std::runtime_error(std::string("while creating Structure exception ")+e.what());
|
||||
}
|
||||
if(offsetField!=string::npos) {
|
||||
fullFieldName = "field";
|
||||
Node node("field");
|
||||
size_t openParan = request.find('(', offsetField);
|
||||
size_t closeParan = request.find(')', openParan);
|
||||
if(closeParan==string::npos) {
|
||||
message = request.substr(offsetField)
|
||||
+ " field( does not have matching )";
|
||||
return PVStructurePtr();
|
||||
size_t num = top.size();
|
||||
StringArray names(num);
|
||||
FieldConstPtrArray fields(num);
|
||||
for(size_t i=0; i<num; ++i) {
|
||||
Node node = top[i];
|
||||
names[i] = node.name;
|
||||
vector<Node> subNode = node.nodes;
|
||||
if(subNode.empty()) {
|
||||
fields[i] = fieldCreate->createStructure();
|
||||
} else {
|
||||
fields[i] = createSubStructure(subNode);
|
||||
}
|
||||
if(closeParan>openParan+1) {
|
||||
createSubNode(node,request.substr(openParan+1,closeParan-openParan-1));
|
||||
}
|
||||
top.push_back(node);
|
||||
}
|
||||
if(offsetGetField!=string::npos) {
|
||||
fullFieldName = "getField";
|
||||
Node node("getField");
|
||||
size_t openParan = request.find('(', offsetGetField);
|
||||
size_t closeParan = request.find(')', openParan);
|
||||
if(closeParan==string::npos) {
|
||||
message = request.substr(offsetField)
|
||||
+ " getField( does not have matching )";
|
||||
return PVStructurePtr();
|
||||
}
|
||||
if(closeParan>openParan+1) {
|
||||
createSubNode(node,request.substr(openParan+1,closeParan-openParan-1));
|
||||
}
|
||||
top.push_back(node);
|
||||
StructureConstPtr structure = fieldCreate->createStructure(names, fields);
|
||||
if(!structure) throw std::invalid_argument("bad request " + crequest);
|
||||
PVStructurePtr pvStructure = structure->build();
|
||||
for(size_t i=0; i<optionList.size(); ++i) {
|
||||
OptionPair pair = optionList[i];
|
||||
string name = pair.name;
|
||||
string value = pair.value;
|
||||
PVStringPtr pvField = pvStructure->getSubField<PVString>(name);
|
||||
if(!pvField) throw std::invalid_argument("bad request " + crequest);
|
||||
pvField->put(value);
|
||||
}
|
||||
if(offsetPutField!=string::npos) {
|
||||
fullFieldName = "putField";
|
||||
Node node("putField");
|
||||
size_t openParan = request.find('(', offsetPutField);
|
||||
size_t closeParan = request.find(')', openParan);
|
||||
if(closeParan==string::npos) {
|
||||
message = request.substr(offsetField)
|
||||
+ " putField( does not have matching )";
|
||||
return PVStructurePtr();
|
||||
}
|
||||
if(closeParan>openParan+1) {
|
||||
createSubNode(node,request.substr(openParan+1,closeParan-openParan-1));
|
||||
}
|
||||
top.push_back(node);
|
||||
}
|
||||
} catch (std::exception &e) {
|
||||
string xxx = e.what();
|
||||
message = "while creating Structure exception " + xxx;
|
||||
return PVStructurePtr();
|
||||
optionList.clear();
|
||||
return pvStructure;
|
||||
}
|
||||
size_t num = top.size();
|
||||
StringArray names(num);
|
||||
FieldConstPtrArray fields(num);
|
||||
for(size_t i=0; i<num; ++i) {
|
||||
Node node = top[i];
|
||||
names[i] = node.name;
|
||||
vector<Node> subNode = node.nodes;
|
||||
if(subNode.empty()) {
|
||||
fields[i] = fieldCreate->createStructure();
|
||||
} else {
|
||||
fields[i] = createSubStructure(subNode);
|
||||
}
|
||||
}
|
||||
StructureConstPtr structure = fieldCreate->createStructure(names, fields);
|
||||
PVStructurePtr pvStructure = pvDataCreate->createPVStructure(structure);
|
||||
for(size_t i=0; i<optionList.size(); ++i) {
|
||||
OptionPair pair = optionList[i];
|
||||
string name = pair.name;
|
||||
string value = pair.value;
|
||||
PVStringPtr pvField = pvStructure->getSubField<PVString>(name);
|
||||
pvField->put(value);
|
||||
}
|
||||
optionList.clear();
|
||||
return pvStructure;
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace epics {namespace pvData {
|
||||
|
||||
CreateRequest::shared_pointer CreateRequest::create()
|
||||
{
|
||||
CreateRequest::shared_pointer createRequest(new CreateRequestImpl());
|
||||
CreateRequest::shared_pointer createRequest(new CreateRequest());
|
||||
return createRequest;
|
||||
}
|
||||
|
||||
}}
|
||||
PVStructure::shared_pointer CreateRequest::createRequest(std::string const & request)
|
||||
{
|
||||
message.clear();
|
||||
try {
|
||||
return ::createRequest(request);
|
||||
} catch(std::exception& e) {
|
||||
message = e.what();
|
||||
return PVStructure::shared_pointer();
|
||||
}
|
||||
}
|
||||
|
||||
PVStructure::shared_pointer createRequest(std::string const & request)
|
||||
{
|
||||
CreateRequestImpl I;
|
||||
return I.createRequest(request);
|
||||
}
|
||||
|
||||
|
||||
}} // namespace
|
||||
|
||||
|
||||
@@ -1,56 +0,0 @@
|
||||
/*createRequest.h*/
|
||||
/**
|
||||
* Copyright - See the COPYRIGHT that is included with this distribution.
|
||||
* pvDataCPP is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
*/
|
||||
|
||||
#ifndef CREATEREQUEST_H
|
||||
#define CREATEREQUEST_H
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
|
||||
#include <pv/pvData.h>
|
||||
#include <pv/lock.h>
|
||||
|
||||
#include <shareLib.h>
|
||||
|
||||
namespace epics { namespace pvData {
|
||||
|
||||
/**
|
||||
* Class to create pvRequest structures to pass to pvAccess Channel methods.
|
||||
*/
|
||||
class epicsShareClass CreateRequest {
|
||||
public:
|
||||
POINTER_DEFINITIONS(CreateRequest);
|
||||
/**
|
||||
* Create s new instance of CreateRequest
|
||||
* @returns A shared pointer to the new instance.
|
||||
*/
|
||||
static CreateRequest::shared_pointer create();
|
||||
virtual ~CreateRequest() {};
|
||||
/**
|
||||
* Create a request structure for the create calls in Channel.
|
||||
* See the package overview documentation for details.
|
||||
* @param request The field request. See the package overview documentation for details.
|
||||
* @param requester The requester;
|
||||
* @return The request PVStructure if a valid request was given.
|
||||
* If a NULL PVStructure is returned then getMessage will return
|
||||
* the reason.
|
||||
*/
|
||||
virtual PVStructure::shared_pointer createRequest(std::string const & request) = 0;
|
||||
/**
|
||||
* Get the error message of createRequest returns NULL
|
||||
* return the error message
|
||||
*/
|
||||
std::string getMessage() {return message;}
|
||||
protected:
|
||||
CreateRequest() {}
|
||||
std::string message;
|
||||
};
|
||||
|
||||
|
||||
}}
|
||||
|
||||
#endif /* CREATEREQUEST_H */
|
||||
|
||||
233
src/copy/pv/createRequest.h
Normal file
233
src/copy/pv/createRequest.h
Normal file
@@ -0,0 +1,233 @@
|
||||
/*createRequest.h*/
|
||||
/*
|
||||
* Copyright information and license terms for this software can be
|
||||
* found in the file LICENSE that is included with the distribution
|
||||
*/
|
||||
|
||||
#ifndef CREATEREQUEST_H
|
||||
#define CREATEREQUEST_H
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
|
||||
#include <pv/pvData.h>
|
||||
#include <pv/lock.h>
|
||||
#include <pv/bitSet.h>
|
||||
|
||||
#include <shareLib.h>
|
||||
|
||||
namespace epics { namespace pvData {
|
||||
|
||||
class BitSet;
|
||||
|
||||
/**
|
||||
* @brief Create pvRequest structure for Channel methods.
|
||||
*
|
||||
* Many methods of the Channel class of pvAccess have an
|
||||
* argument <b>PVStructurePtr const * pvRequest</b>.
|
||||
* This class provides a method that creates a valid pvRequest.
|
||||
*
|
||||
*/
|
||||
class epicsShareClass CreateRequest {
|
||||
public:
|
||||
POINTER_DEFINITIONS(CreateRequest);
|
||||
/**
|
||||
* Create s new instance of CreateRequest
|
||||
* @returns A shared pointer to the new instance.
|
||||
*/
|
||||
static CreateRequest::shared_pointer create();
|
||||
~CreateRequest() {};
|
||||
/**
|
||||
* Create a request structure for the create calls in Channel.
|
||||
* See the package overview documentation for details.
|
||||
* @param request The field request. See the package overview documentation for details.
|
||||
* @return The request PVStructure if a valid request was given.
|
||||
* If a NULL PVStructure is returned then getMessage will return
|
||||
* the reason.
|
||||
*/
|
||||
PVStructure::shared_pointer createRequest(std::string const & request);
|
||||
/**
|
||||
* Get the error message of createRequest returns NULL
|
||||
* return the error message
|
||||
*/
|
||||
std::string getMessage() {return message;}
|
||||
protected:
|
||||
CreateRequest() {}
|
||||
std::string message;
|
||||
};
|
||||
|
||||
/** Parse and build pvRequest structure.
|
||||
*
|
||||
@params request the Request string to be parsed. eg. "field(value)"
|
||||
@returns The resulting strucuture. Never NULL
|
||||
@throws std::exception for various parsing errors
|
||||
*/
|
||||
epicsShareExtern
|
||||
PVStructure::shared_pointer createRequest(std::string const & request);
|
||||
|
||||
/** Helper for implementations of epics::pvAccess::ChannelProvider in interpreting the
|
||||
* 'field' substructure of a pvRequest.
|
||||
* Copies between an internal (base) Structure, and a client/user visible (requested) Structure.
|
||||
*
|
||||
* @note PVRequestMapper is not re-entrant. It is copyable and swap()able.
|
||||
*/
|
||||
class epicsShareClass PVRequestMapper {
|
||||
public:
|
||||
enum mode_t {
|
||||
/** Masking mode.
|
||||
*
|
||||
* Requested Structure is identical to Base.
|
||||
* The 'field' substructure of the provided pvRequest is used to construct a BitSet
|
||||
* where the bits corresponding to the "selected" fields are set. This mask can be
|
||||
* access via. requestedMask(). The copy* and mask* methods operate only
|
||||
* on "selected" fields.
|
||||
*/
|
||||
Mask,
|
||||
/** Slice mode
|
||||
*
|
||||
* The Requested Structure is a strict sub-set of the Base Structure containing
|
||||
* those fields "selected" by the 'field' substructure of the provided pvRequest.
|
||||
*/
|
||||
Slice,
|
||||
};
|
||||
|
||||
PVRequestMapper();
|
||||
//! @see compute()
|
||||
PVRequestMapper(const PVStructure& base,
|
||||
const PVStructure& pvRequest,
|
||||
mode_t mode = Mask);
|
||||
|
||||
//! return to state of default ctor
|
||||
void reset();
|
||||
|
||||
//! @returns the Structure of the PVStructure previously passed to compute(). NULL if never computed()'d
|
||||
inline const StructureConstPtr& base() const { return typeBase; }
|
||||
//! @returns the Structure which is the selected sub-set of the base Structure. NULL if never computed()'d
|
||||
inline const StructureConstPtr& requested() const { return typeRequested; }
|
||||
|
||||
/** A mask of all fields in the base structure which are also in the requested structure,
|
||||
* and any parent/structure "compress" bits. eg. bit 0 is always set.
|
||||
*
|
||||
@code
|
||||
PVRequestMapper mapper(...);
|
||||
...
|
||||
BitSet changed = ...; // a base changed mask
|
||||
bool wouldcopy = changed.logical_and(mapper.requestedMask());
|
||||
// wouldcopy==false means that copyBaseToRequested(..., changed, ...) would be a no-op
|
||||
@endcode
|
||||
*
|
||||
* eg. allows early detection of empty monitor updates.
|
||||
*/
|
||||
inline const BitSet& requestedMask() const { return maskRequested; }
|
||||
|
||||
//! @returns A new instance of the requested() Structure
|
||||
PVStructurePtr buildRequested() const;
|
||||
//! @returns A new instance of the base() Structure
|
||||
PVStructurePtr buildBase() const;
|
||||
|
||||
/** (re)compute the selected subset of provided base structure.
|
||||
* @param base A full base structure.
|
||||
* Must be "top level" (field offset zero).
|
||||
* @param pvRequest The user/client provided request modifier
|
||||
* @param mode Control how the mapping is constructed. @see mode_t for a description of mapping modes.
|
||||
*
|
||||
* @post Updates warnings()
|
||||
* @throws std::runtime_error For errors involving invalid pvRequest
|
||||
* @throws std::logic_error if the provided base is not a "top level" PVStructure.
|
||||
*/
|
||||
void compute(const PVStructure& base,
|
||||
const PVStructure& pvRequest,
|
||||
mode_t mode = Mask);
|
||||
|
||||
//! After compute(), check if !warnings().empty()
|
||||
inline const std::string& warnings() const { return messages; }
|
||||
|
||||
/** Copy field values from Base structure into Requested structure
|
||||
*
|
||||
* @param base An instance of the base Structure. Field values are copied from it.
|
||||
* Need not be the same instance passed to compute().
|
||||
* @param baseMask A bit mask selecting those base fields to copy.
|
||||
* @param request An instance of the requested() Structure. Field values are copied to it.
|
||||
* @param requestMask A bit mask indicating which requested fields were copied.
|
||||
* BitSet::clear() is not called.
|
||||
*/
|
||||
void copyBaseToRequested(
|
||||
const PVStructure& base,
|
||||
const BitSet& baseMask,
|
||||
PVStructure& request,
|
||||
BitSet& requestMask
|
||||
) const;
|
||||
|
||||
/** Copy field values into Base structure from Requested structure
|
||||
*
|
||||
* @param base An instance of the base Structure. Field values are copied into it.
|
||||
* Need not be the same instance passed to compute().
|
||||
* @param baseMask A bit mask indicating which base fields were copied.
|
||||
* BitSet::clear() is not called.
|
||||
* @param request An instance of the requested() Structure. Field values are copied from it.
|
||||
* @param requestMask A bit mask selecting those requested fields to copy.
|
||||
*/
|
||||
void copyBaseFromRequested(
|
||||
PVStructure& base,
|
||||
BitSet& baseMask,
|
||||
const PVStructure& request,
|
||||
const BitSet& requestMask
|
||||
) const;
|
||||
|
||||
//! Translate Base bit mask into requested bit mask.
|
||||
//! BitSet::clear() is not called.
|
||||
inline void maskBaseToRequested(
|
||||
const BitSet& baseMask,
|
||||
BitSet& requestMask
|
||||
) const
|
||||
{ _mapMask(baseMask, requestMask, false); }
|
||||
|
||||
//! Translate requested bit mask into base bit mask.
|
||||
//! BitSet::clear() is not called.
|
||||
inline void maskBaseFromRequested(
|
||||
BitSet& baseMask,
|
||||
const BitSet& requestMask
|
||||
) const
|
||||
{ _mapMask(requestMask, baseMask, true); }
|
||||
|
||||
//! Exchange contents of two mappers. O(0) and never throws.
|
||||
void swap(PVRequestMapper& other);
|
||||
|
||||
private:
|
||||
bool _compute(const PVStructure& base, const PVStructure& pvReq,
|
||||
FieldBuilderPtr& builder, bool keepids, unsigned depth);
|
||||
|
||||
void _map(const PVStructure& src,
|
||||
const BitSet& maskSrc,
|
||||
PVStructure& dest,
|
||||
BitSet& maskDest,
|
||||
bool dir_r2b) const;
|
||||
void _mapMask(const BitSet& maskSrc,
|
||||
BitSet& maskDest,
|
||||
bool dir_r2b) const;
|
||||
|
||||
StructureConstPtr typeBase, typeRequested;
|
||||
BitSet maskRequested;
|
||||
// Map between field offsets of base and requested Structures.
|
||||
// Include all fields, both leaf and sub-structure.
|
||||
struct Mapping {
|
||||
size_t to; // offset in destination Structure
|
||||
BitSet tomask, // if !leaf these are the other bits in the destination mask to changed
|
||||
frommask; // if !leaf these are the other bits in the source mask to be copied
|
||||
bool valid; // only true in (sparse) base -> requested mapping
|
||||
bool leaf; // not a (sub)Structure?
|
||||
Mapping() :valid(false) {}
|
||||
Mapping(size_t to, bool leaf) :to(to), valid(true), leaf(leaf) {}
|
||||
};
|
||||
typedef std::vector<Mapping> mapping_t;
|
||||
mapping_t base2req, req2base;
|
||||
|
||||
std::string messages;
|
||||
|
||||
mutable BitSet scratch; // avoid temporary allocs. (we aren't re-entrant!)
|
||||
};
|
||||
|
||||
}}
|
||||
|
||||
#endif /* CREATEREQUEST_H */
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
/* pvCopy.h */
|
||||
/**
|
||||
* Copyright - See the COPYRIGHT that is included with this distribution.
|
||||
* EPICS pvData is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
/*
|
||||
* Copyright information and license terms for this software can be
|
||||
* found in the file LICENSE that is included with the distribution
|
||||
*/
|
||||
/**
|
||||
* @author Marty Kraimer
|
||||
@@ -14,6 +13,7 @@
|
||||
#include <stdexcept>
|
||||
#include <memory>
|
||||
|
||||
#include <compilerDependencies.h>
|
||||
#include <shareLib.h>
|
||||
|
||||
#include <pv/pvData.h>
|
||||
@@ -25,7 +25,8 @@ class PVCopyTraverseMasterCallback;
|
||||
typedef std::tr1::shared_ptr<PVCopyTraverseMasterCallback> PVCopyTraverseMasterCallbackPtr;
|
||||
|
||||
/**
|
||||
* Callback for traversing master structure
|
||||
* @brief Callback for traversing master structure
|
||||
*
|
||||
* Must be implemented by code that creates pvCopy.
|
||||
*/
|
||||
class epicsShareClass PVCopyTraverseMasterCallback
|
||||
@@ -53,18 +54,20 @@ typedef std::tr1::shared_ptr<CopyStructureNode> CopyStructureNodePtr;
|
||||
|
||||
|
||||
/**
|
||||
* @brief Support for subset of fields in a pvStructure.
|
||||
*
|
||||
* Class that manages one or more PVStructures that holds an arbitrary subset of the fields
|
||||
* in another PVStructure called master.
|
||||
*/
|
||||
class epicsShareClass PVCopy :
|
||||
class epicsShareClass EPICS_DEPRECATED PVCopy :
|
||||
public std::tr1::enable_shared_from_this<PVCopy>
|
||||
{
|
||||
public:
|
||||
POINTER_DEFINITIONS(PVCopy);
|
||||
/**
|
||||
* Create a new pvCopy
|
||||
* @param pvMaster The top level sructure for which a copy of
|
||||
* an arbritary subset of the fields in master will be created and managed.
|
||||
* @param pvMaster The top-level structure for which a copy of
|
||||
* an arbitrary subset of the fields in master will be created and managed.
|
||||
* @param pvRequest Selects the set of subfields desired and options for each field.
|
||||
* @param structureName The name for the top level of any PVStructure created.
|
||||
*/
|
||||
@@ -73,10 +76,10 @@ public:
|
||||
PVStructurePtr const &pvRequest,
|
||||
std::string const & structureName);
|
||||
virtual ~PVCopy(){}
|
||||
virtual void destroy();
|
||||
void destroy();
|
||||
/**
|
||||
* Get the top level structure of master
|
||||
* @returns The master top level structure.
|
||||
* Get the top-level structure of master
|
||||
* @returns The master top-level structure.
|
||||
* This should not be modified.
|
||||
*/
|
||||
PVStructurePtr getPVMaster();
|
||||
@@ -94,7 +97,7 @@ public:
|
||||
StructureConstPtr getStructure();
|
||||
/**
|
||||
* Create a copy instance. Monitors keep a queue of monitor elements.
|
||||
* Since each element needs a PVStructure, multiple top level structures will be created.
|
||||
* Since each element needs a PVStructure, multiple top-level structures will be created.
|
||||
*/
|
||||
PVStructurePtr createPVStructure();
|
||||
/**
|
||||
@@ -113,15 +116,15 @@ public:
|
||||
PVStructurePtr const &masterPVStructure,
|
||||
PVFieldPtr const &masterPVField);
|
||||
/**
|
||||
* Given a offset in the copy get the corresponding field in pvMaster.
|
||||
* @param offset The offset in the copy.
|
||||
* Given an offset in the copy get the corresponding field in pvMaster.
|
||||
* @param structureOffset The offset in the copy.
|
||||
*/
|
||||
PVFieldPtr getMasterPVField(std::size_t structureOffset);
|
||||
/**
|
||||
* Initialize the fields in copyPVStructure by giving each field
|
||||
* the value from the corresponding field in pvMaster.
|
||||
* bitSet will be set to show that all fields are changed.
|
||||
* @param copyPVStructure A copy top level structure.
|
||||
* @param copyPVStructure A copy top-level structure.
|
||||
* @param bitSet A bitSet for copyPVStructure.
|
||||
*/
|
||||
void initCopy(
|
||||
@@ -130,7 +133,7 @@ public:
|
||||
/**
|
||||
* Set all fields in copyPVStructure to the value of the corresponding field in pvMaster.
|
||||
* Each field that is changed has it's corresponding bit set in bitSet.
|
||||
* @param copyPVStructure A copy top level structure.
|
||||
* @param copyPVStructure A copy top-level structure.
|
||||
* @param bitSet A bitSet for copyPVStructure.
|
||||
*/
|
||||
void updateCopySetBitSet(
|
||||
@@ -138,8 +141,8 @@ public:
|
||||
BitSetPtr const &bitSet);
|
||||
/**
|
||||
* For each set bit in bitSet
|
||||
* set the field in copyPVStructure to the value of the corrseponding field in pvMaster.
|
||||
* @param copyPVStructure A copy top level structure.
|
||||
* set the field in copyPVStructure to the value of the corresponding field in pvMaster.
|
||||
* @param copyPVStructure A copy top-level structure.
|
||||
* @param bitSet A bitSet for copyPVStructure.
|
||||
*/
|
||||
void updateCopyFromBitSet(
|
||||
@@ -147,8 +150,8 @@ public:
|
||||
BitSetPtr const &bitSet);
|
||||
/**
|
||||
* For each set bit in bitSet
|
||||
* set the field in pvMaster to the value of the corrseponding field in copyPVStructure
|
||||
* @param copyPVStructure A copy top level structure.
|
||||
* set the field in pvMaster to the value of the corresponding field in copyPVStructure
|
||||
* @param copyPVStructure A copy top-level structure.
|
||||
* @param bitSet A bitSet for copyPVStructure.
|
||||
*/
|
||||
void updateMaster(
|
||||
@@ -156,7 +159,7 @@ public:
|
||||
BitSetPtr const &bitSet);
|
||||
/**
|
||||
* Get the options for the field at the specified offset.
|
||||
* @param offset the offset in copy.
|
||||
* @param fieldOffset the offset in copy.
|
||||
* @returns A NULL is returned if no options were specified for the field.
|
||||
* If options were specified,PVStructurePtr is a structures
|
||||
* with a set of PVString subfields that specify name,value pairs.s
|
||||
@@ -1,8 +1,7 @@
|
||||
/* pvCopy.cpp */
|
||||
/**
|
||||
* Copyright - See the COPYRIGHT that is included with this distribution.
|
||||
* EPICS pvData is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
/*
|
||||
* Copyright information and license terms for this software can be
|
||||
* found in the file LICENSE that is included with the distribution
|
||||
*/
|
||||
/**
|
||||
* @author Marty Kraimer
|
||||
@@ -13,12 +12,16 @@
|
||||
#include <memory>
|
||||
#include <sstream>
|
||||
|
||||
#include <epicsThread.h>
|
||||
#include <compilerDependencies.h>
|
||||
#undef EPICS_DEPRECATED
|
||||
#define EPICS_DEPRECATED
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
|
||||
#include <pv/thread.h>
|
||||
|
||||
#include <pv/pvCopy.h>
|
||||
#include <pv/convert.h>
|
||||
|
||||
using std::tr1::static_pointer_cast;
|
||||
using std::tr1::dynamic_pointer_cast;
|
||||
@@ -29,12 +32,17 @@ using std::endl;
|
||||
|
||||
namespace epics { namespace pvData {
|
||||
|
||||
static PVCopyPtr NULLPVCopy;
|
||||
static FieldConstPtr NULLField;
|
||||
static StructureConstPtr NULLStructure;
|
||||
static PVStructurePtr NULLPVStructure;
|
||||
static CopyNodePtr NULLCopyNode;
|
||||
static CopyMasterNodePtr NULLCopyMasterNode;
|
||||
/**
|
||||
* Convenience method for implementing dump.
|
||||
* It generates a newline and inserts blanks at the beginning of the newline.
|
||||
* @param builder The std::string * being constructed.
|
||||
* @param indentLevel Indent level, Each level is four spaces.
|
||||
*/
|
||||
static void newLine(string *buffer, int indentLevel)
|
||||
{
|
||||
*buffer += "\n";
|
||||
*buffer += string(indentLevel*4, ' ');
|
||||
}
|
||||
|
||||
struct CopyNode {
|
||||
CopyNode()
|
||||
@@ -67,13 +75,13 @@ PVCopyPtr PVCopy::create(
|
||||
PVStructurePtr pvStructure(pvRequest);
|
||||
if(structureName.size()>0) {
|
||||
if(pvRequest->getStructure()->getNumberFields()>0) {
|
||||
pvStructure = pvRequest->getStructureField(structureName);
|
||||
if(!pvStructure) return NULLPVCopy;
|
||||
pvStructure = pvRequest->getSubField<PVStructure>(structureName);
|
||||
if(!pvStructure) return PVCopyPtr();
|
||||
}
|
||||
} else if(pvStructure->getSubField("field")) {
|
||||
pvStructure = pvRequest->getStructureField("field");
|
||||
} else if(pvStructure->getSubField<PVStructure>("field")) {
|
||||
pvStructure = pvRequest->getSubField<PVStructure>("field");
|
||||
}
|
||||
PVCopyPtr pvCopy = PVCopyPtr(new PVCopy(pvMaster));
|
||||
PVCopyPtr pvCopy(new PVCopy(pvMaster));
|
||||
bool result = pvCopy->init(pvStructure);
|
||||
if(!result) pvCopy.reset();
|
||||
return pvCopy;
|
||||
@@ -135,7 +143,7 @@ PVStructurePtr PVCopy::getOptions(std::size_t fieldOffset)
|
||||
while(true) {
|
||||
if(!node->isStructure) {
|
||||
if(node->structureOffset==fieldOffset) return node->options;
|
||||
return NULLPVStructure;
|
||||
return PVStructurePtr();
|
||||
}
|
||||
CopyStructureNodePtr structNode = static_pointer_cast<CopyStructureNode>(node);
|
||||
CopyNodePtrArrayPtr nodes = structNode->nodes;
|
||||
@@ -146,7 +154,7 @@ PVStructurePtr PVCopy::getOptions(std::size_t fieldOffset)
|
||||
if(fieldOffset>=soff && fieldOffset<soff+node->nfields) {
|
||||
if(fieldOffset==soff) return node->options;
|
||||
if(!node->isStructure) {
|
||||
return NULLPVStructure;
|
||||
return PVStructurePtr();
|
||||
}
|
||||
okToContinue = true;
|
||||
break;
|
||||
@@ -243,10 +251,9 @@ void PVCopy::updateCopySetBitSet(
|
||||
updateSubFieldSetBitSet(copyPVField,pvMasterField,bitSet);
|
||||
return;
|
||||
}
|
||||
ConvertPtr convert = getConvert();
|
||||
bool isEqual = convert->equals(copyPVField,pvField);
|
||||
bool isEqual = (*copyPVField == *pvField);
|
||||
if(!isEqual) {
|
||||
convert->copy(pvField, copyPVField);
|
||||
copyPVField->copyUnchecked(*pvField);
|
||||
bitSet->set(copyPVField->getFieldOffset());
|
||||
}
|
||||
}
|
||||
@@ -292,7 +299,7 @@ string PVCopy::dump()
|
||||
|
||||
void PVCopy::dump(string *builder,CopyNodePtr const &node,int indentLevel)
|
||||
{
|
||||
getConvert()->newLine(builder,indentLevel);
|
||||
newLine(builder,indentLevel);
|
||||
std::stringstream ss;
|
||||
ss << (node->isStructure ? "structureNode" : "masterNode");
|
||||
ss << " structureOffset " << node->structureOffset;
|
||||
@@ -300,14 +307,14 @@ void PVCopy::dump(string *builder,CopyNodePtr const &node,int indentLevel)
|
||||
*builder += ss.str();
|
||||
PVStructurePtr options = node->options;
|
||||
if(options) {
|
||||
getConvert()->newLine(builder,indentLevel +1);
|
||||
newLine(builder,indentLevel +1);
|
||||
|
||||
// TODO !!! ugly
|
||||
std::ostringstream oss;
|
||||
oss << *options;
|
||||
*builder += oss.str();
|
||||
|
||||
getConvert()->newLine(builder,indentLevel);
|
||||
newLine(builder,indentLevel);
|
||||
}
|
||||
if(!node->isStructure) {
|
||||
CopyMasterNodePtr masterNode = static_pointer_cast<CopyMasterNode>(node);
|
||||
@@ -320,7 +327,7 @@ void PVCopy::dump(string *builder,CopyNodePtr const &node,int indentLevel)
|
||||
CopyNodePtrArrayPtr nodes = structureNode->nodes;
|
||||
for(size_t i=0; i<nodes->size(); ++i) {
|
||||
if((*nodes)[i].get()==NULL) {
|
||||
getConvert()->newLine(builder,indentLevel +1);
|
||||
newLine(builder,indentLevel +1);
|
||||
ss.str("");
|
||||
ss << "node[" << i << "] is null";
|
||||
*builder += ss.str();
|
||||
@@ -338,8 +345,8 @@ bool PVCopy::init(epics::pvData::PVStructurePtr const &pvRequest)
|
||||
if(len==string::npos) entireMaster = true;
|
||||
if(len==0) entireMaster = true;
|
||||
PVStructurePtr pvOptions;
|
||||
if(len==1 && pvRequest->getSubField("_options")) {
|
||||
pvOptions = pvRequest->getStructureField("_options");
|
||||
if(len==1) {
|
||||
pvOptions = pvRequest->getSubField<PVStructure>("_options");
|
||||
}
|
||||
if(entireMaster) {
|
||||
structure = pvMasterStructure->getStructure();
|
||||
@@ -381,7 +388,7 @@ StructureConstPtr PVCopy::createStructure(
|
||||
PVFieldPtrArray const &pvFromRequestFields = pvFromRequest->getPVFields();
|
||||
StringArray const &fromRequestFieldNames = pvFromRequest->getStructure()->getFieldNames();
|
||||
size_t length = pvFromRequestFields.size();
|
||||
if(length==0) return NULLStructure;
|
||||
if(length==0) return StructureConstPtr();
|
||||
FieldConstPtrArray fields; fields.reserve(length);
|
||||
StringArray fieldNames; fields.reserve(length);
|
||||
for(size_t i=0; i<length; ++i) {
|
||||
@@ -411,7 +418,7 @@ StructureConstPtr PVCopy::createStructure(
|
||||
fields.push_back(field);
|
||||
}
|
||||
size_t numsubfields = fields.size();
|
||||
if(numsubfields==0) return NULLStructure;
|
||||
if(numsubfields==0) return StructureConstPtr();
|
||||
return getFieldCreate()->createStructure(fieldNames, fields);
|
||||
}
|
||||
|
||||
@@ -431,11 +438,8 @@ CopyNodePtr PVCopy::createStructureNodes(
|
||||
PVFieldPtr copyPVField = copyPVFields[i];
|
||||
string fieldName = copyPVField->getFieldName();
|
||||
|
||||
PVStructurePtr requestPVStructure = static_pointer_cast<PVStructure>(
|
||||
pvFromRequest->getSubField(fieldName));
|
||||
PVStructurePtr pvSubFieldOptions;
|
||||
PVFieldPtr pvField = requestPVStructure->getSubField("_options");
|
||||
if(pvField) pvSubFieldOptions = static_pointer_cast<PVStructure>(pvField);
|
||||
PVStructurePtr requestPVStructure = pvFromRequest->getSubField<PVStructure>(fieldName);
|
||||
PVStructurePtr pvSubFieldOptions = requestPVStructure->getSubField<PVStructure>("_options");
|
||||
PVFieldPtr pvMasterField;
|
||||
PVFieldPtrArray const & pvMasterFields = pvMasterStructure->getPVFields();
|
||||
for(size_t j=0; i<pvMasterFields.size(); j++ ) {
|
||||
@@ -499,17 +503,16 @@ void PVCopy::updateSubFieldSetBitSet(
|
||||
FieldConstPtr field = pvCopy->getField();
|
||||
Type type = field->getType();
|
||||
if(type!=epics::pvData::structure) {
|
||||
ConvertPtr convert = getConvert();
|
||||
bool isEqual = convert->equals(pvCopy,pvMaster);
|
||||
bool isEqual = (*pvCopy == *pvMaster);
|
||||
if(isEqual) {
|
||||
if(type==structureArray) {
|
||||
// always act as though a change occurred.
|
||||
// Note that array elements are shared.
|
||||
bitSet->set(pvCopy->getFieldOffset());
|
||||
bitSet->set(pvCopy->getFieldOffset());
|
||||
}
|
||||
}
|
||||
if(isEqual) return;
|
||||
convert->copy(pvMaster, pvCopy);
|
||||
pvCopy->copyUnchecked(*pvMaster);
|
||||
bitSet->set(pvCopy->getFieldOffset());
|
||||
return;
|
||||
}
|
||||
@@ -542,7 +545,7 @@ void PVCopy::updateStructureNodeFromBitSet(
|
||||
CopyNodePtrArrayPtr nodes = structureNode->nodes;
|
||||
for(size_t i=0; i<nodes->size(); i++) {
|
||||
CopyNodePtr node = (*nodes)[i];
|
||||
PVFieldPtr pvField = pvCopy->getSubField(node->structureOffset);
|
||||
PVFieldPtr pvField = pvCopy->getSubFieldT(node->structureOffset);
|
||||
if(node->isStructure) {
|
||||
PVStructurePtr xxx = static_pointer_cast<PVStructure>(pvField);
|
||||
CopyStructureNodePtr subStructureNode =
|
||||
@@ -574,7 +577,6 @@ void PVCopy::updateSubFieldFromBitSet(
|
||||
if(nextSet==string::npos) return;
|
||||
if(nextSet>=pvCopy->getNextFieldOffset()) return;
|
||||
}
|
||||
ConvertPtr convert = getConvert();
|
||||
if(pvCopy->getField()->getType()==epics::pvData::structure) {
|
||||
PVStructurePtr pvCopyStructure =
|
||||
static_pointer_cast<PVStructure>(pvCopy);
|
||||
@@ -595,9 +597,9 @@ void PVCopy::updateSubFieldFromBitSet(
|
||||
}
|
||||
} else {
|
||||
if(toCopy) {
|
||||
convert->copy(pvMasterField, pvCopy);
|
||||
pvCopy->copyUnchecked(*pvMasterField);
|
||||
} else {
|
||||
convert->copy(pvCopy, pvMasterField);
|
||||
pvMasterField->copyUnchecked(*pvCopy);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -624,7 +626,7 @@ CopyMasterNodePtr PVCopy::getCopyOffset(
|
||||
if(masterNode) return masterNode;
|
||||
}
|
||||
}
|
||||
return NULLCopyMasterNode;
|
||||
return CopyMasterNodePtr();
|
||||
}
|
||||
|
||||
CopyMasterNodePtr PVCopy::getMasterNode(
|
||||
@@ -644,7 +646,7 @@ CopyMasterNodePtr PVCopy::getMasterNode(
|
||||
static_pointer_cast<CopyStructureNode>(node);
|
||||
return getMasterNode(subNode,structureOffset);
|
||||
}
|
||||
return NULLCopyMasterNode;
|
||||
return CopyMasterNodePtr();
|
||||
}
|
||||
|
||||
}}
|
||||
|
||||
328
src/copy/requestmapper.cpp
Normal file
328
src/copy/requestmapper.cpp
Normal file
@@ -0,0 +1,328 @@
|
||||
/*
|
||||
* Copyright information and license terms for this software can be
|
||||
* found in the file LICENSE that is included with the distribution
|
||||
*/
|
||||
|
||||
#include <sstream>
|
||||
|
||||
#include <epicsAssert.h>
|
||||
#include <epicsTypes.h>
|
||||
#include <epicsVersion.h>
|
||||
#include <epicsConvert.h>
|
||||
#include <epicsAssert.h>
|
||||
#include <epicsMutex.h>
|
||||
#include <epicsGuard.h>
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include <pv/createRequest.h>
|
||||
#include <pv/epicsException.h>
|
||||
#include <pv/bitSet.h>
|
||||
|
||||
// Our arbitrary limit on pvRequest structure depth to bound stack usage during recursion
|
||||
static const unsigned maxDepth = 5;
|
||||
|
||||
namespace epics{namespace pvData {
|
||||
|
||||
PVRequestMapper::PVRequestMapper() {}
|
||||
|
||||
PVRequestMapper::PVRequestMapper(const PVStructure &base,
|
||||
const PVStructure &pvRequest,
|
||||
mode_t mode)
|
||||
{
|
||||
compute(base, pvRequest, mode);
|
||||
}
|
||||
|
||||
PVStructurePtr PVRequestMapper::buildRequested() const
|
||||
{
|
||||
if(!typeRequested)
|
||||
THROW_EXCEPTION2(std::logic_error, "No mapping compute()d");
|
||||
return typeRequested->build();
|
||||
}
|
||||
|
||||
PVStructurePtr PVRequestMapper::buildBase() const
|
||||
{
|
||||
if(!typeBase)
|
||||
THROW_EXCEPTION2(std::logic_error, "No mapping compute()d");
|
||||
return typeBase->build();
|
||||
}
|
||||
|
||||
void PVRequestMapper::compute(const PVStructure &base,
|
||||
const PVStructure &pvRequest,
|
||||
mode_t mode)
|
||||
{
|
||||
if(base.getFieldOffset()!=0)
|
||||
THROW_EXCEPTION2(std::logic_error, "Mapper must be used with top level PVStructure");
|
||||
|
||||
bool ok = true;
|
||||
|
||||
// we want to be transactional, which requires a second copy of everything.
|
||||
PVRequestMapper temp;
|
||||
|
||||
// whether to preserve IDs of partial structures.
|
||||
bool keepids = false;
|
||||
PVScalar::const_shared_pointer pbp(pvRequest.getSubField<PVScalar>("record._options.keepIDs"));
|
||||
try {
|
||||
if(pbp) keepids = pbp->getAs<boolean>();
|
||||
}catch(std::runtime_error& e){
|
||||
std::ostringstream msg;
|
||||
msg<<"Can't parse keepIDs : '"<<e.what()<<"' ";
|
||||
temp.messages+=msg.str();
|
||||
}
|
||||
|
||||
PVStructure::const_shared_pointer fields(pvRequest.getSubField<PVStructure>("field"));
|
||||
if(!fields || fields->getPVFields().empty()) {
|
||||
// not selection, or empty selection, treated as select all
|
||||
temp.typeBase = temp.typeRequested = base.getStructure();
|
||||
|
||||
for(size_t i=1, N=base.getNextFieldOffset(); i<N; i++)
|
||||
temp.maskRequested.set(i);
|
||||
|
||||
} else {
|
||||
FieldBuilderPtr builder(getFieldCreate()->createFieldBuilder());
|
||||
|
||||
if(keepids)
|
||||
builder = builder->setId(base.getStructure()->getID());
|
||||
|
||||
ok &= temp._compute(base, *fields, builder, keepids, 0); // fills in builder
|
||||
|
||||
temp.typeBase = base.getStructure();
|
||||
temp.typeRequested = builder->createStructure();
|
||||
// possible that typeBase==typeRequested if all fields explicitly selected
|
||||
}
|
||||
|
||||
if(mode==Mask) {
|
||||
// short circuit use of masked Structure, but keep maskRequested
|
||||
temp.typeRequested = temp.typeBase;
|
||||
}
|
||||
|
||||
{
|
||||
PVStructurePtr proto(temp.typeRequested->build());
|
||||
|
||||
// base -> request may be sparce mapping
|
||||
temp.base2req.resize(base.getNextFieldOffset());
|
||||
// request -> base is dense mapping
|
||||
temp.req2base.resize(proto->getNextFieldOffset());
|
||||
|
||||
// special handling for whole structure mapping. in part because getSubField(0) isn't allowed
|
||||
temp.base2req[0] = Mapping(0, false);
|
||||
temp.req2base[0] = Mapping(0, false);
|
||||
|
||||
// Iterate prototype of requested to map with base field offsets.
|
||||
// which is handled as a special case below.
|
||||
// We also don't try to prevent redundant copies if both leaf and compress bits are set.
|
||||
for(size_t r=1, N=proto->getNextFieldOffset(); r<N; r++) {
|
||||
PVField::const_shared_pointer fld_req(proto->getSubFieldT(r)),
|
||||
fld_base(base.getSubFieldT(fld_req->getFullName()));
|
||||
const size_t b = fld_base->getFieldOffset();
|
||||
|
||||
if(!temp.requestedMask().get(b))
|
||||
continue;
|
||||
|
||||
bool leaf = fld_base->getField()->getType()!=structure;
|
||||
|
||||
// initialize mapping when our bit is set
|
||||
temp.base2req[b] = Mapping(r, leaf);
|
||||
temp.req2base[r] = Mapping(b, leaf);
|
||||
|
||||
// add ourself to all "compress" bit mappings of enclosing structures
|
||||
for(const PVStructure *parent = fld_req->getParent(); parent; parent = parent->getParent()) {
|
||||
temp.req2base[parent->getFieldOffset()].tomask .set(b);
|
||||
temp.req2base[parent->getFieldOffset()].frommask.set(r);
|
||||
}
|
||||
|
||||
for(const PVStructure *parent = fld_base->getParent(); parent; parent = parent->getParent()) {
|
||||
temp.base2req[parent->getFieldOffset()].tomask .set(r);
|
||||
temp.base2req[parent->getFieldOffset()].frommask.set(b);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
temp.maskRequested.set(0);
|
||||
|
||||
if(temp.maskRequested.nextSetBit(1)==-1) {
|
||||
ok = false;
|
||||
temp.messages+="Empty field selection";
|
||||
}
|
||||
|
||||
if(!ok)
|
||||
throw std::runtime_error(temp.messages);
|
||||
|
||||
swap(temp);
|
||||
}
|
||||
|
||||
bool PVRequestMapper::_compute(const PVStructure& base, const PVStructure& pvReq,
|
||||
FieldBuilderPtr& builder, bool keepids, unsigned depth)
|
||||
{
|
||||
bool ok = true;
|
||||
const StringArray& reqNames = pvReq.getStructure()->getFieldNames();
|
||||
|
||||
for(size_t i=0, N=reqNames.size(); i<N; i++) {
|
||||
// iterate through requested fields
|
||||
|
||||
PVField::const_shared_pointer subtype(base.getSubField(reqNames[i]));
|
||||
const FieldConstPtr& subReq = pvReq.getStructure()->getFields()[i];
|
||||
|
||||
if(subReq->getType()!=structure) {
|
||||
// pvRequest .field was not properly composed
|
||||
std::ostringstream msg;
|
||||
// not a great warning message as it doesn't distinguish 'a.value' from 'b.value',
|
||||
// but getFullName() whould prefix with 'field.', which would probably cause
|
||||
// more frequent confusion...
|
||||
msg<<"request invalid '"<<pvReq.getStructure()->getFieldNames()[i]<<"' ";
|
||||
messages+=msg.str();
|
||||
ok = false;
|
||||
|
||||
} else if(!subtype) {
|
||||
// requested field does not actually exist in base
|
||||
std::ostringstream msg;
|
||||
msg<<"No field '"<<pvReq.getStructure()->getFieldNames()[i]<<"' ";
|
||||
messages+=msg.str();
|
||||
|
||||
} else if(depth>=maxDepth // exceeds max recursion depth
|
||||
|| subtype->getField()->getType()!=structure // requested field is a leaf
|
||||
|| static_cast<const Structure&>(*subReq).getFieldNames().empty() // requests all sub-fields
|
||||
)
|
||||
{
|
||||
// just add the whole thing
|
||||
builder = builder->add(reqNames[i], subtype->getField());
|
||||
for(size_t j=subtype->getFieldOffset(), N=subtype->getNextFieldOffset(); j<N; j++)
|
||||
maskRequested.set(j);
|
||||
|
||||
if(subtype->getField()->getType()!=structure
|
||||
&& !static_cast<const Structure&>(*subReq).getFieldNames().empty())
|
||||
{
|
||||
// attempt to select below a leaf field
|
||||
std::ostringstream msg;
|
||||
msg<<"Leaf field '"<<pvReq.getFullName()<<"' ";
|
||||
messages+=msg.str();
|
||||
} else if(depth>=maxDepth) {
|
||||
std::ostringstream msg;
|
||||
msg<<"selection truncated at '"<<pvReq.getFullName()<<"' ";
|
||||
messages+=msg.str();
|
||||
}
|
||||
|
||||
} else {
|
||||
// recurse into sub-structure
|
||||
const PVStructure& substruct = static_cast<const PVStructure&>(*subtype);
|
||||
|
||||
builder = builder->addNestedStructure(reqNames[i]);
|
||||
maskRequested.set(substruct.getFieldOffset());
|
||||
|
||||
if(keepids)
|
||||
builder = builder->setId(substruct.getStructure()->getID());
|
||||
|
||||
_compute(substruct,
|
||||
static_cast<const PVStructure&>(*pvReq.getPVFields()[i]),
|
||||
builder, keepids, depth+1u);
|
||||
|
||||
builder = builder->endNested();
|
||||
}
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
void PVRequestMapper::copyBaseToRequested(
|
||||
const PVStructure& base,
|
||||
const BitSet& baseMask,
|
||||
PVStructure& request,
|
||||
BitSet& requestMask
|
||||
) const {
|
||||
assert(base.getStructure()==typeBase);
|
||||
assert(request.getStructure()==typeRequested);
|
||||
_map(base, baseMask, request, requestMask, false);
|
||||
}
|
||||
|
||||
void PVRequestMapper::copyBaseFromRequested(
|
||||
PVStructure& base,
|
||||
BitSet& baseMask,
|
||||
const PVStructure& request,
|
||||
const BitSet& requestMask
|
||||
) const {
|
||||
assert(base.getStructure()==typeBase);
|
||||
assert(request.getStructure()==typeRequested);
|
||||
_map(request, requestMask, base, baseMask, true);
|
||||
}
|
||||
|
||||
void PVRequestMapper::_map(const PVStructure& src, const BitSet& maskSrc,
|
||||
PVStructure& dest, BitSet& maskDest,
|
||||
bool dir_r2b) const
|
||||
{
|
||||
{
|
||||
scratch = maskSrc;
|
||||
const mapping_t& map = dir_r2b ? req2base : base2req;
|
||||
|
||||
assert(map.size()==src.getNumberFields());
|
||||
|
||||
for(int32 i=scratch.nextSetBit(0), N=map.size(); i>=0 && i<N; i=scratch.nextSetBit(i+1)) {
|
||||
const Mapping& M = map[i];
|
||||
if(!M.valid) {
|
||||
assert(!dir_r2b); // only base -> requested mapping can have holes
|
||||
|
||||
} else if(M.leaf) {
|
||||
// just copy
|
||||
dest.getSubFieldT(M.to)->copy(*src.getSubFieldT(i));
|
||||
maskDest.set(M.to);
|
||||
|
||||
} else {
|
||||
// set bits of all sub-fields (in requested structure)
|
||||
// these indicies are always >i
|
||||
scratch |= M.frommask;
|
||||
|
||||
// we will also set the individual bits, but if a compress bit is set in the input,
|
||||
// then set the corresponding bit in the output.
|
||||
maskDest.set(M.to);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PVRequestMapper::_mapMask(const BitSet& maskSrc,
|
||||
BitSet& maskDest,
|
||||
bool dir_r2b) const
|
||||
{
|
||||
if(maskSrc.isEmpty()) {
|
||||
// no-op
|
||||
|
||||
} else {
|
||||
const mapping_t& map = dir_r2b ? req2base : base2req;
|
||||
|
||||
for(int32 i=maskSrc.nextSetBit(0), N=map.size(); i>=0 && i<N; i=maskSrc.nextSetBit(i+1)) {
|
||||
const Mapping& M = map[i];
|
||||
if(!M.valid) {
|
||||
assert(!dir_r2b); // only base -> requested mapping can have holes
|
||||
|
||||
} else {
|
||||
maskDest.set(M.to);
|
||||
|
||||
if(!M.leaf) {
|
||||
maskDest |= M.tomask;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void PVRequestMapper::swap(PVRequestMapper& other)
|
||||
{
|
||||
typeBase.swap(other.typeBase);
|
||||
typeRequested.swap(other.typeRequested);
|
||||
maskRequested.swap(other.maskRequested);
|
||||
base2req.swap(other.base2req);
|
||||
req2base.swap(other.req2base);
|
||||
messages.swap(other.messages);
|
||||
scratch.swap(other.scratch); // paranoia
|
||||
}
|
||||
|
||||
void PVRequestMapper::reset()
|
||||
{
|
||||
typeBase.reset();
|
||||
typeRequested.reset();
|
||||
maskRequested.clear();
|
||||
base2req.clear();
|
||||
req2base.clear();
|
||||
messages.clear();
|
||||
scratch.clear(); // paranoia
|
||||
}
|
||||
|
||||
}} //namespace epics::pvData
|
||||
@@ -1,7 +1,6 @@
|
||||
/**
|
||||
* Copyright - See the COPYRIGHT that is included with this distribution.
|
||||
* EPICS pvData is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
/*
|
||||
* Copyright information and license terms for this software can be
|
||||
* found in the file LICENSE that is included with the distribution
|
||||
*/
|
||||
/**
|
||||
* @author mes
|
||||
@@ -12,19 +11,19 @@
|
||||
#include <sstream>
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include <pv/convert.h>
|
||||
#include <pv/pvData.h>
|
||||
|
||||
using std::string;
|
||||
|
||||
namespace epics { namespace pvData {
|
||||
|
||||
// Introspection object comparision
|
||||
// Introspection object comparison
|
||||
|
||||
/** Field equality conditions:
|
||||
* 1) same instance
|
||||
* 2) same type (field and scalar/element), same name, same subfields (if any)
|
||||
*/
|
||||
bool operator==(const Field& a, const Field& b)
|
||||
bool compare(const Field& a, const Field& b)
|
||||
{
|
||||
if(&a==&b)
|
||||
return true;
|
||||
@@ -34,53 +33,53 @@ bool operator==(const Field& a, const Field& b)
|
||||
case scalar: {
|
||||
const Scalar &A=static_cast<const Scalar&>(a);
|
||||
const Scalar &B=static_cast<const Scalar&>(b);
|
||||
return A==B;
|
||||
return compare(A, B);
|
||||
}
|
||||
case scalarArray: {
|
||||
const ScalarArray &A=static_cast<const ScalarArray&>(a);
|
||||
const ScalarArray &B=static_cast<const ScalarArray&>(b);
|
||||
return A==B;
|
||||
return compare(A, B);
|
||||
}
|
||||
case structure: {
|
||||
const Structure &A=static_cast<const Structure&>(a);
|
||||
const Structure &B=static_cast<const Structure&>(b);
|
||||
return A==B;
|
||||
return compare(A, B);
|
||||
}
|
||||
case structureArray: {
|
||||
const StructureArray &A=static_cast<const StructureArray&>(a);
|
||||
const StructureArray &B=static_cast<const StructureArray&>(b);
|
||||
return A==B;
|
||||
return compare(A, B);
|
||||
}
|
||||
case union_: {
|
||||
const Union &A=static_cast<const Union&>(a);
|
||||
const Union &B=static_cast<const Union&>(b);
|
||||
return A==B;
|
||||
return compare(A, B);
|
||||
}
|
||||
case unionArray: {
|
||||
const UnionArray &A=static_cast<const UnionArray&>(a);
|
||||
const UnionArray &B=static_cast<const UnionArray&>(b);
|
||||
return A==B;
|
||||
return compare(A, B);
|
||||
}
|
||||
default:
|
||||
throw std::logic_error("Invalid Field type in comparision");
|
||||
throw std::logic_error("Invalid Field type in comparison");
|
||||
}
|
||||
}
|
||||
|
||||
bool operator==(const Scalar& a, const Scalar& b)
|
||||
bool compare(const Scalar& a, const Scalar& b)
|
||||
{
|
||||
if(&a==&b)
|
||||
return true;
|
||||
return a.getScalarType()==b.getScalarType();
|
||||
}
|
||||
|
||||
bool operator==(const ScalarArray& a, const ScalarArray& b)
|
||||
bool compare(const ScalarArray& a, const ScalarArray& b)
|
||||
{
|
||||
if(&a==&b)
|
||||
return true;
|
||||
return a.getElementType()==b.getElementType();
|
||||
}
|
||||
|
||||
bool operator==(const Structure& a, const Structure& b)
|
||||
bool compare(const Structure& a, const Structure& b)
|
||||
{
|
||||
if(&a==&b)
|
||||
return true;
|
||||
@@ -91,23 +90,23 @@ bool operator==(const Structure& a, const Structure& b)
|
||||
return false;
|
||||
|
||||
// std::equals does not work, since FieldConstPtrArray is an array of shared_pointers
|
||||
FieldConstPtrArray af = a.getFields();
|
||||
FieldConstPtrArray bf = b.getFields();
|
||||
FieldConstPtrArray const & af = a.getFields();
|
||||
FieldConstPtrArray const & bf = b.getFields();
|
||||
for (size_t i = 0; i < nflds; i++)
|
||||
if (*(af[i].get()) != *(bf[i].get()))
|
||||
return false;
|
||||
|
||||
StringArray an = a.getFieldNames();
|
||||
StringArray bn = b.getFieldNames();
|
||||
StringArray const & an = a.getFieldNames();
|
||||
StringArray const & bn = b.getFieldNames();
|
||||
return std::equal( an.begin(), an.end(), bn.begin() );
|
||||
}
|
||||
|
||||
bool operator==(const StructureArray& a, const StructureArray& b)
|
||||
bool compare(const StructureArray& a, const StructureArray& b)
|
||||
{
|
||||
return *(a.getStructure().get())==*(b.getStructure().get());
|
||||
}
|
||||
|
||||
bool operator==(const Union& a, const Union& b)
|
||||
bool compare(const Union& a, const Union& b)
|
||||
{
|
||||
if(&a==&b)
|
||||
return true;
|
||||
@@ -118,23 +117,23 @@ bool operator==(const Union& a, const Union& b)
|
||||
return false;
|
||||
|
||||
// std::equals does not work, since FieldConstPtrArray is an array of shared_pointers
|
||||
FieldConstPtrArray af = a.getFields();
|
||||
FieldConstPtrArray bf = b.getFields();
|
||||
FieldConstPtrArray const & af = a.getFields();
|
||||
FieldConstPtrArray const & bf = b.getFields();
|
||||
for (size_t i = 0; i < nflds; i++)
|
||||
if (*(af[i].get()) != *(bf[i].get()))
|
||||
return false;
|
||||
|
||||
StringArray an = a.getFieldNames();
|
||||
StringArray bn = b.getFieldNames();
|
||||
StringArray const & an = a.getFieldNames();
|
||||
StringArray const & bn = b.getFieldNames();
|
||||
return std::equal( an.begin(), an.end(), bn.begin() );
|
||||
}
|
||||
|
||||
bool operator==(const UnionArray& a, const UnionArray& b)
|
||||
bool compare(const UnionArray& a, const UnionArray& b)
|
||||
{
|
||||
return *(a.getUnion().get())==*(b.getUnion().get());
|
||||
}
|
||||
|
||||
bool operator==(const BoundedString& a, const BoundedString& b)
|
||||
bool compare(const BoundedString& a, const BoundedString& b)
|
||||
{
|
||||
if(&a==&b)
|
||||
return true;
|
||||
@@ -272,7 +271,7 @@ bool compareField(const PVUnion* left, const PVUnion* right)
|
||||
|
||||
if (ls->isVariant())
|
||||
{
|
||||
PVFieldPtr lval = left->get();
|
||||
const PVField::const_shared_pointer& lval = left->get();
|
||||
if (lval.get() == 0)
|
||||
return right->get().get() == 0;
|
||||
else
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
/* Convert.cpp */
|
||||
/**
|
||||
* Copyright - See the COPYRIGHT that is included with this distribution.
|
||||
* EPICS pvData is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
/*
|
||||
* Copyright information and license terms for this software can be
|
||||
* found in the file LICENSE that is included with the distribution
|
||||
*/
|
||||
/**
|
||||
* @author mrk
|
||||
@@ -13,6 +12,8 @@
|
||||
#include <stdexcept>
|
||||
#include <typeinfo>
|
||||
|
||||
#include <epicsMutex.h>
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include <pv/lock.h>
|
||||
#include <pv/pvIntrospect.h>
|
||||
@@ -52,9 +53,6 @@ void Convert::getString(string *buf,PVField const *pvField,int /*indentLevel*/)
|
||||
std::ostringstream strm;
|
||||
pvField->dumpValue(strm);
|
||||
strm << std::endl;
|
||||
// PrinterPlain p;
|
||||
// p.setStream(strm);
|
||||
// p.print(*pvField);
|
||||
strm.str().swap(*buf);
|
||||
}
|
||||
|
||||
@@ -152,371 +150,6 @@ size_t Convert::toStringArray(PVScalarArrayPtr const & pv,
|
||||
return data.size();
|
||||
}
|
||||
|
||||
bool Convert::isCopyCompatible(FieldConstPtr const &from, FieldConstPtr const &to)
|
||||
{
|
||||
if(from->getType()!=to->getType()) return false;
|
||||
switch(from->getType()) {
|
||||
case scalar:
|
||||
{
|
||||
ScalarConstPtr xxx = static_pointer_cast<const Scalar>(from);
|
||||
ScalarConstPtr yyy = static_pointer_cast<const Scalar>(to);
|
||||
return isCopyScalarCompatible(xxx,yyy);
|
||||
}
|
||||
case scalarArray:
|
||||
{
|
||||
ScalarArrayConstPtr xxx = static_pointer_cast<const ScalarArray>(from);
|
||||
ScalarArrayConstPtr yyy = static_pointer_cast<const ScalarArray>(to);
|
||||
return isCopyScalarArrayCompatible(xxx,yyy);
|
||||
}
|
||||
case structure:
|
||||
{
|
||||
StructureConstPtr xxx = static_pointer_cast<const Structure>(from);
|
||||
StructureConstPtr yyy = static_pointer_cast<const Structure>(to);
|
||||
return isCopyStructureCompatible(xxx,yyy);
|
||||
}
|
||||
case structureArray:
|
||||
{
|
||||
StructureArrayConstPtr xxx = static_pointer_cast<const StructureArray>(from);
|
||||
StructureArrayConstPtr yyy = static_pointer_cast<const StructureArray>(to);
|
||||
return isCopyStructureArrayCompatible(xxx,yyy);
|
||||
}
|
||||
case union_:
|
||||
{
|
||||
UnionConstPtr xxx = static_pointer_cast<const Union>(from);
|
||||
UnionConstPtr yyy = static_pointer_cast<const Union>(to);
|
||||
return isCopyUnionCompatible(xxx,yyy);
|
||||
}
|
||||
case unionArray:
|
||||
{
|
||||
UnionArrayConstPtr xxx = static_pointer_cast<const UnionArray>(from);
|
||||
UnionArrayConstPtr yyy = static_pointer_cast<const UnionArray>(to);
|
||||
return isCopyUnionArrayCompatible(xxx,yyy);
|
||||
}
|
||||
}
|
||||
string message("Convert::isCopyCompatible should never get here");
|
||||
throw std::logic_error(message);
|
||||
}
|
||||
|
||||
void Convert::copy(PVFieldPtr const & from, PVFieldPtr const & to)
|
||||
{
|
||||
switch(from->getField()->getType()) {
|
||||
case scalar:
|
||||
{
|
||||
PVScalarPtr xxx = static_pointer_cast<PVScalar>(from);
|
||||
PVScalarPtr yyy = static_pointer_cast<PVScalar>(to);
|
||||
copyScalar(xxx,yyy);
|
||||
return;
|
||||
}
|
||||
case scalarArray:
|
||||
{
|
||||
PVScalarArrayPtr fromArray = static_pointer_cast<PVScalarArray>(from);
|
||||
PVScalarArrayPtr toArray = static_pointer_cast<PVScalarArray>(to);
|
||||
toArray->assign(*fromArray.get());
|
||||
return;
|
||||
}
|
||||
case structure:
|
||||
{
|
||||
PVStructurePtr xxx = static_pointer_cast<PVStructure>(from);
|
||||
PVStructurePtr yyy = static_pointer_cast<PVStructure>(to);
|
||||
copyStructure(xxx,yyy);
|
||||
return;
|
||||
}
|
||||
case structureArray: {
|
||||
PVStructureArrayPtr fromArray = static_pointer_cast<PVStructureArray>(from);
|
||||
PVStructureArrayPtr toArray = static_pointer_cast<PVStructureArray>(to);
|
||||
copyStructureArray(fromArray,toArray);
|
||||
return;
|
||||
}
|
||||
case union_:
|
||||
{
|
||||
PVUnionPtr xxx = static_pointer_cast<PVUnion>(from);
|
||||
PVUnionPtr yyy = static_pointer_cast<PVUnion>(to);
|
||||
copyUnion(xxx,yyy);
|
||||
return;
|
||||
}
|
||||
case unionArray: {
|
||||
PVUnionArrayPtr fromArray = static_pointer_cast<PVUnionArray>(from);
|
||||
PVUnionArrayPtr toArray = static_pointer_cast<PVUnionArray>(to);
|
||||
copyUnionArray(fromArray,toArray);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool Convert::isCopyScalarCompatible(
|
||||
ScalarConstPtr const & fromField, ScalarConstPtr const & toField)
|
||||
{
|
||||
ScalarType fromScalarType = fromField->getScalarType();
|
||||
ScalarType toScalarType = toField->getScalarType();
|
||||
if(fromScalarType==toScalarType) return true;
|
||||
if(ScalarTypeFunc::isNumeric(fromScalarType)
|
||||
&& ScalarTypeFunc::isNumeric(toScalarType)) return true;
|
||||
if(fromScalarType==pvString) return true;
|
||||
if(toScalarType==pvString) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
void Convert::copyScalar(PVScalarPtr const & from, PVScalarPtr const & to)
|
||||
{
|
||||
if(to->isImmutable()) {
|
||||
if(from==to) return;
|
||||
string message("Convert.copyScalar destination is immutable");
|
||||
throw std::invalid_argument(message);
|
||||
}
|
||||
to->assign(*from.get());
|
||||
}
|
||||
|
||||
bool Convert::isCopyScalarArrayCompatible(ScalarArrayConstPtr const &fromArray,
|
||||
ScalarArrayConstPtr const &toArray)
|
||||
{
|
||||
ScalarType fromType = fromArray->getElementType();
|
||||
ScalarType toType = toArray->getElementType();
|
||||
if(fromType==toType) return true;
|
||||
if(ScalarTypeFunc::isNumeric(fromType)
|
||||
&& ScalarTypeFunc::isNumeric(toType)) return true;
|
||||
if(toType==pvString) return true;
|
||||
if(fromType==pvString) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Convert::isCopyStructureCompatible(
|
||||
StructureConstPtr const &fromStruct, StructureConstPtr const &toStruct)
|
||||
{
|
||||
FieldConstPtrArray fromFields = fromStruct->getFields();
|
||||
FieldConstPtrArray toFields = toStruct->getFields();
|
||||
size_t length = fromStruct->getNumberFields();
|
||||
if(length!=toStruct->getNumberFields()) return false;
|
||||
for(size_t i=0; i<length; i++) {
|
||||
FieldConstPtr from = fromFields[i];
|
||||
FieldConstPtr to = toFields[i];
|
||||
Type fromType = from->getType();
|
||||
Type toType = to->getType();
|
||||
if(fromType!=toType) return false;
|
||||
switch(fromType) {
|
||||
case scalar:
|
||||
{
|
||||
ScalarConstPtr xxx = static_pointer_cast<const Scalar>(from);
|
||||
ScalarConstPtr yyy = static_pointer_cast<const Scalar>(to);
|
||||
if(!isCopyScalarCompatible(xxx,yyy)) return false;
|
||||
}
|
||||
break;
|
||||
case scalarArray:
|
||||
{
|
||||
ScalarArrayConstPtr xxx = static_pointer_cast<const ScalarArray>(from);
|
||||
ScalarArrayConstPtr yyy = static_pointer_cast<const ScalarArray>(to);
|
||||
if(!isCopyScalarArrayCompatible(xxx,yyy)) return false;
|
||||
}
|
||||
break;
|
||||
case structure:
|
||||
{
|
||||
StructureConstPtr xxx = static_pointer_cast<const Structure>(from);
|
||||
StructureConstPtr yyy = static_pointer_cast<const Structure>(to);
|
||||
if(!isCopyStructureCompatible(xxx,yyy)) return false;
|
||||
}
|
||||
break;
|
||||
case structureArray:
|
||||
{
|
||||
StructureArrayConstPtr xxx = static_pointer_cast<const StructureArray>(from);
|
||||
StructureArrayConstPtr yyy = static_pointer_cast<const StructureArray>(to);
|
||||
if(!isCopyStructureArrayCompatible(xxx,yyy)) return false;
|
||||
}
|
||||
case union_:
|
||||
{
|
||||
UnionConstPtr xxx = static_pointer_cast<const Union>(from);
|
||||
UnionConstPtr yyy = static_pointer_cast<const Union>(to);
|
||||
if(!isCopyUnionCompatible(xxx,yyy)) return false;
|
||||
}
|
||||
break;
|
||||
case unionArray:
|
||||
{
|
||||
UnionArrayConstPtr xxx = static_pointer_cast<const UnionArray>(from);
|
||||
UnionArrayConstPtr yyy = static_pointer_cast<const UnionArray>(to);
|
||||
if(!isCopyUnionArrayCompatible(xxx,yyy)) return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void Convert::copyStructure(PVStructurePtr const & from, PVStructurePtr const & to)
|
||||
{
|
||||
if(to->isImmutable()) {
|
||||
if(from==to) return;
|
||||
throw std::invalid_argument("Convert.copyStructure destination is immutable");
|
||||
}
|
||||
if(from==to) return;
|
||||
PVFieldPtrArray const & fromDatas = from->getPVFields();
|
||||
PVFieldPtrArray const & toDatas = to->getPVFields();
|
||||
if(from->getStructure()->getNumberFields()
|
||||
!= to->getStructure()->getNumberFields()) {
|
||||
string message("Convert.copyStructure Illegal copyStructure");
|
||||
throw std::invalid_argument(message);
|
||||
}
|
||||
size_t numberFields = from->getStructure()->getNumberFields();
|
||||
if(numberFields>=2) {
|
||||
string name0 = fromDatas[0]->getFieldName();
|
||||
string name1 = fromDatas[1]->getFieldName();
|
||||
// look for enumerated structure and copy choices first
|
||||
if(name0.compare("index")==0 && name1.compare("choices")==0) {
|
||||
FieldConstPtr fieldIndex = fromDatas[0]->getField();
|
||||
FieldConstPtr fieldChoices = fromDatas[1]->getField();
|
||||
if(fieldIndex->getType()==scalar
|
||||
&& fieldChoices->getType()==scalarArray) {
|
||||
PVScalarPtr pvScalar = static_pointer_cast<PVScalar>(fromDatas[0]);
|
||||
PVScalarArrayPtr pvArray =
|
||||
static_pointer_cast<PVScalarArray>(fromDatas[1]);
|
||||
if((pvScalar->getScalar()->getScalarType()==pvInt)
|
||||
&& (pvArray->getScalarArray()->getElementType()==pvString)) {
|
||||
PVScalarArrayPtr toArray =
|
||||
static_pointer_cast<PVScalarArray>(toDatas[1]);
|
||||
toArray->assign(*pvArray.get());
|
||||
PVScalarPtr toScalar = static_pointer_cast<PVScalar>(toDatas[0]);
|
||||
copyScalar(pvScalar,toScalar);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for(size_t i=0; i < numberFields; i++) {
|
||||
PVFieldPtr fromData = fromDatas[i];
|
||||
PVFieldPtr toData = toDatas[i];
|
||||
Type fromType = fromData->getField()->getType();
|
||||
Type toType = toData->getField()->getType();
|
||||
if(fromType!=toType) {
|
||||
string message("Convert.copyStructure Illegal copyStructure");
|
||||
throw std::invalid_argument(message);
|
||||
}
|
||||
if(toData->isImmutable()) {
|
||||
if(fromData==toData) return;
|
||||
throw std::invalid_argument("Convert.copyStructure destination is immutable");
|
||||
}
|
||||
switch(fromType) {
|
||||
case scalar:
|
||||
{
|
||||
PVScalarPtr xxx = static_pointer_cast<PVScalar>(fromData);
|
||||
PVScalarPtr yyy = static_pointer_cast<PVScalar>(toData);
|
||||
copyScalar(xxx,yyy);
|
||||
break;
|
||||
}
|
||||
case scalarArray:
|
||||
{
|
||||
PVScalarArrayPtr fromArray = static_pointer_cast<PVScalarArray>(fromData);
|
||||
PVScalarArrayPtr toArray = static_pointer_cast<PVScalarArray>(toData);
|
||||
toArray->assign(*fromArray.get());
|
||||
break;
|
||||
}
|
||||
case structure:
|
||||
{
|
||||
PVStructurePtr xxx = static_pointer_cast<PVStructure>(fromData);
|
||||
PVStructurePtr yyy = static_pointer_cast<PVStructure>(toData);
|
||||
copyStructure(xxx,yyy);
|
||||
break;
|
||||
}
|
||||
case structureArray:
|
||||
{
|
||||
PVStructureArrayPtr fromArray =
|
||||
static_pointer_cast<PVStructureArray>(fromData);
|
||||
PVStructureArrayPtr toArray =
|
||||
static_pointer_cast<PVStructureArray>(toData);
|
||||
copyStructureArray(fromArray,toArray);
|
||||
break;
|
||||
}
|
||||
case union_:
|
||||
{
|
||||
PVUnionPtr xxx = static_pointer_cast<PVUnion>(fromData);
|
||||
PVUnionPtr yyy = static_pointer_cast<PVUnion>(toData);
|
||||
copyUnion(xxx,yyy);
|
||||
break;
|
||||
}
|
||||
case unionArray:
|
||||
{
|
||||
PVUnionArrayPtr fromArray =
|
||||
static_pointer_cast<PVUnionArray>(fromData);
|
||||
PVUnionArrayPtr toArray =
|
||||
static_pointer_cast<PVUnionArray>(toData);
|
||||
copyUnionArray(fromArray,toArray);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool Convert::isCopyUnionCompatible(
|
||||
UnionConstPtr const &from, UnionConstPtr const &to)
|
||||
{
|
||||
return *(from.get()) == *(to.get());
|
||||
}
|
||||
|
||||
void Convert::copyUnion(PVUnionPtr const & from, PVUnionPtr const & to)
|
||||
{
|
||||
if(to->isImmutable()) {
|
||||
if(from==to) return;
|
||||
throw std::invalid_argument("Convert.copyUnion destination is immutable");
|
||||
}
|
||||
if(from==to) return;
|
||||
if(!isCopyUnionCompatible(from->getUnion(), to->getUnion())) {
|
||||
throw std::invalid_argument("Illegal copyUnion");
|
||||
}
|
||||
|
||||
PVFieldPtr fromValue = from->get();
|
||||
if (from->getUnion()->isVariant())
|
||||
{
|
||||
to->set(from->get());
|
||||
}
|
||||
else
|
||||
{
|
||||
if (fromValue.get() == 0)
|
||||
to->select(PVUnion::UNDEFINED_INDEX);
|
||||
else
|
||||
to->set(from->getSelectedIndex(),from->get());
|
||||
}
|
||||
}
|
||||
|
||||
bool Convert::isCopyStructureArrayCompatible(
|
||||
StructureArrayConstPtr const &from, StructureArrayConstPtr const &to)
|
||||
{
|
||||
StructureConstPtr xxx = from->getStructure();
|
||||
StructureConstPtr yyy = to->getStructure();
|
||||
return isCopyStructureCompatible(xxx,yyy);
|
||||
}
|
||||
|
||||
void Convert::copyStructureArray(
|
||||
PVStructureArrayPtr const & from, PVStructureArrayPtr const & to)
|
||||
{
|
||||
if(from==to) {
|
||||
return;
|
||||
} else if(to->isImmutable()) {
|
||||
throw std::invalid_argument("Convert.copyStructureArray destination is immutable");
|
||||
}
|
||||
to->replace(from->view());
|
||||
}
|
||||
|
||||
bool Convert::isCopyUnionArrayCompatible(
|
||||
UnionArrayConstPtr const &from, UnionArrayConstPtr const &to)
|
||||
{
|
||||
UnionConstPtr xxx = from->getUnion();
|
||||
UnionConstPtr yyy = to->getUnion();
|
||||
return isCopyUnionCompatible(xxx,yyy);
|
||||
}
|
||||
|
||||
void Convert::copyUnionArray(
|
||||
PVUnionArrayPtr const & from, PVUnionArrayPtr const & to)
|
||||
{
|
||||
if(from==to) {
|
||||
return;
|
||||
} else if(to->isImmutable()) {
|
||||
throw std::invalid_argument("Convert.copyUnionArray destination is immutable");
|
||||
}
|
||||
to->replace(from->view());
|
||||
}
|
||||
|
||||
void Convert::newLine(string *buffer, int indentLevel)
|
||||
{
|
||||
*buffer += "\n";
|
||||
*buffer += string(indentLevel*4, ' ');
|
||||
}
|
||||
|
||||
ConvertPtr Convert::getConvert()
|
||||
{
|
||||
static ConvertPtr convert;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -2,7 +2,7 @@
|
||||
|
||||
SRC_DIRS += $(PVDATA_SRC)/factory
|
||||
|
||||
INC += factory.h
|
||||
INC += pv/factory.h
|
||||
LIBSRCS += TypeFunc.cpp
|
||||
LIBSRCS += FieldCreateFactory.cpp
|
||||
LIBSRCS += PVField.cpp
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
/*PVArray.cpp*/
|
||||
/**
|
||||
* Copyright - See the COPYRIGHT that is included with this distribution.
|
||||
* EPICS pvData is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
/*
|
||||
* Copyright information and license terms for this software can be
|
||||
* found in the file LICENSE that is included with the distribution
|
||||
*/
|
||||
/**
|
||||
* @author mrk
|
||||
|
||||
@@ -1,29 +1,27 @@
|
||||
/*PVDataCreateFactory.cpp*/
|
||||
/**
|
||||
* Copyright - See the COPYRIGHT that is included with this distribution.
|
||||
* EPICS pvData is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
/*
|
||||
* Copyright information and license terms for this software can be
|
||||
* found in the file LICENSE that is included with the distribution
|
||||
*/
|
||||
/**
|
||||
* @author mrk
|
||||
*/
|
||||
|
||||
#ifdef _WIN32
|
||||
#define NOMINMAX
|
||||
#endif
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdlib>
|
||||
#include <string>
|
||||
#include <cstdio>
|
||||
|
||||
#include <epicsMutex.h>
|
||||
#include <epicsThread.h>
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include <pv/lock.h>
|
||||
#include <pv/pvIntrospect.h>
|
||||
#include <pv/pvData.h>
|
||||
#include <pv/convert.h>
|
||||
#include <pv/factory.h>
|
||||
#include <pv/serializeHelper.h>
|
||||
#include <pv/reftrack.h>
|
||||
|
||||
using std::tr1::static_pointer_cast;
|
||||
using std::size_t;
|
||||
@@ -59,135 +57,111 @@ template<> const ScalarType PVFloatArray::typeCode = pvFloat;
|
||||
template<> const ScalarType PVDoubleArray::typeCode = pvDouble;
|
||||
template<> const ScalarType PVStringArray::typeCode = pvString;
|
||||
|
||||
/** Default storage for scalar values
|
||||
*/
|
||||
template<typename T>
|
||||
class BasePVScalar : public PVScalarValue<T> {
|
||||
public:
|
||||
typedef T value_type;
|
||||
typedef T* pointer;
|
||||
typedef const T* const_pointer;
|
||||
|
||||
BasePVScalar(ScalarConstPtr const & scalar);
|
||||
virtual ~BasePVScalar();
|
||||
virtual T get() const ;
|
||||
virtual void put(T val);
|
||||
virtual void serialize(ByteBuffer *pbuffer,
|
||||
SerializableControl *pflusher) const;
|
||||
virtual void deserialize(ByteBuffer *pbuffer,
|
||||
DeserializableControl *pflusher);
|
||||
private:
|
||||
T value;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
BasePVScalar<T>::BasePVScalar(ScalarConstPtr const & scalar)
|
||||
: PVScalarValue<T>(scalar),value(0)
|
||||
{}
|
||||
//Note: '0' is a suitable default for all POD types (not string)
|
||||
PVScalarValue<T>::~PVScalarValue() {}
|
||||
|
||||
template<typename T>
|
||||
BasePVScalar<T>::~BasePVScalar() {}
|
||||
|
||||
template<typename T>
|
||||
T BasePVScalar<T>::get() const { return value;}
|
||||
|
||||
template<typename T>
|
||||
void BasePVScalar<T>::put(T val)
|
||||
std::ostream& PVScalarValue<T>::dumpValue(std::ostream& o) const
|
||||
{
|
||||
value = val;
|
||||
PVField::postPut();
|
||||
return o << get();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void BasePVScalar<T>::serialize(ByteBuffer *pbuffer,
|
||||
void PVScalarValue<T>::operator>>=(T& value) const
|
||||
{
|
||||
value = get();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void PVScalarValue<T>::operator<<=(typename storage_t::arg_type value)
|
||||
{
|
||||
put(value);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void PVScalarValue<T>::assign(const PVScalar& scalar)
|
||||
{
|
||||
if(isImmutable())
|
||||
throw std::invalid_argument("destination is immutable");
|
||||
copyUnchecked(scalar);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void PVScalarValue<T>::copy(const PVScalar& from)
|
||||
{
|
||||
assign(from);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void PVScalarValue<T>::copyUnchecked(const PVScalar& from)
|
||||
{
|
||||
if(this==&from)
|
||||
return;
|
||||
T result;
|
||||
from.getAs((void*)&result, typeCode);
|
||||
put(result);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void PVScalarValue<T>::serialize(ByteBuffer *pbuffer,
|
||||
SerializableControl *pflusher) const {
|
||||
pflusher->ensureBuffer(sizeof(T));
|
||||
pbuffer->put(value);
|
||||
pbuffer->put(storage.value);
|
||||
}
|
||||
|
||||
template<>
|
||||
void PVScalarValue<std::string>::serialize(ByteBuffer *pbuffer,
|
||||
SerializableControl *pflusher) const {
|
||||
SerializeHelper::serializeString(storage.value, pbuffer, pflusher);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void BasePVScalar<T>::deserialize(ByteBuffer *pbuffer,
|
||||
void PVScalarValue<T>::deserialize(ByteBuffer *pbuffer,
|
||||
DeserializableControl *pflusher)
|
||||
{
|
||||
pflusher->ensureData(sizeof(T));
|
||||
value = pbuffer->GET(T);
|
||||
storage.value = pbuffer->GET(T);
|
||||
}
|
||||
|
||||
typedef BasePVScalar<boolean> BasePVBoolean;
|
||||
typedef BasePVScalar<int8> BasePVByte;
|
||||
typedef BasePVScalar<int16> BasePVShort;
|
||||
typedef BasePVScalar<int32> BasePVInt;
|
||||
typedef BasePVScalar<int64> BasePVLong;
|
||||
typedef BasePVScalar<uint8> BasePVUByte;
|
||||
typedef BasePVScalar<uint16> BasePVUShort;
|
||||
typedef BasePVScalar<uint32> BasePVUInt;
|
||||
typedef BasePVScalar<uint64> BasePVULong;
|
||||
typedef BasePVScalar<float> BasePVFloat;
|
||||
typedef BasePVScalar<double> BasePVDouble;
|
||||
template<>
|
||||
void PVScalarValue<std::string>::deserialize(ByteBuffer *pbuffer,
|
||||
DeserializableControl *pflusher)
|
||||
{
|
||||
storage.value = SerializeHelper::deserializeString(pbuffer, pflusher);
|
||||
// TODO: check for violations of maxLength?
|
||||
}
|
||||
|
||||
// BasePVString is special case, since it implements SerializableArray
|
||||
class BasePVString : public PVString {
|
||||
public:
|
||||
typedef string value_type;
|
||||
typedef string* pointer;
|
||||
typedef const string* const_pointer;
|
||||
|
||||
BasePVString(ScalarConstPtr const & scalar);
|
||||
virtual ~BasePVString();
|
||||
virtual string get() const ;
|
||||
virtual void put(string val);
|
||||
virtual void serialize(ByteBuffer *pbuffer,
|
||||
SerializableControl *pflusher) const;
|
||||
virtual void deserialize(ByteBuffer *pbuffer,
|
||||
DeserializableControl *pflusher);
|
||||
virtual void serialize(ByteBuffer *pbuffer,
|
||||
SerializableControl *pflusher, size_t offset, size_t count) const;
|
||||
private:
|
||||
string value;
|
||||
std::size_t maxLength;
|
||||
};
|
||||
|
||||
BasePVString::BasePVString(ScalarConstPtr const & scalar)
|
||||
: PVString(scalar),value()
|
||||
PVString::PVString(ScalarConstPtr const & scalar)
|
||||
: PVScalarValue<std::string>(scalar)
|
||||
{
|
||||
BoundedStringConstPtr boundedString = std::tr1::dynamic_pointer_cast<const BoundedString>(scalar);
|
||||
if (boundedString.get())
|
||||
maxLength = boundedString->getMaximumLength();
|
||||
storage.maxLength = boundedString->getMaximumLength();
|
||||
else
|
||||
maxLength = 0;
|
||||
storage.maxLength = 0;
|
||||
}
|
||||
|
||||
BasePVString::~BasePVString() {}
|
||||
|
||||
string BasePVString::get() const { return value;}
|
||||
|
||||
void BasePVString::put(string val)
|
||||
std::ostream& PVString::dumpValue(std::ostream& o) const
|
||||
{
|
||||
if (maxLength > 0 && val.length() > maxLength)
|
||||
throw std::overflow_error("string too long");
|
||||
|
||||
value = val;
|
||||
postPut();
|
||||
// we escape, but do not quote, for scalar string
|
||||
o<<escape(get());
|
||||
return o;
|
||||
}
|
||||
|
||||
void BasePVString::serialize(ByteBuffer *pbuffer,
|
||||
/* mixing overrides (virtual functions) and overloads (different argument lists) is fun...
|
||||
* we override all overloads to avoid the "hides overloaded virtual function" warning from clang.
|
||||
* In this case we don't need/want to, so just delegate to the base class.
|
||||
*/
|
||||
void PVString::serialize(ByteBuffer *pbuffer,
|
||||
SerializableControl *pflusher) const
|
||||
{
|
||||
SerializeHelper::serializeString(value, pbuffer, pflusher);
|
||||
}
|
||||
{PVScalarValue<std::string>::serialize(pbuffer, pflusher);}
|
||||
|
||||
void BasePVString::deserialize(ByteBuffer *pbuffer,
|
||||
DeserializableControl *pflusher)
|
||||
{
|
||||
value = SerializeHelper::deserializeString(pbuffer, pflusher);
|
||||
}
|
||||
|
||||
void BasePVString::serialize(ByteBuffer *pbuffer,
|
||||
void PVString::serialize(ByteBuffer *pbuffer,
|
||||
SerializableControl *pflusher, size_t offset, size_t count) const
|
||||
{
|
||||
// check bounds
|
||||
const size_t length = /*(value == null) ? 0 :*/ value.length();
|
||||
const size_t length = storage.value.length();
|
||||
/*if (offset < 0) offset = 0;
|
||||
else*/ if (offset > length) offset = length;
|
||||
//if (count < 0) count = length;
|
||||
@@ -197,10 +171,10 @@ void BasePVString::serialize(ByteBuffer *pbuffer,
|
||||
count = maxCount;
|
||||
|
||||
// write
|
||||
SerializeHelper::serializeSubstring(value, offset, count, pbuffer, pflusher);
|
||||
SerializeHelper::serializeSubstring(storage.value, offset, count, pbuffer, pflusher);
|
||||
}
|
||||
|
||||
void PVArray::checkLength(size_t len)
|
||||
void PVArray::checkLength(size_t len) const
|
||||
{
|
||||
Array::ArraySizeType type = getArray()->getArraySizeType();
|
||||
if (type != Array::variable)
|
||||
@@ -213,61 +187,79 @@ void PVArray::checkLength(size_t len)
|
||||
}
|
||||
}
|
||||
|
||||
/** Default storage for arrays
|
||||
*/
|
||||
template<typename T>
|
||||
class DefaultPVArray : public PVValueArray<T> {
|
||||
public:
|
||||
typedef T* pointer;
|
||||
typedef const T* const_pointer;
|
||||
typedef std::vector<T> vector;
|
||||
typedef const std::vector<T> const_vector;
|
||||
typedef std::tr1::shared_ptr<vector> shared_vector;
|
||||
|
||||
typedef ::epics::pvData::shared_vector<T> svector;
|
||||
typedef ::epics::pvData::shared_vector<const T> const_svector;
|
||||
|
||||
DefaultPVArray(ScalarArrayConstPtr const & scalarArray);
|
||||
virtual ~DefaultPVArray();
|
||||
|
||||
virtual size_t getLength() const {return value.size();}
|
||||
virtual size_t getCapacity() const {return value.capacity();}
|
||||
|
||||
virtual void setCapacity(size_t capacity);
|
||||
virtual void setLength(size_t length);
|
||||
|
||||
virtual const_svector view() const {return value;}
|
||||
virtual void swap(const_svector &other);
|
||||
virtual void replace(const const_svector& next);
|
||||
|
||||
// from Serializable
|
||||
virtual void serialize(ByteBuffer *pbuffer,SerializableControl *pflusher) const;
|
||||
virtual void deserialize(ByteBuffer *pbuffer,DeserializableControl *pflusher);
|
||||
virtual void serialize(ByteBuffer *pbuffer,
|
||||
SerializableControl *pflusher, size_t offset, size_t count) const;
|
||||
private:
|
||||
const_svector value;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
DefaultPVArray<T>::DefaultPVArray(ScalarArrayConstPtr const & scalarArray)
|
||||
: PVValueArray<T>(scalarArray),
|
||||
value()
|
||||
PVValueArray<T>::PVValueArray(ScalarArrayConstPtr const & scalarArray)
|
||||
:base_t(scalarArray)
|
||||
,value()
|
||||
|
||||
{}
|
||||
|
||||
PVValueArray<PVStructurePtr>::PVValueArray(StructureArrayConstPtr const & structureArray)
|
||||
:base_t(structureArray)
|
||||
,structureArray(structureArray)
|
||||
|
||||
{}
|
||||
|
||||
PVValueArray<PVUnionPtr>::PVValueArray(UnionArrayConstPtr const & unionArray)
|
||||
:base_t(unionArray)
|
||||
,unionArray(unionArray)
|
||||
{}
|
||||
|
||||
template<typename T>
|
||||
PVValueArray<T>::~PVValueArray() {}
|
||||
|
||||
template<typename T>
|
||||
ArrayConstPtr PVValueArray<T>::getArray() const
|
||||
{
|
||||
ArrayConstPtr array = this->getArray();
|
||||
if (array->getArraySizeType() == Array::fixed)
|
||||
{
|
||||
// this->setLength(array->getMaximumCapacity());
|
||||
this->setCapacityMutable(false);
|
||||
}
|
||||
return std::tr1::static_pointer_cast<const Array>(this->getField());
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
DefaultPVArray<T>::~DefaultPVArray()
|
||||
{ }
|
||||
std::ostream& PVValueArray<T>::dumpValue(std::ostream& o) const
|
||||
{
|
||||
const_svector v(this->view());
|
||||
typename const_svector::const_iterator it(v.begin()),
|
||||
end(v.end());
|
||||
o << '[';
|
||||
if(it!=end) {
|
||||
o << print_cast(*it++);
|
||||
for(; it!=end; ++it)
|
||||
o << ',' << print_cast(*it);
|
||||
|
||||
}
|
||||
return o << ']';
|
||||
}
|
||||
|
||||
template<>
|
||||
std::ostream& PVValueArray<std::string>::dumpValue(std::ostream& o, size_t index) const
|
||||
{
|
||||
return o << '"' << escape(this->view().at(index)) << '"';
|
||||
}
|
||||
|
||||
template<>
|
||||
std::ostream& PVValueArray<std::string>::dumpValue(std::ostream& o) const
|
||||
{
|
||||
const_svector v(this->view());
|
||||
const_svector::const_iterator it(v.begin()),
|
||||
end(v.end());
|
||||
o << '[';
|
||||
if(it!=end) {
|
||||
o << '"' << escape(*it++) << '"';
|
||||
for(; it!=end; ++it)
|
||||
o << ", \"" << escape(*it) << '"';
|
||||
|
||||
}
|
||||
return o << ']';
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void DefaultPVArray<T>::setCapacity(size_t capacity)
|
||||
std::ostream& PVValueArray<T>::dumpValue(std::ostream& o, size_t index) const
|
||||
{
|
||||
return o << print_cast(this->view().at(index));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void PVValueArray<T>::setCapacity(size_t capacity)
|
||||
{
|
||||
if(this->isCapacityMutable()) {
|
||||
this->checkLength(capacity);
|
||||
@@ -278,7 +270,7 @@ void DefaultPVArray<T>::setCapacity(size_t capacity)
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void DefaultPVArray<T>::setLength(size_t length)
|
||||
void PVValueArray<T>::setLength(size_t length)
|
||||
{
|
||||
if(this->isImmutable())
|
||||
THROW_EXCEPTION2(std::logic_error, "immutable");
|
||||
@@ -295,7 +287,7 @@ void DefaultPVArray<T>::setLength(size_t length)
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void DefaultPVArray<T>::replace(const const_svector& next)
|
||||
void PVValueArray<T>::replace(const const_svector& next)
|
||||
{
|
||||
this->checkLength(next.size());
|
||||
|
||||
@@ -304,7 +296,7 @@ void DefaultPVArray<T>::replace(const const_svector& next)
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void DefaultPVArray<T>::swap(const_svector &other)
|
||||
void PVValueArray<T>::swap(const_svector &other)
|
||||
{
|
||||
if (this->isImmutable())
|
||||
THROW_EXCEPTION2(std::logic_error, "immutable");
|
||||
@@ -316,13 +308,13 @@ void DefaultPVArray<T>::swap(const_svector &other)
|
||||
|
||||
|
||||
template<typename T>
|
||||
void DefaultPVArray<T>::serialize(ByteBuffer *pbuffer,
|
||||
void PVValueArray<T>::serialize(ByteBuffer *pbuffer,
|
||||
SerializableControl *pflusher) const {
|
||||
serialize(pbuffer, pflusher, 0, this->getLength());
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void DefaultPVArray<T>::deserialize(ByteBuffer *pbuffer,
|
||||
void PVValueArray<T>::deserialize(ByteBuffer *pbuffer,
|
||||
DeserializableControl *pcontrol) {
|
||||
|
||||
size_t size = this->getArray()->getArraySizeType() == Array::fixed ?
|
||||
@@ -371,7 +363,7 @@ void DefaultPVArray<T>::deserialize(ByteBuffer *pbuffer,
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void DefaultPVArray<T>::serialize(ByteBuffer *pbuffer,
|
||||
void PVValueArray<T>::serialize(ByteBuffer *pbuffer,
|
||||
SerializableControl *pflusher, size_t offset, size_t count) const
|
||||
{
|
||||
//TODO: avoid incrementing the ref counter...
|
||||
@@ -415,7 +407,7 @@ void DefaultPVArray<T>::serialize(ByteBuffer *pbuffer,
|
||||
// specializations for string
|
||||
|
||||
template<>
|
||||
void DefaultPVArray<string>::deserialize(ByteBuffer *pbuffer,
|
||||
void PVValueArray<string>::deserialize(ByteBuffer *pbuffer,
|
||||
DeserializableControl *pcontrol) {
|
||||
|
||||
size_t size = this->getArray()->getArraySizeType() == Array::fixed ?
|
||||
@@ -442,7 +434,7 @@ void DefaultPVArray<string>::deserialize(ByteBuffer *pbuffer,
|
||||
}
|
||||
|
||||
template<>
|
||||
void DefaultPVArray<string>::serialize(ByteBuffer *pbuffer,
|
||||
void PVValueArray<string>::serialize(ByteBuffer *pbuffer,
|
||||
SerializableControl *pflusher, size_t offset, size_t count) const {
|
||||
|
||||
const_svector temp(value);
|
||||
@@ -458,18 +450,17 @@ void DefaultPVArray<string>::serialize(ByteBuffer *pbuffer,
|
||||
}
|
||||
}
|
||||
|
||||
typedef DefaultPVArray<boolean> DefaultPVBooleanArray;
|
||||
typedef DefaultPVArray<int8> BasePVByteArray;
|
||||
typedef DefaultPVArray<int16> BasePVShortArray;
|
||||
typedef DefaultPVArray<int32> BasePVIntArray;
|
||||
typedef DefaultPVArray<int64> BasePVLongArray;
|
||||
typedef DefaultPVArray<uint8> BasePVUByteArray;
|
||||
typedef DefaultPVArray<uint16> BasePVUShortArray;
|
||||
typedef DefaultPVArray<uint32> BasePVUIntArray;
|
||||
typedef DefaultPVArray<uint64> BasePVULongArray;
|
||||
typedef DefaultPVArray<float> BasePVFloatArray;
|
||||
typedef DefaultPVArray<double> BasePVDoubleArray;
|
||||
typedef DefaultPVArray<string> BasePVStringArray;
|
||||
template<typename T>
|
||||
void PVValueArray<T>::_getAsVoid(epics::pvData::shared_vector<const void>& out) const
|
||||
{
|
||||
out = static_shared_vector_cast<const void>(this->view());
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void PVValueArray<T>::_putFromVoid(const epics::pvData::shared_vector<const void>& in)
|
||||
{
|
||||
this->replace(shared_vector_convert<const T>(in));
|
||||
}
|
||||
|
||||
// Factory
|
||||
|
||||
@@ -527,7 +518,7 @@ PVFieldPtr PVDataCreate::createPVField(PVFieldPtr const & fieldToClone)
|
||||
PVStructurePtr pvStructure
|
||||
= static_pointer_cast<PVStructure>(fieldToClone);
|
||||
StringArray const & fieldNames = pvStructure->getStructure()->getFieldNames();
|
||||
PVFieldPtrArray pvFieldPtrArray = pvStructure->getPVFields();
|
||||
PVFieldPtrArray const & pvFieldPtrArray = pvStructure->getPVFields();
|
||||
return createPVStructure(fieldNames,pvFieldPtrArray);
|
||||
}
|
||||
case structureArray:
|
||||
@@ -537,7 +528,7 @@ PVFieldPtr PVDataCreate::createPVField(PVFieldPtr const & fieldToClone)
|
||||
StructureArrayConstPtr structureArray = from->getStructureArray();
|
||||
PVStructureArrayPtr to = createPVStructureArray(
|
||||
structureArray);
|
||||
getConvert()->copyStructureArray(from, to);
|
||||
to->copyUnchecked(*from);
|
||||
return to;
|
||||
}
|
||||
case union_:
|
||||
@@ -552,7 +543,7 @@ PVFieldPtr PVDataCreate::createPVField(PVFieldPtr const & fieldToClone)
|
||||
= static_pointer_cast<PVUnionArray>(fieldToClone);
|
||||
UnionArrayConstPtr unionArray = from->getUnionArray();
|
||||
PVUnionArrayPtr to = createPVUnionArray(unionArray);
|
||||
getConvert()->copyUnionArray(from, to);
|
||||
to->copyUnchecked(*from);
|
||||
return to;
|
||||
}
|
||||
}
|
||||
@@ -564,29 +555,29 @@ PVScalarPtr PVDataCreate::createPVScalar(ScalarConstPtr const & scalar)
|
||||
ScalarType scalarType = scalar->getScalarType();
|
||||
switch(scalarType) {
|
||||
case pvBoolean:
|
||||
return PVScalarPtr(new BasePVBoolean(scalar));
|
||||
return PVScalarPtr(new PVBoolean(scalar));
|
||||
case pvByte:
|
||||
return PVScalarPtr(new BasePVByte(scalar));
|
||||
return PVScalarPtr(new PVByte(scalar));
|
||||
case pvShort:
|
||||
return PVScalarPtr(new BasePVShort(scalar));
|
||||
return PVScalarPtr(new PVShort(scalar));
|
||||
case pvInt:
|
||||
return PVScalarPtr(new BasePVInt(scalar));
|
||||
return PVScalarPtr(new PVInt(scalar));
|
||||
case pvLong:
|
||||
return PVScalarPtr(new BasePVLong(scalar));
|
||||
return PVScalarPtr(new PVLong(scalar));
|
||||
case pvUByte:
|
||||
return PVScalarPtr(new BasePVUByte(scalar));
|
||||
return PVScalarPtr(new PVUByte(scalar));
|
||||
case pvUShort:
|
||||
return PVScalarPtr(new BasePVUShort(scalar));
|
||||
return PVScalarPtr(new PVUShort(scalar));
|
||||
case pvUInt:
|
||||
return PVScalarPtr(new BasePVUInt(scalar));
|
||||
return PVScalarPtr(new PVUInt(scalar));
|
||||
case pvULong:
|
||||
return PVScalarPtr(new BasePVULong(scalar));
|
||||
return PVScalarPtr(new PVULong(scalar));
|
||||
case pvFloat:
|
||||
return PVScalarPtr(new BasePVFloat(scalar));
|
||||
return PVScalarPtr(new PVFloat(scalar));
|
||||
case pvDouble:
|
||||
return PVScalarPtr(new BasePVDouble(scalar));
|
||||
return PVScalarPtr(new PVDouble(scalar));
|
||||
case pvString:
|
||||
return PVScalarPtr(new BasePVString(scalar));
|
||||
return PVScalarPtr(new PVString(scalar));
|
||||
}
|
||||
throw std::logic_error("PVDataCreate::createPVScalar should never get here");
|
||||
}
|
||||
@@ -602,7 +593,7 @@ PVScalarPtr PVDataCreate::createPVScalar(PVScalarPtr const & scalarToClone)
|
||||
{
|
||||
ScalarType scalarType = scalarToClone->getScalar()->getScalarType();
|
||||
PVScalarPtr pvScalar = createPVScalar(scalarType);
|
||||
getConvert()->copyScalar(scalarToClone, pvScalar);
|
||||
pvScalar->copyUnchecked(*scalarToClone);
|
||||
return pvScalar;
|
||||
}
|
||||
|
||||
@@ -611,29 +602,29 @@ PVScalarArrayPtr PVDataCreate::createPVScalarArray(
|
||||
{
|
||||
switch(scalarArray->getElementType()) {
|
||||
case pvBoolean:
|
||||
return PVScalarArrayPtr(new DefaultPVBooleanArray(scalarArray));
|
||||
return PVScalarArrayPtr(new PVBooleanArray(scalarArray));
|
||||
case pvByte:
|
||||
return PVScalarArrayPtr(new BasePVByteArray(scalarArray));
|
||||
return PVScalarArrayPtr(new PVByteArray(scalarArray));
|
||||
case pvShort:
|
||||
return PVScalarArrayPtr(new BasePVShortArray(scalarArray));
|
||||
return PVScalarArrayPtr(new PVShortArray(scalarArray));
|
||||
case pvInt:
|
||||
return PVScalarArrayPtr(new BasePVIntArray(scalarArray));
|
||||
return PVScalarArrayPtr(new PVIntArray(scalarArray));
|
||||
case pvLong:
|
||||
return PVScalarArrayPtr(new BasePVLongArray(scalarArray));
|
||||
return PVScalarArrayPtr(new PVLongArray(scalarArray));
|
||||
case pvUByte:
|
||||
return PVScalarArrayPtr(new BasePVUByteArray(scalarArray));
|
||||
return PVScalarArrayPtr(new PVUByteArray(scalarArray));
|
||||
case pvUShort:
|
||||
return PVScalarArrayPtr(new BasePVUShortArray(scalarArray));
|
||||
return PVScalarArrayPtr(new PVUShortArray(scalarArray));
|
||||
case pvUInt:
|
||||
return PVScalarArrayPtr(new BasePVUIntArray(scalarArray));
|
||||
return PVScalarArrayPtr(new PVUIntArray(scalarArray));
|
||||
case pvULong:
|
||||
return PVScalarArrayPtr(new BasePVULongArray(scalarArray));
|
||||
return PVScalarArrayPtr(new PVULongArray(scalarArray));
|
||||
case pvFloat:
|
||||
return PVScalarArrayPtr(new BasePVFloatArray(scalarArray));
|
||||
return PVScalarArrayPtr(new PVFloatArray(scalarArray));
|
||||
case pvDouble:
|
||||
return PVScalarArrayPtr(new BasePVDoubleArray(scalarArray));
|
||||
return PVScalarArrayPtr(new PVDoubleArray(scalarArray));
|
||||
case pvString:
|
||||
return PVScalarArrayPtr(new BasePVStringArray(scalarArray));
|
||||
return PVScalarArrayPtr(new PVStringArray(scalarArray));
|
||||
}
|
||||
throw std::logic_error("PVDataCreate::createPVScalarArray should never get here");
|
||||
|
||||
@@ -712,7 +703,7 @@ PVStructurePtr PVDataCreate::createPVStructure(PVStructurePtr const & structToCl
|
||||
}
|
||||
StructureConstPtr structure = structToClone->getStructure();
|
||||
PVStructurePtr pvStructure(new PVStructure(structure));
|
||||
getConvert()->copyStructure(structToClone,pvStructure);
|
||||
pvStructure->copyUnchecked(*structToClone);
|
||||
return pvStructure;
|
||||
}
|
||||
|
||||
@@ -724,20 +715,57 @@ PVUnionPtr PVDataCreate::createPVUnion(PVUnionPtr const & unionToClone)
|
||||
return punion;
|
||||
}
|
||||
|
||||
// TODO not thread-safe (local static initializers)
|
||||
// TODO replace with non-locking singleton pattern
|
||||
PVDataCreatePtr PVDataCreate::getPVDataCreate()
|
||||
namespace detail {
|
||||
struct pvfield_factory {
|
||||
PVDataCreatePtr pvDataCreate;
|
||||
pvfield_factory() :pvDataCreate(new PVDataCreate()) {
|
||||
registerRefCounter("PVField", &PVField::num_instances);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
static detail::pvfield_factory* pvfield_factory_s;
|
||||
static epicsThreadOnceId pvfield_factory_once = EPICS_THREAD_ONCE_INIT;
|
||||
|
||||
static void pvfield_factory_init(void*)
|
||||
{
|
||||
static PVDataCreatePtr pvDataCreate;
|
||||
static Mutex mutex;
|
||||
Lock xx(mutex);
|
||||
|
||||
if(pvDataCreate.get()==0) pvDataCreate = PVDataCreatePtr(new PVDataCreate());
|
||||
return pvDataCreate;
|
||||
try {
|
||||
pvfield_factory_s = new detail::pvfield_factory;
|
||||
}catch(std::exception& e){
|
||||
std::cerr<<"Error initializing getFieldCreate() : "<<e.what()<<"\n";
|
||||
}
|
||||
}
|
||||
|
||||
PVDataCreatePtr getPVDataCreate() {
|
||||
return PVDataCreate::getPVDataCreate();
|
||||
const PVDataCreatePtr& PVDataCreate::getPVDataCreate()
|
||||
{
|
||||
epicsThreadOnce(&pvfield_factory_once, &pvfield_factory_init, 0);
|
||||
if(!pvfield_factory_s->pvDataCreate)
|
||||
throw std::logic_error("getPVDataCreate() not initialized");
|
||||
return pvfield_factory_s->pvDataCreate;
|
||||
}
|
||||
|
||||
// explicitly instanciate to ensure that windows
|
||||
// builds emit exported symbols for inline'd methods
|
||||
template class PVScalarValue<boolean>;
|
||||
template class PVScalarValue<int8>;
|
||||
template class PVScalarValue<uint8>;
|
||||
template class PVScalarValue<int16>;
|
||||
template class PVScalarValue<uint16>;
|
||||
template class PVScalarValue<int32>;
|
||||
template class PVScalarValue<uint32>;
|
||||
template class PVScalarValue<int64>;
|
||||
template class PVScalarValue<uint64>;
|
||||
template class PVScalarValue<float>;
|
||||
template class PVScalarValue<double>;
|
||||
template class PVScalarValue<std::string>;
|
||||
|
||||
}} // namespace epics::pvData
|
||||
|
||||
namespace std{
|
||||
std::ostream& operator<<(std::ostream& o, const epics::pvData::PVField *ptr)
|
||||
{
|
||||
if(ptr) return o << *ptr;
|
||||
return o << "nullptr";
|
||||
}
|
||||
}
|
||||
|
||||
}}
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
/*PVField.cpp*/
|
||||
/**
|
||||
* Copyright - See the COPYRIGHT that is included with this distribution.
|
||||
* EPICS pvData is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
/*
|
||||
* Copyright information and license terms for this software can be
|
||||
* found in the file LICENSE that is included with the distribution
|
||||
*/
|
||||
/**
|
||||
* @author mrk
|
||||
@@ -12,11 +11,13 @@
|
||||
#include <string>
|
||||
#include <cstdio>
|
||||
|
||||
#include <epicsMutex.h>
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include <pv/lock.h>
|
||||
#include <pv/pvData.h>
|
||||
#include <pv/factory.h>
|
||||
#include <pv/convert.h>
|
||||
#include <pv/reftrack.h>
|
||||
|
||||
using std::tr1::const_pointer_cast;
|
||||
using std::size_t;
|
||||
@@ -24,16 +25,20 @@ using std::string;
|
||||
|
||||
namespace epics { namespace pvData {
|
||||
|
||||
size_t PVField::num_instances;
|
||||
|
||||
PVField::PVField(FieldConstPtr field)
|
||||
: notImplemented("not implemented"),
|
||||
parent(NULL),field(field),
|
||||
: parent(NULL),field(field),
|
||||
fieldOffset(0), nextFieldOffset(0),
|
||||
immutable(false)
|
||||
{
|
||||
REFTRACE_INCREMENT(num_instances);
|
||||
}
|
||||
|
||||
PVField::~PVField()
|
||||
{ }
|
||||
{
|
||||
REFTRACE_DECREMENT(num_instances);
|
||||
}
|
||||
|
||||
|
||||
size_t PVField::getFieldOffset() const
|
||||
@@ -55,22 +60,16 @@ size_t PVField::getNumberFields() const
|
||||
}
|
||||
|
||||
|
||||
bool PVField::isImmutable() const {return immutable;}
|
||||
|
||||
void PVField::setImmutable() {immutable = true;}
|
||||
|
||||
const FieldConstPtr & PVField::getField() const {return field;}
|
||||
|
||||
PVStructure *PVField::getParent() const {return parent;}
|
||||
|
||||
void PVField::postPut()
|
||||
{
|
||||
if(postHandler.get()!=NULL) postHandler->postPut();
|
||||
if(postHandler) postHandler->postPut();
|
||||
}
|
||||
|
||||
void PVField::setPostHandler(PostHandlerPtr const &handler)
|
||||
{
|
||||
if(postHandler.get()!=NULL) {
|
||||
if(postHandler) {
|
||||
if(postHandler.get()==handler.get()) return;
|
||||
throw std::logic_error(
|
||||
"PVField::setPostHandler a postHandler is already registered");
|
||||
@@ -98,7 +97,7 @@ std::ostream& operator<<(std::ostream& o, const PVField& f)
|
||||
string PVField::getFullName() const
|
||||
{
|
||||
string ret(fieldName);
|
||||
for(PVField *fld=getParent(); fld; fld=fld->getParent())
|
||||
for(const PVField *fld=getParent(); fld; fld=fld->getParent())
|
||||
{
|
||||
if(fld->getFieldName().size()==0) break;
|
||||
ret = fld->getFieldName() + '.' + ret;
|
||||
@@ -121,7 +120,7 @@ void PVField::computeOffset(const PVField * pvField) {
|
||||
}
|
||||
size_t offset = 0;
|
||||
size_t nextOffset = 1;
|
||||
PVFieldPtrArray pvFields = pvTop->getPVFields();
|
||||
const PVFieldPtrArray & pvFields = pvTop->getPVFields();
|
||||
for(size_t i=0; i < pvTop->getStructure()->getNumberFields(); i++) {
|
||||
offset = nextOffset;
|
||||
PVField *pvField = pvFields[i].get();
|
||||
@@ -153,7 +152,7 @@ void PVField::computeOffset(const PVField * pvField,size_t offset) {
|
||||
size_t beginOffset = offset;
|
||||
size_t nextOffset = offset + 1;
|
||||
const PVStructure *pvStructure = static_cast<const PVStructure *>(pvField);
|
||||
const PVFieldPtrArray pvFields = pvStructure->getPVFields();
|
||||
const PVFieldPtrArray & pvFields = pvStructure->getPVFields();
|
||||
for(size_t i=0; i < pvStructure->getStructure()->getNumberFields(); i++) {
|
||||
offset = nextOffset;
|
||||
PVField *pvSubField = pvFields[i].get();
|
||||
@@ -180,4 +179,71 @@ void PVField::computeOffset(const PVField * pvField,size_t offset) {
|
||||
xxx->nextFieldOffset = nextOffset;
|
||||
}
|
||||
|
||||
void PVField::copy(const PVField& from)
|
||||
{
|
||||
if(isImmutable())
|
||||
throw std::invalid_argument("destination is immutable");
|
||||
|
||||
if (getField() != from.getField())
|
||||
throw std::invalid_argument("field types do not match");
|
||||
|
||||
copyUnchecked(from);
|
||||
}
|
||||
|
||||
void PVField::copyUnchecked(const PVField& from)
|
||||
{
|
||||
assert(getField()==from.getField());
|
||||
|
||||
switch(getField()->getType())
|
||||
{
|
||||
case scalar:
|
||||
{
|
||||
const PVScalar* fromS = static_cast<const PVScalar*>(&from);
|
||||
PVScalar* toS = static_cast<PVScalar*>(this);
|
||||
toS->copyUnchecked(*fromS);
|
||||
break;
|
||||
}
|
||||
case scalarArray:
|
||||
{
|
||||
const PVScalarArray* fromS = static_cast<const PVScalarArray*>(&from);
|
||||
PVScalarArray* toS = static_cast<PVScalarArray*>(this);
|
||||
toS->copyUnchecked(*fromS);
|
||||
break;
|
||||
}
|
||||
case structure:
|
||||
{
|
||||
const PVStructure* fromS = static_cast<const PVStructure*>(&from);
|
||||
PVStructure* toS = static_cast<PVStructure*>(this);
|
||||
toS->copyUnchecked(*fromS);
|
||||
break;
|
||||
}
|
||||
case structureArray:
|
||||
{
|
||||
const PVStructureArray* fromS = static_cast<const PVStructureArray*>(&from);
|
||||
PVStructureArray* toS = static_cast<PVStructureArray*>(this);
|
||||
toS->copyUnchecked(*fromS);
|
||||
break;
|
||||
}
|
||||
case union_:
|
||||
{
|
||||
const PVUnion* fromS = static_cast<const PVUnion*>(&from);
|
||||
PVUnion* toS = static_cast<PVUnion*>(this);
|
||||
toS->copyUnchecked(*fromS);
|
||||
break;
|
||||
}
|
||||
case unionArray:
|
||||
{
|
||||
const PVUnionArray* fromS = static_cast<const PVUnionArray*>(&from);
|
||||
PVUnionArray* toS = static_cast<PVUnionArray*>(this);
|
||||
toS->copyUnchecked(*fromS);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
throw std::logic_error("PVField::copy unknown type");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}}
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
/*PVScalar.cpp*/
|
||||
/**
|
||||
* Copyright - See the COPYRIGHT that is included with this distribution.
|
||||
* EPICS pvData is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
/*
|
||||
* Copyright information and license terms for this software can be
|
||||
* found in the file LICENSE that is included with the distribution
|
||||
*/
|
||||
/**
|
||||
* @author mrk
|
||||
@@ -29,22 +28,4 @@ namespace epics { namespace pvData {
|
||||
{
|
||||
return static_pointer_cast<const Scalar>(PVField::getField());
|
||||
}
|
||||
|
||||
template<>
|
||||
std::ostream& PVScalarValue<int8>::dumpValue(std::ostream& o) const
|
||||
{
|
||||
return o << static_cast<int>(get());
|
||||
}
|
||||
|
||||
template<>
|
||||
std::ostream& PVScalarValue<uint8>::dumpValue(std::ostream& o) const
|
||||
{
|
||||
return o << static_cast<unsigned int>(get());
|
||||
}
|
||||
|
||||
template<>
|
||||
std::ostream& PVScalarValue<boolean>::dumpValue(std::ostream& o) const
|
||||
{
|
||||
return o << std::boolalpha << static_cast<bool>(get());
|
||||
}
|
||||
}}
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
/*PVScalarArray.cpp*/
|
||||
/**
|
||||
* Copyright - See the COPYRIGHT that is included with this distribution.
|
||||
* EPICS pvData is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
/*
|
||||
* Copyright information and license terms for this software can be
|
||||
* found in the file LICENSE that is included with the distribution
|
||||
*/
|
||||
/**
|
||||
* @author mrk
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
/*PVStructure.cpp*/
|
||||
/**
|
||||
* Copyright - See the COPYRIGHT that is included with this distribution.
|
||||
* EPICS pvData is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
/*
|
||||
* Copyright information and license terms for this software can be
|
||||
* found in the file LICENSE that is included with the distribution
|
||||
*/
|
||||
/**
|
||||
* @author mrk
|
||||
@@ -16,7 +15,6 @@
|
||||
#define epicsExportSharedSymbols
|
||||
#include <pv/pvData.h>
|
||||
#include <pv/pvIntrospect.h>
|
||||
#include <pv/convert.h>
|
||||
#include <pv/factory.h>
|
||||
#include <pv/bitSet.h>
|
||||
|
||||
@@ -26,38 +24,14 @@ using std::string;
|
||||
|
||||
namespace epics { namespace pvData {
|
||||
|
||||
PVFieldPtr PVStructure::nullPVField;
|
||||
PVBooleanPtr PVStructure::nullPVBoolean;
|
||||
PVBytePtr PVStructure::nullPVByte;
|
||||
PVShortPtr PVStructure::nullPVShort;
|
||||
PVIntPtr PVStructure::nullPVInt;
|
||||
PVLongPtr PVStructure::nullPVLong;
|
||||
PVUBytePtr PVStructure::nullPVUByte;
|
||||
PVUShortPtr PVStructure::nullPVUShort;
|
||||
PVUIntPtr PVStructure::nullPVUInt;
|
||||
PVULongPtr PVStructure::nullPVULong;
|
||||
PVFloatPtr PVStructure::nullPVFloat;
|
||||
PVDoublePtr PVStructure::nullPVDouble;
|
||||
PVStringPtr PVStructure::nullPVString;
|
||||
PVStructurePtr PVStructure::nullPVStructure;
|
||||
PVStructureArrayPtr PVStructure::nullPVStructureArray;
|
||||
PVUnionPtr PVStructure::nullPVUnion;
|
||||
PVUnionArrayPtr PVStructure::nullPVUnionArray;
|
||||
PVScalarArrayPtr PVStructure::nullPVScalarArray;
|
||||
|
||||
static PVFieldPtr findSubField(
|
||||
string const &fieldName,
|
||||
const PVStructure *pvStructure);
|
||||
|
||||
PVStructure::PVStructure(StructureConstPtr const & structurePtr)
|
||||
: PVField(structurePtr),
|
||||
structurePtr(structurePtr),
|
||||
extendsStructureName("")
|
||||
{
|
||||
size_t numberFields = structurePtr->getNumberFields();
|
||||
FieldConstPtrArray fields = structurePtr->getFields();
|
||||
StringArray fieldNames = structurePtr->getFieldNames();
|
||||
// PVFieldPtrArray * xxx = const_cast<PVFieldPtrArray *>(&pvFields);
|
||||
FieldConstPtrArray const & fields = structurePtr->getFields();
|
||||
StringArray const & fieldNames = structurePtr->getFieldNames();
|
||||
pvFields.reserve(numberFields);
|
||||
PVDataCreatePtr pvDataCreate = getPVDataCreate();
|
||||
for(size_t i=0; i<numberFields; i++) {
|
||||
@@ -76,7 +50,7 @@ PVStructure::PVStructure(StructureConstPtr const & structurePtr,
|
||||
extendsStructureName("")
|
||||
{
|
||||
size_t numberFields = structurePtr->getNumberFields();
|
||||
StringArray fieldNames = structurePtr->getFieldNames();
|
||||
StringArray const & fieldNames = structurePtr->getFieldNames();
|
||||
pvFields.reserve(numberFields);
|
||||
for(size_t i=0; i<numberFields; i++) {
|
||||
pvFields.push_back(pvs[i]);
|
||||
@@ -86,9 +60,7 @@ PVStructure::PVStructure(StructureConstPtr const & structurePtr,
|
||||
}
|
||||
}
|
||||
|
||||
PVStructure::~PVStructure()
|
||||
{
|
||||
}
|
||||
PVStructure::~PVStructure() {}
|
||||
|
||||
void PVStructure::setImmutable()
|
||||
{
|
||||
@@ -100,145 +72,149 @@ void PVStructure::setImmutable()
|
||||
PVField::setImmutable();
|
||||
}
|
||||
|
||||
StructureConstPtr PVStructure::getStructure() const
|
||||
PVFieldPtr PVStructure::getSubFieldImpl(size_t fieldOffset, bool throws) const
|
||||
{
|
||||
return structurePtr;
|
||||
}
|
||||
const PVStructure *current = this;
|
||||
|
||||
const PVFieldPtrArray & PVStructure::getPVFields() const
|
||||
{
|
||||
return pvFields;
|
||||
}
|
||||
|
||||
PVFieldPtr PVStructure::getSubField(string const &fieldName) const
|
||||
{
|
||||
return findSubField(fieldName,this);
|
||||
}
|
||||
|
||||
|
||||
PVFieldPtr PVStructure::getSubField(size_t fieldOffset) const
|
||||
{
|
||||
if(fieldOffset<=getFieldOffset()) {
|
||||
return nullPVField;
|
||||
}
|
||||
if(fieldOffset>getNextFieldOffset()) return nullPVField;
|
||||
size_t numFields = pvFields.size();
|
||||
for(size_t i=0; i<numFields; i++) {
|
||||
PVFieldPtr pvField = pvFields[i];
|
||||
if(pvField->getFieldOffset()==fieldOffset) return pvFields[i];
|
||||
if(pvField->getNextFieldOffset()<=fieldOffset) continue;
|
||||
if(pvField->getField()->getType()==structure) {
|
||||
PVStructure *pvStructure = static_cast<PVStructure *>(pvField.get());
|
||||
return pvStructure->getSubField(fieldOffset);
|
||||
recurse:
|
||||
// we don't permit self lookup
|
||||
if(fieldOffset<=current->getFieldOffset() || fieldOffset>=current->getNextFieldOffset()) {
|
||||
if(throws) {
|
||||
std::stringstream ss;
|
||||
ss << "Failed to get field with offset "
|
||||
<< fieldOffset << " (Invalid offset)" ;
|
||||
throw std::runtime_error(ss.str());
|
||||
} else {
|
||||
return PVFieldPtr();
|
||||
}
|
||||
}
|
||||
|
||||
for(size_t i=0, numFields = current->pvFields.size(); i<numFields; i++) {
|
||||
const PVFieldPtr& pvField = current->pvFields[i];
|
||||
|
||||
if(pvField->getFieldOffset()==fieldOffset) {
|
||||
return pvField;
|
||||
|
||||
} else if(pvField->getNextFieldOffset()<=fieldOffset) {
|
||||
continue;
|
||||
|
||||
} else if(pvField->getField()->getType()==structure) {
|
||||
current = static_cast<PVStructure *>(pvField.get());
|
||||
goto recurse;
|
||||
}
|
||||
}
|
||||
// the first test against current->getNextFieldOffset() would avoid this
|
||||
throw std::logic_error("PVStructure.getSubField: Logic error");
|
||||
}
|
||||
|
||||
|
||||
PVBooleanPtr PVStructure::getBooleanField(string const &fieldName)
|
||||
PVFieldPtr PVStructure::getSubFieldImpl(const char *name, bool throws) const
|
||||
{
|
||||
return getSubField<PVBoolean>(fieldName);
|
||||
}
|
||||
|
||||
PVBytePtr PVStructure::getByteField(string const &fieldName)
|
||||
{
|
||||
return getSubField<PVByte>(fieldName);
|
||||
}
|
||||
|
||||
PVShortPtr PVStructure::getShortField(string const &fieldName)
|
||||
{
|
||||
return getSubField<PVShort>(fieldName);
|
||||
}
|
||||
|
||||
PVIntPtr PVStructure::getIntField(string const &fieldName)
|
||||
{
|
||||
return getSubField<PVInt>(fieldName);
|
||||
}
|
||||
|
||||
PVLongPtr PVStructure::getLongField(string const &fieldName)
|
||||
{
|
||||
return getSubField<PVLong>(fieldName);
|
||||
}
|
||||
|
||||
PVUBytePtr PVStructure::getUByteField(string const &fieldName)
|
||||
{
|
||||
return getSubField<PVUByte>(fieldName);
|
||||
}
|
||||
|
||||
PVUShortPtr PVStructure::getUShortField(string const &fieldName)
|
||||
{
|
||||
return getSubField<PVUShort>(fieldName);
|
||||
}
|
||||
|
||||
PVUIntPtr PVStructure::getUIntField(string const &fieldName)
|
||||
{
|
||||
return getSubField<PVUInt>(fieldName);
|
||||
}
|
||||
|
||||
PVULongPtr PVStructure::getULongField(string const &fieldName)
|
||||
{
|
||||
return getSubField<PVULong>(fieldName);
|
||||
}
|
||||
|
||||
PVFloatPtr PVStructure::getFloatField(string const &fieldName)
|
||||
{
|
||||
return getSubField<PVFloat>(fieldName);
|
||||
}
|
||||
|
||||
PVDoublePtr PVStructure::getDoubleField(string const &fieldName)
|
||||
{
|
||||
return getSubField<PVDouble>(fieldName);
|
||||
}
|
||||
|
||||
PVStringPtr PVStructure::getStringField(string const &fieldName)
|
||||
{
|
||||
return getSubField<PVString>(fieldName);
|
||||
}
|
||||
|
||||
PVStructurePtr PVStructure::getStructureField(string const &fieldName)
|
||||
{
|
||||
return getSubField<PVStructure>(fieldName);
|
||||
}
|
||||
|
||||
PVUnionPtr PVStructure::getUnionField(string const &fieldName)
|
||||
{
|
||||
return getSubField<PVUnion>(fieldName);
|
||||
}
|
||||
|
||||
PVScalarArrayPtr PVStructure::getScalarArrayField(
|
||||
string const &fieldName,ScalarType elementType)
|
||||
{
|
||||
PVFieldPtr pvField = findSubField(fieldName,this);
|
||||
if(pvField.get()==NULL) {
|
||||
return nullPVScalarArray;
|
||||
const PVStructure *parent = this;
|
||||
if(!name)
|
||||
{
|
||||
if (throws)
|
||||
throw std::invalid_argument("Failed to get field: (Field name is NULL string)");
|
||||
else
|
||||
return PVFieldPtr();
|
||||
}
|
||||
FieldConstPtr field = pvField->getField();
|
||||
Type type = field->getType();
|
||||
if(type!=scalarArray) {
|
||||
return nullPVScalarArray;
|
||||
const char *fullName = name;
|
||||
while(true) {
|
||||
const char *sep=name;
|
||||
while(*sep!='\0' && *sep!='.' && *sep!=' ') sep++;
|
||||
if(*sep==' ')
|
||||
{
|
||||
if (throws)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "Failed to get field: " << fullName
|
||||
<< " (No spaces allowed in field name)";
|
||||
throw std::runtime_error(ss.str());
|
||||
}
|
||||
else
|
||||
return PVFieldPtr();
|
||||
}
|
||||
size_t N = sep-name;
|
||||
if(N==0)
|
||||
{
|
||||
if (throws)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "Failed to get field: " << fullName
|
||||
<< " (Zero-length field name encountered)";
|
||||
throw std::runtime_error(ss.str());
|
||||
}
|
||||
else
|
||||
return PVFieldPtr();
|
||||
}
|
||||
|
||||
const PVFieldPtrArray& pvFields = parent->getPVFields();
|
||||
|
||||
PVField *child = NULL;
|
||||
|
||||
for(size_t i=0, n=pvFields.size(); i!=n; i++) {
|
||||
const PVFieldPtr& fld = pvFields[i];
|
||||
const std::string& fname = fld->getFieldName();
|
||||
|
||||
if(fname.size()==N && memcmp(name, fname.c_str(), N)==0) {
|
||||
child = fld.get();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(!child)
|
||||
{
|
||||
if (throws)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "Failed to get field: " << fullName << " ("
|
||||
<< std::string(fullName, sep) << " not found)";
|
||||
throw std::runtime_error(ss.str());
|
||||
}
|
||||
else
|
||||
return PVFieldPtr();
|
||||
}
|
||||
|
||||
if(*sep) {
|
||||
// this is not the requested leaf
|
||||
parent = dynamic_cast<PVStructure*>(child);
|
||||
if(!parent)
|
||||
{
|
||||
if (throws)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "Failed to get field: " << fullName
|
||||
<< " (" << std::string(fullName, sep)
|
||||
<< " is not a structure)";
|
||||
throw std::runtime_error(ss.str());
|
||||
}
|
||||
else
|
||||
return PVFieldPtr();
|
||||
}
|
||||
child = NULL;
|
||||
name = sep+1; // skip past '.'
|
||||
// loop around to new parent
|
||||
|
||||
} else {
|
||||
return child->shared_from_this();
|
||||
}
|
||||
}
|
||||
ScalarArrayConstPtr pscalarArray
|
||||
= static_pointer_cast<const ScalarArray>(pvField->getField());
|
||||
if(pscalarArray->getElementType()!=elementType) {
|
||||
return nullPVScalarArray;
|
||||
}
|
||||
return std::tr1::static_pointer_cast<PVScalarArray>(pvField);
|
||||
}
|
||||
|
||||
PVStructureArrayPtr PVStructure::getStructureArrayField(
|
||||
string const &fieldName)
|
||||
void PVStructure::throwBadFieldType(const char *name)
|
||||
{
|
||||
return getSubField<PVStructureArray>(fieldName);
|
||||
std::ostringstream ss;
|
||||
ss << "Failed to get field: " << name << " (Field has wrong type)";
|
||||
throw std::runtime_error(ss.str());
|
||||
}
|
||||
|
||||
PVUnionArrayPtr PVStructure::getUnionArrayField(
|
||||
string const &fieldName)
|
||||
void PVStructure::throwBadFieldType(std::size_t fieldOffset)
|
||||
{
|
||||
return getSubField<PVUnionArray>(fieldName);
|
||||
std::stringstream ss;
|
||||
ss << "Failed to get field with offset "
|
||||
<< fieldOffset << " (Field has wrong type)";
|
||||
throw std::runtime_error(ss.str());
|
||||
}
|
||||
|
||||
|
||||
void PVStructure::serialize(ByteBuffer *pbuffer,
|
||||
SerializableControl *pflusher) const {
|
||||
size_t fieldsSize = pvFields.size();
|
||||
@@ -256,9 +232,8 @@ void PVStructure::deserialize(ByteBuffer *pbuffer,
|
||||
|
||||
void PVStructure::serialize(ByteBuffer *pbuffer,
|
||||
SerializableControl *pflusher, BitSet *pbitSet) const {
|
||||
PVStructure* nonConstThis = const_cast<PVStructure*>(this);
|
||||
size_t numberFields = nonConstThis->getNumberFields();
|
||||
size_t offset = nonConstThis->getFieldOffset();
|
||||
size_t numberFields = this->getNumberFields();
|
||||
size_t offset = this->getFieldOffset();
|
||||
int32 next = pbitSet->nextSetBit(static_cast<uint32>(offset));
|
||||
|
||||
// no more changes or no changes in this structure
|
||||
@@ -272,7 +247,7 @@ void PVStructure::serialize(ByteBuffer *pbuffer,
|
||||
|
||||
size_t fieldsSize = pvFields.size();
|
||||
for(size_t i = 0; i<fieldsSize; i++) {
|
||||
PVFieldPtr pvField = pvFields[i];
|
||||
PVField* pvField = pvFields[i].get();
|
||||
offset = pvField->getFieldOffset();
|
||||
int32 inumberFields = static_cast<int32>(pvField->getNumberFields());
|
||||
next = pbitSet->nextSetBit(static_cast<uint32>(offset));
|
||||
@@ -286,8 +261,7 @@ void PVStructure::serialize(ByteBuffer *pbuffer,
|
||||
if(inumberFields==1) {
|
||||
pvField->serialize(pbuffer, pflusher);
|
||||
} else {
|
||||
PVStructurePtr pvStructure = std::tr1::static_pointer_cast<PVStructure>(pvField);
|
||||
pvStructure->serialize(pbuffer, pflusher, pbitSet);
|
||||
static_cast<PVStructure*>(pvField)->serialize(pbuffer, pflusher, pbitSet);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -328,37 +302,6 @@ void PVStructure::deserialize(ByteBuffer *pbuffer,
|
||||
}
|
||||
}
|
||||
|
||||
static PVFieldPtr findSubField(
|
||||
string const & fieldName,
|
||||
PVStructure const *pvStructure)
|
||||
{
|
||||
if( fieldName.length()<1) return PVFieldPtr();
|
||||
string::size_type index = fieldName.find('.');
|
||||
string name = fieldName;
|
||||
string restOfName = string();
|
||||
if(index>0) {
|
||||
name = fieldName.substr(0, index);
|
||||
if(fieldName.length()>index) {
|
||||
restOfName = fieldName.substr(index+1);
|
||||
}
|
||||
}
|
||||
PVFieldPtrArray pvFields = pvStructure->getPVFields();
|
||||
PVFieldPtr pvField;
|
||||
size_t numFields = pvStructure->getStructure()->getNumberFields();
|
||||
for(size_t i=0; i<numFields; i++) {
|
||||
pvField = pvFields[i];
|
||||
size_t result = pvField->getFieldName().compare(name);
|
||||
if(result==0) {
|
||||
if(restOfName.length()==0) return pvFields[i];
|
||||
if(pvField->getField()->getType()!=structure) return PVFieldPtr();
|
||||
PVStructurePtr pvStructure =
|
||||
std::tr1::static_pointer_cast<PVStructure>(pvField);
|
||||
return findSubField(restOfName,pvStructure.get());
|
||||
}
|
||||
}
|
||||
return PVFieldPtr();
|
||||
}
|
||||
|
||||
std::ostream& PVStructure::dumpValue(std::ostream& o) const
|
||||
{
|
||||
o << format::indent() << getStructure()->getID() << ' ' << getFieldName();
|
||||
@@ -382,4 +325,79 @@ std::ostream& PVStructure::dumpValue(std::ostream& o) const
|
||||
return o;
|
||||
}
|
||||
|
||||
|
||||
void PVStructure::copy(const PVStructure& from)
|
||||
{
|
||||
if(isImmutable())
|
||||
throw std::invalid_argument("destination is immutable");
|
||||
|
||||
if(*getStructure() != *from.getStructure())
|
||||
throw std::invalid_argument("structure definitions do not match");
|
||||
|
||||
copyUnchecked(from);
|
||||
}
|
||||
|
||||
void PVStructure::copyUnchecked(const PVStructure& from)
|
||||
{
|
||||
if (this == &from)
|
||||
return;
|
||||
|
||||
PVFieldPtrArray const & fromPVFields = from.getPVFields();
|
||||
PVFieldPtrArray const & toPVFields = getPVFields();
|
||||
|
||||
size_t fieldsSize = fromPVFields.size();
|
||||
for(size_t i = 0; i<fieldsSize; i++) {
|
||||
toPVFields[i]->copyUnchecked(*fromPVFields[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void PVStructure::copyUnchecked(const PVStructure& from, const BitSet& maskBitSet, bool inverse)
|
||||
{
|
||||
if (this == &from)
|
||||
return;
|
||||
|
||||
size_t numberFields = from.getNumberFields();
|
||||
size_t offset = from.getFieldOffset();
|
||||
int32 next = inverse ?
|
||||
maskBitSet.nextClearBit(static_cast<uint32>(offset)) :
|
||||
maskBitSet.nextSetBit(static_cast<uint32>(offset));
|
||||
|
||||
// no more changes or no changes in this structure
|
||||
if(next<0||next>=static_cast<int32>(offset+numberFields)) return;
|
||||
|
||||
// entire structure
|
||||
if(static_cast<int32>(offset)==next) {
|
||||
copyUnchecked(from);
|
||||
return;
|
||||
}
|
||||
|
||||
PVFieldPtrArray const & fromPVFields = from.getPVFields();
|
||||
PVFieldPtrArray const & toPVFields = getPVFields();
|
||||
|
||||
size_t fieldsSize = fromPVFields.size();
|
||||
for(size_t i = 0; i<fieldsSize; i++) {
|
||||
PVFieldPtr pvField = fromPVFields[i];
|
||||
offset = pvField->getFieldOffset();
|
||||
int32 inumberFields = static_cast<int32>(pvField->getNumberFields());
|
||||
next = inverse ?
|
||||
maskBitSet.nextClearBit(static_cast<uint32>(offset)) :
|
||||
maskBitSet.nextSetBit(static_cast<uint32>(offset));
|
||||
|
||||
// no more changes
|
||||
if(next<0) return;
|
||||
// no change in this pvField
|
||||
if(next>=static_cast<int32>(offset+inumberFields)) continue;
|
||||
|
||||
// serialize field or fields
|
||||
if(inumberFields==1) {
|
||||
toPVFields[i]->copyUnchecked(*pvField);
|
||||
} else {
|
||||
PVStructure::shared_pointer fromPVStructure = std::tr1::static_pointer_cast<PVStructure>(pvField);
|
||||
PVStructure::shared_pointer toPVStructure = std::tr1::static_pointer_cast<PVStructure>(toPVFields[i]);
|
||||
toPVStructure->copyUnchecked(*fromPVStructure, maskBitSet, inverse);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}}
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
/*PVStructureArray.cpp*/
|
||||
/**
|
||||
* Copyright - See the COPYRIGHT that is included with this distribution.
|
||||
* EPICS pvData is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
/*
|
||||
* Copyright information and license terms for this software can be
|
||||
* found in the file LICENSE that is included with the distribution
|
||||
*/
|
||||
/**
|
||||
* @author mrk
|
||||
@@ -14,7 +13,6 @@
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include <pv/pvData.h>
|
||||
#include <pv/convert.h>
|
||||
#include <pv/factory.h>
|
||||
#include <pv/serializeHelper.h>
|
||||
|
||||
@@ -78,14 +76,14 @@ void PVStructureArray::compress() {
|
||||
size_t newLength = 0;
|
||||
|
||||
for(size_t i=0; i<length; i++) {
|
||||
if(vec[i].get()!=NULL) {
|
||||
if(vec[i]) {
|
||||
newLength++;
|
||||
continue;
|
||||
}
|
||||
// find first non 0
|
||||
size_t notNull = 0;
|
||||
for(size_t j=i+1;j<length;j++) {
|
||||
if(vec[j].get()!=NULL) {
|
||||
if(vec[j]) {
|
||||
notNull = j;
|
||||
break;
|
||||
}
|
||||
@@ -179,7 +177,7 @@ void PVStructureArray::deserialize(ByteBuffer *pbuffer,
|
||||
data[i].reset();
|
||||
}
|
||||
else {
|
||||
if(data[i].get()==NULL) {
|
||||
if(data[i].get()==NULL || !data[i].unique()) {
|
||||
data[i] = pvDataCreate->createPVStructure(structure);
|
||||
}
|
||||
data[i]->deserialize(pbuffer, pcontrol);
|
||||
@@ -242,4 +240,23 @@ std::ostream& PVStructureArray::dumpValue(std::ostream& o, std::size_t index) co
|
||||
return o;
|
||||
}
|
||||
|
||||
void PVStructureArray::copy(const PVStructureArray& from)
|
||||
{
|
||||
if(isImmutable())
|
||||
throw std::invalid_argument("destination is immutable");
|
||||
|
||||
if(*getStructureArray() != *from.getStructureArray())
|
||||
throw std::invalid_argument("structureArray definitions do not match");
|
||||
|
||||
copyUnchecked(from);
|
||||
}
|
||||
|
||||
void PVStructureArray::copyUnchecked(const PVStructureArray& from)
|
||||
{
|
||||
if (this == &from)
|
||||
return;
|
||||
|
||||
replace(from.view());
|
||||
}
|
||||
|
||||
}}
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
/*PVUnion.cpp*/
|
||||
/**
|
||||
* Copyright - See the COPYRIGHT that is included with this distribution.
|
||||
* EPICS pvData is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
/*
|
||||
* Copyright information and license terms for this software can be
|
||||
* found in the file LICENSE that is included with the distribution
|
||||
*/
|
||||
/**
|
||||
* @author mse
|
||||
@@ -16,7 +15,6 @@
|
||||
#define epicsExportSharedSymbols
|
||||
#include <pv/pvData.h>
|
||||
#include <pv/pvIntrospect.h>
|
||||
#include <pv/convert.h>
|
||||
#include <pv/factory.h>
|
||||
#include <pv/serializeHelper.h>
|
||||
|
||||
@@ -27,7 +25,9 @@ using std::string;
|
||||
namespace epics { namespace pvData {
|
||||
|
||||
#define PVUNION_UNDEFINED_INDEX -1
|
||||
int32 PVUnion::UNDEFINED_INDEX = PVUNION_UNDEFINED_INDEX;
|
||||
const int32 PVUnion::UNDEFINED_INDEX = PVUNION_UNDEFINED_INDEX;
|
||||
|
||||
PVDataCreatePtr PVUnion::pvDataCreate(getPVDataCreate());
|
||||
|
||||
PVUnion::PVUnion(UnionConstPtr const & unionPtr)
|
||||
: PVField(unionPtr),
|
||||
@@ -40,24 +40,7 @@ PVUnion::PVUnion(UnionConstPtr const & unionPtr)
|
||||
|
||||
#undef PVUNION_UNDEFINED_INDEX
|
||||
|
||||
PVUnion::~PVUnion()
|
||||
{
|
||||
}
|
||||
|
||||
UnionConstPtr PVUnion::getUnion() const
|
||||
{
|
||||
return unionPtr;
|
||||
}
|
||||
|
||||
PVFieldPtr PVUnion::get() const
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
int32 PVUnion::getSelectedIndex() const
|
||||
{
|
||||
return selector;
|
||||
}
|
||||
PVUnion::~PVUnion() {}
|
||||
|
||||
string PVUnion::getSelectedFieldName() const
|
||||
{
|
||||
@@ -70,8 +53,11 @@ string PVUnion::getSelectedFieldName() const
|
||||
|
||||
PVFieldPtr PVUnion::select(int32 index)
|
||||
{
|
||||
if (variant && index != UNDEFINED_INDEX)
|
||||
throw std::invalid_argument("index out of bounds");
|
||||
|
||||
// no change
|
||||
if (selector == index)
|
||||
if (selector == index && !variant)
|
||||
return value;
|
||||
|
||||
if (index == UNDEFINED_INDEX)
|
||||
@@ -80,14 +66,12 @@ PVFieldPtr PVUnion::select(int32 index)
|
||||
value.reset();
|
||||
return value;
|
||||
}
|
||||
else if (variant)
|
||||
throw std::invalid_argument("index out of bounds");
|
||||
else if (index < 0 || index > static_cast<int32>(unionPtr->getFields().size()))
|
||||
else if (index < 0 || size_t(index) >= unionPtr->getFields().size())
|
||||
throw std::invalid_argument("index out of bounds");
|
||||
|
||||
FieldConstPtr field = unionPtr->getField(index);
|
||||
selector = index;
|
||||
value = getPVDataCreate()->createPVField(field);
|
||||
value = pvDataCreate->createPVField(field);
|
||||
|
||||
return value;
|
||||
}
|
||||
@@ -99,11 +83,6 @@ PVFieldPtr PVUnion::select(string const & fieldName)
|
||||
throw std::invalid_argument("no such fieldName");
|
||||
return select(index);
|
||||
}
|
||||
|
||||
void PVUnion::set(PVFieldPtr const & value)
|
||||
{
|
||||
set(selector, value);
|
||||
}
|
||||
|
||||
void PVUnion::set(int32 index, PVFieldPtr const & value)
|
||||
{
|
||||
@@ -114,14 +93,14 @@ void PVUnion::set(int32 index, PVFieldPtr const & value)
|
||||
if (index == UNDEFINED_INDEX)
|
||||
{
|
||||
// for undefined index we accept only null values
|
||||
if (value.get())
|
||||
if (value)
|
||||
throw std::invalid_argument("non-null value for index == UNDEFINED_INDEX");
|
||||
}
|
||||
else if (index < 0 || index > static_cast<int32>(unionPtr->getFields().size()))
|
||||
else if (index < 0 || size_t(index) >= unionPtr->getFields().size())
|
||||
throw std::invalid_argument("index out of bounds");
|
||||
|
||||
// value type must match
|
||||
if (value->getField() != unionPtr->getField(index))
|
||||
else if (!value)
|
||||
throw std::invalid_argument("Can't set defined index w/ NULL");
|
||||
else if (value->getField() != unionPtr->getField(index))
|
||||
throw std::invalid_argument("selected field and its introspection data do not match");
|
||||
}
|
||||
|
||||
@@ -136,7 +115,7 @@ void PVUnion::set(string const & fieldName, PVFieldPtr const & value)
|
||||
if (index == -1)
|
||||
throw std::invalid_argument("no such fieldName");
|
||||
|
||||
set(index, value);
|
||||
set(index, value);
|
||||
}
|
||||
|
||||
void PVUnion::serialize(ByteBuffer *pbuffer, SerializableControl *pflusher) const
|
||||
@@ -144,10 +123,10 @@ void PVUnion::serialize(ByteBuffer *pbuffer, SerializableControl *pflusher) cons
|
||||
if (variant)
|
||||
{
|
||||
// write introspection data
|
||||
if (value.get() == 0)
|
||||
if (value.get() == 0) {
|
||||
pflusher->ensureBuffer(1);
|
||||
pbuffer->put((int8)-1);
|
||||
else
|
||||
{
|
||||
}else {
|
||||
pflusher->cachedSerialize(value->getField(), pbuffer);
|
||||
value->serialize(pbuffer, pflusher);
|
||||
}
|
||||
@@ -171,7 +150,7 @@ void PVUnion::deserialize(ByteBuffer *pbuffer, DeserializableControl *pcontrol)
|
||||
{
|
||||
// try to reuse existing field instance
|
||||
if (!value.get() || *value->getField() != *field)
|
||||
value = getPVDataCreate()->createPVField(field);
|
||||
value = pvDataCreate->createPVField(field);
|
||||
value->deserialize(pbuffer, pcontrol);
|
||||
}
|
||||
else
|
||||
@@ -188,7 +167,7 @@ void PVUnion::deserialize(ByteBuffer *pbuffer, DeserializableControl *pcontrol)
|
||||
FieldConstPtr field = unionPtr->getField(selector);
|
||||
// try to reuse existing field instance
|
||||
if (!value.get() || *value->getField() != *field)
|
||||
value = getPVDataCreate()->createPVField(field);
|
||||
value = pvDataCreate->createPVField(field);
|
||||
}
|
||||
value->deserialize(pbuffer, pcontrol);
|
||||
}
|
||||
@@ -202,7 +181,7 @@ std::ostream& PVUnion::dumpValue(std::ostream& o) const
|
||||
o << format::indent() << getUnion()->getID() << ' ' << getFieldName() << std::endl;
|
||||
{
|
||||
format::indent_scope s(o);
|
||||
PVFieldPtr fieldField = get();
|
||||
const PVField::const_shared_pointer& fieldField = get();
|
||||
if (fieldField.get() == NULL)
|
||||
o << format::indent() << "(none)" << std::endl;
|
||||
else
|
||||
@@ -217,4 +196,57 @@ std::ostream& PVUnion::dumpValue(std::ostream& o) const
|
||||
return o;
|
||||
}
|
||||
|
||||
void PVUnion::copy(const PVUnion& from)
|
||||
{
|
||||
if(isImmutable())
|
||||
throw std::invalid_argument("destination is immutable");
|
||||
|
||||
if(*getUnion() != *from.getUnion())
|
||||
throw std::invalid_argument("union definitions do not match");
|
||||
|
||||
copyUnchecked(from);
|
||||
}
|
||||
|
||||
void PVUnion::copyUnchecked(const PVUnion& from)
|
||||
{
|
||||
|
||||
const PVField::const_shared_pointer& fromValue = from.get();
|
||||
if (from.getUnion()->isVariant())
|
||||
{
|
||||
if (fromValue.get() == 0)
|
||||
{
|
||||
set(PVField::shared_pointer());
|
||||
}
|
||||
else
|
||||
{
|
||||
PVFieldPtr toValue = get();
|
||||
if (toValue.get() == 0 || *toValue->getField() != *fromValue->getField())
|
||||
{
|
||||
toValue = pvDataCreate->createPVField(fromValue->getField());
|
||||
toValue->copyUnchecked(*fromValue);
|
||||
set(toValue);
|
||||
}
|
||||
else
|
||||
{
|
||||
toValue->copyUnchecked(*fromValue);
|
||||
postPut();
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (fromValue.get() == 0)
|
||||
{
|
||||
select(PVUnion::UNDEFINED_INDEX);
|
||||
}
|
||||
else
|
||||
{
|
||||
select(from.getSelectedIndex())->copyUnchecked(*fromValue);
|
||||
}
|
||||
postPut();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}}
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
/*PVUnionArray.cpp*/
|
||||
/**
|
||||
* Copyright - See the COPYRIGHT that is included with this distribution.
|
||||
* EPICS pvData is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
/*
|
||||
* Copyright information and license terms for this software can be
|
||||
* found in the file LICENSE that is included with the distribution
|
||||
*/
|
||||
/**
|
||||
* @author mrk
|
||||
@@ -14,7 +13,6 @@
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include <pv/pvData.h>
|
||||
#include <pv/convert.h>
|
||||
#include <pv/factory.h>
|
||||
#include <pv/serializeHelper.h>
|
||||
|
||||
@@ -78,14 +76,14 @@ void PVUnionArray::compress() {
|
||||
size_t newLength = 0;
|
||||
|
||||
for(size_t i=0; i<length; i++) {
|
||||
if(vec[i].get()!=NULL) {
|
||||
if(vec[i]) {
|
||||
newLength++;
|
||||
continue;
|
||||
}
|
||||
// find first non 0
|
||||
size_t notNull = 0;
|
||||
for(size_t j=i+1;j<length;j++) {
|
||||
if(vec[j].get()!=NULL) {
|
||||
if(vec[j]) {
|
||||
notNull = j;
|
||||
break;
|
||||
}
|
||||
@@ -178,7 +176,7 @@ void PVUnionArray::deserialize(ByteBuffer *pbuffer,
|
||||
data[i].reset();
|
||||
}
|
||||
else {
|
||||
if(data[i].get()==NULL) {
|
||||
if(data[i].get()==NULL || !data[i].unique()) {
|
||||
data[i] = pvDataCreate->createPVUnion(punion);
|
||||
}
|
||||
data[i]->deserialize(pbuffer, pcontrol);
|
||||
@@ -241,4 +239,24 @@ std::ostream& PVUnionArray::dumpValue(std::ostream& o, std::size_t index) const
|
||||
return o;
|
||||
}
|
||||
|
||||
void PVUnionArray::copy(const PVUnionArray& from)
|
||||
{
|
||||
if(isImmutable())
|
||||
throw std::invalid_argument("destination is immutable");
|
||||
|
||||
if(*getUnionArray() != *from.getUnionArray())
|
||||
throw std::invalid_argument("unionArray definitions do not match");
|
||||
|
||||
copyUnchecked(from);
|
||||
}
|
||||
|
||||
void PVUnionArray::copyUnchecked(const PVUnionArray& from)
|
||||
{
|
||||
if (this == &from)
|
||||
return;
|
||||
|
||||
replace(from.view());
|
||||
}
|
||||
|
||||
|
||||
}}
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
/* StandardField.cpp */
|
||||
/**
|
||||
* Copyright - See the COPYRIGHT that is included with this distribution.
|
||||
* EPICS pvData is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
/*
|
||||
* Copyright information and license terms for this software can be
|
||||
* found in the file LICENSE that is included with the distribution
|
||||
*/
|
||||
/**
|
||||
* @author mrk
|
||||
@@ -11,6 +10,8 @@
|
||||
#include <cstdio>
|
||||
#include <stdexcept>
|
||||
|
||||
#include <epicsMutex.h>
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include <pv/lock.h>
|
||||
#include <pv/pvIntrospect.h>
|
||||
@@ -89,9 +90,9 @@ StructureConstPtr StandardField::createProperties(string id,FieldConstPtr field,
|
||||
}
|
||||
if(type==structure) {
|
||||
StructureConstPtr structurePtr = static_pointer_cast<const Structure>(field);
|
||||
StringArray names = structurePtr->getFieldNames();
|
||||
StringArray const & names = structurePtr->getFieldNames();
|
||||
if(names.size()==2) {
|
||||
FieldConstPtrArray fields = structurePtr->getFields();
|
||||
FieldConstPtrArray const & fields = structurePtr->getFields();
|
||||
FieldConstPtr first = fields[0];
|
||||
FieldConstPtr second = fields[1];
|
||||
string nameFirst = names[0];
|
||||
@@ -554,7 +555,7 @@ StructureConstPtr StandardField::enumerated()
|
||||
fields[0] = fieldCreate->createScalar(pvInt);
|
||||
fields[1] = fieldCreate->createScalarArray(pvString);
|
||||
return fieldCreate->createStructure("enum_t",names,fields);
|
||||
// NOTE: if this method is used to get NTEnum wihtout properties the ID will be wrong!
|
||||
// NOTE: if this method is used to get NTEnum without properties the ID will be wrong!
|
||||
}
|
||||
|
||||
StructureConstPtr StandardField::enumerated(string const &properties)
|
||||
@@ -643,7 +644,7 @@ StructureConstPtr StandardField::enumeratedAlarm()
|
||||
return enumeratedAlarmField;
|
||||
}
|
||||
|
||||
StandardFieldPtr StandardField::getStandardField()
|
||||
const StandardFieldPtr &StandardField::getStandardField()
|
||||
{
|
||||
static StandardFieldPtr standardFieldCreate;
|
||||
static Mutex mutex;
|
||||
@@ -657,10 +658,4 @@ StandardFieldPtr StandardField::getStandardField()
|
||||
return standardFieldCreate;
|
||||
}
|
||||
|
||||
|
||||
|
||||
StandardFieldPtr getStandardField() {
|
||||
return StandardField::getStandardField();
|
||||
}
|
||||
|
||||
}}
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
/* StandardPVField.cpp */
|
||||
/**
|
||||
* Copyright - See the COPYRIGHT that is included with this distribution.
|
||||
* EPICS pvData is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
/*
|
||||
* Copyright information and license terms for this software can be
|
||||
* found in the file LICENSE that is included with the distribution
|
||||
*/
|
||||
/**
|
||||
* @author mrk
|
||||
@@ -10,11 +9,12 @@
|
||||
#include <string>
|
||||
#include <stdexcept>
|
||||
|
||||
#include <epicsMutex.h>
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include <pv/lock.h>
|
||||
#include <pv/pvIntrospect.h>
|
||||
#include <pv/pvData.h>
|
||||
#include <pv/convert.h>
|
||||
#include <pv/standardField.h>
|
||||
#include <pv/standardPVField.h>
|
||||
|
||||
@@ -67,11 +67,9 @@ PVStructurePtr StandardPVField::enumerated(StringArray const &choices)
|
||||
{
|
||||
StructureConstPtr field = standardField->enumerated();
|
||||
PVStructurePtr pvStructure = pvDataCreate->createPVStructure(field);
|
||||
PVScalarArrayPtr pvScalarArray = pvStructure->getScalarArrayField(
|
||||
"choices",pvString);
|
||||
PVStringArray::svector cdata(choices.size());
|
||||
std::copy(choices.begin(), choices.end(), cdata.begin());
|
||||
static_cast<PVStringArray&>(*pvScalarArray).replace(freeze(cdata));
|
||||
pvStructure->getSubFieldT<PVStringArray>("choices")->replace(freeze(cdata));
|
||||
return pvStructure;
|
||||
}
|
||||
|
||||
@@ -80,11 +78,9 @@ PVStructurePtr StandardPVField::enumerated(
|
||||
{
|
||||
StructureConstPtr field = standardField->enumerated(properties);
|
||||
PVStructurePtr pvStructure = pvDataCreate->createPVStructure(field);
|
||||
PVScalarArrayPtr pvScalarArray = pvStructure->getScalarArrayField(
|
||||
"value.choices",pvString);
|
||||
PVStringArray::svector cdata(choices.size());
|
||||
std::copy(choices.begin(), choices.end(), cdata.begin());
|
||||
static_cast<PVStringArray&>(*pvScalarArray).replace(freeze(cdata));
|
||||
pvStructure->getSubFieldT<PVStringArray>("value.choices")->replace(freeze(cdata));
|
||||
return pvStructure;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
/*TypeFunc.cpp*/
|
||||
/**
|
||||
* Copyright - See the COPYRIGHT that is included with this distribution.
|
||||
* EPICS pvData is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
/*
|
||||
* Copyright information and license terms for this software can be
|
||||
* found in the file LICENSE that is included with the distribution
|
||||
*/
|
||||
/**
|
||||
* @author mrk
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
/*factory.h*/
|
||||
/**
|
||||
* Copyright - See the COPYRIGHT that is included with this distribution.
|
||||
* EPICS pvData is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
*/
|
||||
/**
|
||||
* @author mrk
|
||||
*/
|
||||
#ifndef FACTORY_H
|
||||
#define FACTORY_H
|
||||
|
||||
namespace epics { namespace pvData {
|
||||
|
||||
enum DebugLevel{noDebug,lowDebug,highDebug};
|
||||
|
||||
}}
|
||||
#endif /*FACTORY_H */
|
||||
@@ -1,297 +1,523 @@
|
||||
/* printer.cpp */
|
||||
/**
|
||||
* Copyright - See the COPYRIGHT that is included with this distribution.
|
||||
* EPICS pvData is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
/*
|
||||
* Copyright information and license terms for this software can be
|
||||
* found in the file LICENSE that is included with the distribution
|
||||
*/
|
||||
|
||||
|
||||
#include <stdio.h>
|
||||
#include <ctype.h>
|
||||
#if defined(__linux__) || defined(__APPLE__)
|
||||
#include <unistd.h>
|
||||
#define HAVE_ISATTY
|
||||
#endif
|
||||
|
||||
#include <deque>
|
||||
|
||||
#include <epicsTime.h>
|
||||
#include <epicsString.h>
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include <pv/printer.h>
|
||||
|
||||
using std::string;
|
||||
|
||||
namespace {
|
||||
|
||||
void indentN(std::ostream& strm, size_t N)
|
||||
{
|
||||
while(N--)
|
||||
strm.put(' ');
|
||||
}
|
||||
|
||||
}
|
||||
#include <pv/bitSet.h>
|
||||
#include <pv/pvData.h>
|
||||
#if EPICS_VERSION_INT>=VERSION_INT(3,15,0,1)
|
||||
# include <pv/json.h>
|
||||
#endif
|
||||
|
||||
namespace epics { namespace pvData {
|
||||
|
||||
namespace format
|
||||
{
|
||||
static int indent_index = std::ios_base::xalloc();
|
||||
static int indent_index = std::ios_base::xalloc();
|
||||
|
||||
long& indent_value(std::ios_base& ios)
|
||||
{
|
||||
return ios.iword(indent_index);
|
||||
long& indent_value(std::ios_base& ios)
|
||||
{
|
||||
return ios.iword(indent_index);
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, indent_level const& indent)
|
||||
{
|
||||
indent_value(os) = indent.level;
|
||||
return os;
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, indent const&)
|
||||
{
|
||||
long il = indent_value(os);
|
||||
for(long i=0, spaces = il * 4; i<spaces; i++)
|
||||
os.put(' ');
|
||||
return os;
|
||||
}
|
||||
|
||||
array_at_internal operator<<(std::ostream& str, array_at const& manip)
|
||||
{
|
||||
return array_at_internal(manip.index, str);
|
||||
}
|
||||
}
|
||||
|
||||
}} //epics::pvData
|
||||
namespace {
|
||||
using namespace epics::pvData;
|
||||
|
||||
bool useEscapes(std::ostream& strm) {
|
||||
FILE *fp = 0;
|
||||
// TODO: a better way to do this...
|
||||
if(&std::cout==&strm)
|
||||
fp = stdout;
|
||||
if(&std::cerr==&strm)
|
||||
fp = stderr;
|
||||
if(!fp) return false;
|
||||
#ifdef HAVE_ISATTY
|
||||
// check $TERM as well?
|
||||
return isatty(fileno(fp))==1;
|
||||
#else
|
||||
// TODO: in theory windows 10 can be made to understand escapes as well
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void printAlarmTx(std::ostream& strm, const PVStructure& sub)
|
||||
{
|
||||
PVScalar::const_shared_pointer pvSeverity(sub.getSubField<PVInt>("severity"));
|
||||
PVScalar::const_shared_pointer pvStatus(sub.getSubField<PVInt>("status"));
|
||||
PVString::const_shared_pointer pvMessage(sub.getSubField<PVString>("message"));
|
||||
|
||||
switch(pvSeverity ? pvSeverity->getAs<int32>() : 0) {
|
||||
case 0: return; // nothing more to do here...
|
||||
case 1: strm<<"MINOR "; break;
|
||||
case 2: strm<<"MAJOR "; break;
|
||||
case 3: strm<<"INVALID "; break;
|
||||
case 4: strm<<"UNDEFINED "; break;
|
||||
default: strm<<pvSeverity->getAs<int32>()<<' ';
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, indent_level const& indent)
|
||||
{
|
||||
indent_value(os) = indent.level;
|
||||
return os;
|
||||
}
|
||||
switch(pvStatus ? pvStatus->getAs<int32>() : 0) {
|
||||
case 0: break;
|
||||
case 1: strm<<"DEVICE "; break;
|
||||
case 2: strm<<"DRIVER "; break;
|
||||
case 3: strm<<"RECORD "; break;
|
||||
case 4: strm<<"DB "; break;
|
||||
case 5: strm<<"CONF "; break;
|
||||
case 6: strm<<"UNDEFINED "; break;
|
||||
case 7: strm<<"CLIENT "; break;
|
||||
default: strm<<pvStatus->getAs<int32>()<<' ';
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, indent const&)
|
||||
{
|
||||
long il = indent_value(os);
|
||||
std::size_t spaces = static_cast<std::size_t>(il) * 4;
|
||||
return os << string(spaces, ' ');
|
||||
}
|
||||
|
||||
array_at_internal operator<<(std::ostream& str, array_at const& manip)
|
||||
{
|
||||
return array_at_internal(manip.index, str);
|
||||
}
|
||||
};
|
||||
|
||||
PrinterBase::PrinterBase()
|
||||
:strm(NULL)
|
||||
{}
|
||||
|
||||
PrinterBase::~PrinterBase() {}
|
||||
|
||||
void PrinterBase::setStream(std::ostream& s)
|
||||
{
|
||||
strm = &s;
|
||||
if(pvMessage && !pvMessage->get().empty())
|
||||
strm<<pvMessage->get()<<' ';
|
||||
}
|
||||
|
||||
void PrinterBase::clearStream()
|
||||
|
||||
void printAlarmT(std::ostream& strm, const PVStructure& top)
|
||||
{
|
||||
strm = NULL;
|
||||
PVStructure::const_shared_pointer sub(top.getSubField<PVStructure>("alarm"));
|
||||
if(sub)
|
||||
printAlarmTx(strm, *sub);
|
||||
}
|
||||
|
||||
void PrinterBase::print(const PVField& pv)
|
||||
void printTimeTx(std::ostream& strm, const PVStructure& tsubop)
|
||||
{
|
||||
if(!strm)
|
||||
throw std::runtime_error("No stream set for PV Printer");
|
||||
impl_print(pv);
|
||||
char timeText[32];
|
||||
epicsTimeStamp epicsTS;
|
||||
|
||||
PVScalar::const_shared_pointer secf(tsubop.getSubField<PVScalar>("secondsPastEpoch")),
|
||||
nsecf(tsubop.getSubField<PVScalar>("nanoseconds")),
|
||||
tagf(tsubop.getSubField<PVScalar>("userTag"));
|
||||
|
||||
epicsTS.secPastEpoch = secf ? secf->getAs<int64>() : 0;
|
||||
epicsTS.nsec = nsecf ? nsecf->getAs<int32>() : 0;
|
||||
|
||||
if(epicsTS.secPastEpoch > POSIX_TIME_AT_EPICS_EPOCH)
|
||||
epicsTS.secPastEpoch -= POSIX_TIME_AT_EPICS_EPOCH;
|
||||
else
|
||||
epicsTS.secPastEpoch = 0;
|
||||
|
||||
epicsTimeToStrftime(timeText, sizeof(timeText), "%Y-%m-%d %H:%M:%S.%03f", &epicsTS);
|
||||
strm <<std::setw(24) <<std::left <<timeText <<' ';
|
||||
if (tagf) {
|
||||
int64 tagv = tagf->getAs<int64>();
|
||||
if(tagv)
|
||||
strm << tagv << ' ';
|
||||
}
|
||||
}
|
||||
|
||||
void PrinterBase::beginStructure(const PVStructure&) {}
|
||||
void PrinterBase::endStructure(const PVStructure&) {}
|
||||
|
||||
void PrinterBase::beginStructureArray(const PVStructureArray&) {}
|
||||
void PrinterBase::endStructureArray(const PVStructureArray&) {}
|
||||
|
||||
void PrinterBase::beginUnion(const PVUnion&) {}
|
||||
void PrinterBase::endUnion(const PVUnion&) {}
|
||||
|
||||
void PrinterBase::beginUnionArray(const PVUnionArray&) {}
|
||||
void PrinterBase::endUnionArray(const PVUnionArray&) {}
|
||||
|
||||
void PrinterBase::encodeScalar(const PVScalar&) {}
|
||||
|
||||
void PrinterBase::encodeArray(const PVScalarArray&) {}
|
||||
|
||||
void PrinterBase::encodeNull() {}
|
||||
|
||||
void PrinterBase::impl_print(const PVField& pv)
|
||||
void printTimeT(std::ostream& strm, const PVStructure& top)
|
||||
{
|
||||
static const PVField* marker = (const PVField*)▮
|
||||
PVStructure::const_shared_pointer sub(top.getSubField<PVStructure>("timeStamp"));
|
||||
if(sub)
|
||||
printTimeTx(strm, *sub);
|
||||
}
|
||||
|
||||
/* Depth first recursive iteration.
|
||||
* Each PV to be printed is appended to the todo queue.
|
||||
* The last child of a structure is followed by a NULL.
|
||||
* As the tree is walked structures and structarrays
|
||||
* are appended to the inprog queue.
|
||||
*/
|
||||
std::deque<const PVField*> todo, inprog;
|
||||
bool printEnumT(std::ostream& strm, const PVStructure& top, bool fromtop)
|
||||
{
|
||||
PVStructure::const_shared_pointer value;
|
||||
if(fromtop) {
|
||||
value = top.getSubField<PVStructure>("value");
|
||||
} else {
|
||||
value = std::tr1::static_pointer_cast<const PVStructure>(top.shared_from_this());
|
||||
}
|
||||
PVScalar::const_shared_pointer idx(value->getSubField<PVScalar>("index"));
|
||||
PVStringArray::const_shared_pointer choices(value->getSubField<PVStringArray>("choices"));
|
||||
if(!idx || !choices) return false;
|
||||
|
||||
todo.push_back(&pv);
|
||||
if(fromtop) {
|
||||
strm<<format::indent();
|
||||
printTimeT(strm, top);
|
||||
printAlarmT(strm, top);
|
||||
}
|
||||
|
||||
while(!todo.empty()) {
|
||||
const PVField *next = todo.front();
|
||||
todo.pop_front();
|
||||
PVStringArray::const_svector ch(choices->view());
|
||||
uint32 I = idx->getAs<uint32>();
|
||||
strm<<'('<<I<<')';
|
||||
if(I>=ch.size()) {
|
||||
strm<<" <undefined>";
|
||||
} else {
|
||||
strm<<' '<<escape(ch[I]);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if(next==marker) {
|
||||
// finished with a structure or structarray,
|
||||
// now we fall back to its parent.
|
||||
assert(!inprog.empty());
|
||||
switch(inprog.back()->getField()->getType()) {
|
||||
case structure:
|
||||
endStructure(*static_cast<const PVStructure *>(inprog.back()));
|
||||
break;
|
||||
void csvEscape(std::string& S)
|
||||
{
|
||||
// concise, not particularly efficient...
|
||||
std::string temp(escape(S).style(escape::CSV).str());
|
||||
if(S.find_first_of(" ,\\")!=S.npos) {// only quote if necessary (stupid Excel)
|
||||
std::string temp2;
|
||||
temp2.reserve(temp.size()+2);
|
||||
temp2.push_back('\"');
|
||||
temp2 += temp;
|
||||
temp2.push_back('\"');
|
||||
temp2.swap(temp);
|
||||
}
|
||||
S = temp;
|
||||
}
|
||||
|
||||
case structureArray:
|
||||
endStructureArray(*static_cast<const PVStructureArray *>(inprog.back()));
|
||||
break;
|
||||
bool printTable(std::ostream& strm, const PVStructure& top)
|
||||
{
|
||||
PVStructure::const_shared_pointer cf(top.getSubField<PVStructure>("value"));
|
||||
if(!cf) return false;
|
||||
|
||||
case union_:
|
||||
endUnion(*static_cast<const PVUnion *>(inprog.back()));
|
||||
break;
|
||||
{
|
||||
const FieldConstPtrArray& fields = cf->getStructure()->getFields();
|
||||
if(fields.empty()) return false;
|
||||
for(size_t i=0, N=fields.size(); i<N; i++) {
|
||||
if(fields[i]->getType()!=scalarArray)
|
||||
return false; // cowardly refusing to handle anything else...
|
||||
}
|
||||
}
|
||||
|
||||
case unionArray:
|
||||
endUnionArray(*static_cast<const PVUnionArray *>(inprog.back()));
|
||||
break;
|
||||
// maybe output a line with meta-data
|
||||
{
|
||||
bool havets = !!top.getSubField("timeStamp");
|
||||
bool haveal = !!top.getSubField("alarm");
|
||||
if(havets || haveal)
|
||||
strm<<format::indent();
|
||||
if(havets) {
|
||||
printTimeT(strm, top);
|
||||
strm<<' ';
|
||||
}
|
||||
if(haveal) {
|
||||
printAlarmT(strm, top);
|
||||
strm<<' ';
|
||||
}
|
||||
strm<<'\n';
|
||||
}
|
||||
|
||||
default:
|
||||
assert(false); // oops!
|
||||
return;
|
||||
}
|
||||
inprog.pop_back();
|
||||
PVStringArray::svector labels;
|
||||
{
|
||||
PVStringArray::const_shared_pointer lf(top.getSubField<PVStringArray>("labels"));
|
||||
if(lf) {
|
||||
PVStringArray::const_svector L(lf->view());
|
||||
labels = thaw(L);
|
||||
}
|
||||
}
|
||||
|
||||
const PVFieldPtrArray& columns = cf->getPVFields();
|
||||
std::vector<shared_vector<std::string> > coldat(columns.size());
|
||||
|
||||
std::vector<size_t> widths(columns.size());
|
||||
labels.reserve(columns.size());
|
||||
|
||||
size_t nrows = size_t(-1);
|
||||
|
||||
for(size_t i=0, N=columns.size(); i<N; i++) {
|
||||
if(i>=labels.size()) {
|
||||
labels.push_back(cf->getStructure()->getFieldName(i));
|
||||
} else {
|
||||
// real field
|
||||
csvEscape(labels[i]);
|
||||
}
|
||||
widths[i] = labels[i].size();
|
||||
|
||||
if(!next) {
|
||||
// NULL entry in a structure array
|
||||
encodeNull();
|
||||
continue;
|
||||
}
|
||||
{
|
||||
shared_vector<const std::string> ro;
|
||||
static_cast<const PVScalarArray*>(columns[i].get())->getAs(ro);
|
||||
coldat[i] = thaw(ro);
|
||||
}
|
||||
|
||||
switch(next->getField()->getType()) {
|
||||
case scalar:
|
||||
encodeScalar(*static_cast<const PVScalar*>(next));
|
||||
break;
|
||||
case scalarArray:
|
||||
encodeArray(*static_cast<const PVScalarArray*>(next));
|
||||
break;
|
||||
case structure: {
|
||||
const PVStructure &fld = *static_cast<const PVStructure*>(next);
|
||||
const PVFieldPtrArray& vals = fld.getPVFields();
|
||||
// truncate if some columns are longer than others
|
||||
nrows = std::min(nrows, coldat[i].size());
|
||||
|
||||
inprog.push_back(next);
|
||||
for(size_t j=0, M=coldat[i].size(); j<M; j++) {
|
||||
csvEscape(coldat[i][j]);
|
||||
widths[i] = std::max(widths[i], coldat[i][j].size());
|
||||
}
|
||||
}
|
||||
|
||||
beginStructure(fld);
|
||||
for(size_t i=0, nfld=fld.getStructure()->getNumberFields(); i<nfld; i++)
|
||||
todo.push_back(vals[i].get());
|
||||
// output header line
|
||||
strm<<format::indent();
|
||||
for(size_t c=0, N=coldat.size(); c<N; c++) {
|
||||
strm<<std::setw(widths[c])<<std::right<<labels[c];
|
||||
if(c+1!=N) {
|
||||
strm<<' ';
|
||||
}
|
||||
}
|
||||
strm<<'\n';
|
||||
|
||||
todo.push_back(marker);
|
||||
break;
|
||||
}
|
||||
case structureArray: {
|
||||
const PVStructureArray &fld = *static_cast<const PVStructureArray*>(next);
|
||||
PVStructureArray::const_svector vals(fld.view());
|
||||
for(size_t r=0; r<nrows; r++) {
|
||||
strm<<format::indent();
|
||||
for(size_t c=0, N=coldat.size(); c<N; c++) {
|
||||
strm<<std::setw(widths[c])<<std::right<<coldat[c][r];
|
||||
if(c+1!=N)
|
||||
strm<<' ';
|
||||
}
|
||||
strm<<'\n';
|
||||
}
|
||||
|
||||
inprog.push_back(next);
|
||||
return true;
|
||||
}
|
||||
|
||||
beginStructureArray(fld);
|
||||
for(PVStructureArray::const_svector::const_iterator it=vals.begin();
|
||||
it!=vals.end(); ++it)
|
||||
{
|
||||
todo.push_back(it->get());
|
||||
}
|
||||
void expandBS(const PVStructure& top, BitSet& mask, bool parents) {
|
||||
if(mask.get(0)) { // special handling because getSubField(0) not allowed
|
||||
// wildcard
|
||||
for(size_t idx=1, N=top.getNumberFields(); idx<N; idx++) {
|
||||
mask.set(idx);
|
||||
}
|
||||
|
||||
todo.push_back(marker);
|
||||
break;
|
||||
}
|
||||
case union_: {
|
||||
const PVUnion &fld = *static_cast<const PVUnion*>(next);
|
||||
} else {
|
||||
for(int32 idx = mask.nextSetBit(0), N=top.getNumberFields(); idx>=0 && idx<N; idx=mask.nextSetBit(idx+1)) {
|
||||
PVField::const_shared_pointer fld = top.getSubFieldT(idx);
|
||||
|
||||
inprog.push_back(next);
|
||||
// look forward and mark all children
|
||||
for(size_t i=idx+1, N=fld->getNextFieldOffset(); i<N; i++)
|
||||
mask.set(i);
|
||||
|
||||
beginUnion(fld);
|
||||
PVFieldPtr val = fld.get();
|
||||
if (val.get()) // TODO print "(none)" ?
|
||||
todo.push_back(val.get());
|
||||
|
||||
todo.push_back(marker);
|
||||
break;
|
||||
}
|
||||
case unionArray: {
|
||||
const PVUnionArray &fld = *static_cast<const PVUnionArray*>(next);
|
||||
PVUnionArray::const_svector vals(fld.view());
|
||||
|
||||
inprog.push_back(next);
|
||||
|
||||
beginUnionArray(fld);
|
||||
for(PVUnionArray::const_svector::const_iterator it=vals.begin();
|
||||
it!=vals.end(); ++it)
|
||||
{
|
||||
todo.push_back(it->get());
|
||||
}
|
||||
|
||||
todo.push_back(marker);
|
||||
break;
|
||||
if(parents) {
|
||||
// look back and mark all parents
|
||||
// we've already stepped past all parents so siblings will not be automatically marked
|
||||
for(const PVStructure *parent = fld->getParent(); parent; parent = parent->getParent()) {
|
||||
mask.set(parent->getFieldOffset());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
PrinterPlain::PrinterPlain()
|
||||
:PrinterBase()
|
||||
,ilvl(0)
|
||||
{}
|
||||
|
||||
PrinterPlain::~PrinterPlain() {}
|
||||
|
||||
void PrinterPlain::beginStructure(const PVStructure& pv)
|
||||
{
|
||||
indentN(S(), ilvl);
|
||||
S() << pv.getStructure()->getID() << " " << pv.getFieldName();
|
||||
S() << std::endl;
|
||||
ilvl++;
|
||||
}
|
||||
namespace epics { namespace pvData {
|
||||
|
||||
void PrinterPlain::endStructure(const PVStructure&) {ilvl--;}
|
||||
|
||||
void PrinterPlain::beginStructureArray(const PVStructureArray& pv)
|
||||
void printRaw(std::ostream& strm, const PVStructure::Formatter& format, const PVStructure& cur)
|
||||
{
|
||||
indentN(S(), ilvl);
|
||||
S() << pv.getStructureArray()->getID() << " "
|
||||
<< pv.getFieldName() << "[] ";
|
||||
ilvl++;
|
||||
}
|
||||
typedef std::deque<std::pair<const PVField*, size_t> > todo_t;
|
||||
todo_t todo;
|
||||
BitSet show, highlight;
|
||||
|
||||
void PrinterPlain::endStructureArray(const PVStructureArray&) {ilvl--;}
|
||||
const long orig_indent = format::indent_value(strm);
|
||||
|
||||
void PrinterPlain::beginUnion(const PVUnion& pv)
|
||||
{
|
||||
indentN(S(), ilvl);
|
||||
S() << pv.getUnion()->getID() << " " << pv.getFieldName() << std::endl;
|
||||
ilvl++;
|
||||
}
|
||||
{
|
||||
if(format.xshow)
|
||||
show = *format.xshow;
|
||||
else
|
||||
show.set(0);
|
||||
|
||||
void PrinterPlain::endUnion(const PVUnion&) {ilvl--;}
|
||||
if(format.xhighlight)
|
||||
highlight = *format.xhighlight;
|
||||
|
||||
void PrinterPlain::beginUnionArray(const PVUnionArray& pv)
|
||||
{
|
||||
indentN(S(), ilvl);
|
||||
S() << pv.getUnionArray()->getID() << " "
|
||||
<< pv.getFieldName() << "[] ";
|
||||
ilvl++;
|
||||
}
|
||||
|
||||
void PrinterPlain::endUnionArray(const PVUnionArray&) {ilvl--;}
|
||||
|
||||
void PrinterPlain::encodeScalar(const PVScalar& pv)
|
||||
{
|
||||
indentN(S(), ilvl);
|
||||
S() << pv.getScalar()->getID() << " "
|
||||
<< pv.getFieldName() << " "
|
||||
<< pv.getAs<string>() << std::endl;
|
||||
}
|
||||
|
||||
void PrinterPlain::encodeArray(const PVScalarArray& pv)
|
||||
{
|
||||
indentN(S(), ilvl);
|
||||
shared_vector<const string> temp;
|
||||
pv.getAs<string>(temp);
|
||||
|
||||
S() << pv.getScalarArray()->getID() << " "
|
||||
<< pv.getFieldName() << " [";
|
||||
for(size_t i=0, len=pv.getLength(); i<len; i++) {
|
||||
S() << temp[i];
|
||||
if(i!=len-1)
|
||||
S().put(',');
|
||||
expandBS(format.xtop, show, true);
|
||||
expandBS(format.xtop, highlight, false);
|
||||
highlight &= show; // can't highlight hidden fields (paranoia)
|
||||
}
|
||||
S() << "]" << std::endl;
|
||||
|
||||
if(!show.get(0)) return; // nothing to do here...
|
||||
|
||||
todo.push_front(std::make_pair(&format.xtop, orig_indent));
|
||||
|
||||
while(!todo.empty()) {
|
||||
todo_t::value_type cur(todo.front());
|
||||
todo.pop_front();
|
||||
|
||||
format::indent_value(strm) = cur.second;
|
||||
|
||||
bool hl = highlight.get(cur.first->getFieldOffset()) && format.xmode==PVStructure::Formatter::ANSI;
|
||||
if(hl)
|
||||
strm<<"\x1b[1m"; // Bold
|
||||
|
||||
switch(cur.first->getField()->getType()) {
|
||||
case structure: {
|
||||
const PVStructure* str = static_cast<const PVStructure*>(cur.first);
|
||||
|
||||
const PVFieldPtrArray& flds = str->getPVFields();
|
||||
|
||||
for(PVFieldPtrArray::const_reverse_iterator it(flds.rbegin()), end(flds.rend()); it!=end; ++it) {
|
||||
const PVField *fld = (*it).get();
|
||||
if(!show.get(fld->getFieldOffset())) continue;
|
||||
|
||||
todo.push_front(std::make_pair(fld, cur.second+1));
|
||||
}
|
||||
|
||||
std::string id(cur.first->getField()->getID());
|
||||
|
||||
strm<<format::indent()<<id<<' '<<cur.first->getFieldName();
|
||||
if(id=="alarm_t") {
|
||||
strm.put(' ');
|
||||
printAlarmTx(strm, *str);
|
||||
} else if(id=="time_t") {
|
||||
strm.put(' ');
|
||||
printTimeTx(strm, *str);
|
||||
} else if(id=="enum_t") {
|
||||
strm.put(' ');
|
||||
printEnumT(strm, *str, false);
|
||||
}
|
||||
strm.put('\n');
|
||||
}
|
||||
break;
|
||||
case scalar:
|
||||
case scalarArray:
|
||||
strm<<format::indent()<<cur.first->getField()->getID()<<' '<<cur.first->getFieldName()
|
||||
<<' '<<*cur.first
|
||||
<<'\n';
|
||||
break;
|
||||
case structureArray:
|
||||
case union_:
|
||||
case unionArray:
|
||||
strm<<*cur.first;
|
||||
break;
|
||||
}
|
||||
|
||||
if(hl)
|
||||
strm<<"\x1b[0m"; // reset
|
||||
}
|
||||
|
||||
format::indent_value(strm) = orig_indent;
|
||||
}
|
||||
|
||||
void PrinterPlain::encodeNull()
|
||||
std::ostream& operator<<(std::ostream& strm, const PVStructure::Formatter& format)
|
||||
{
|
||||
indentN(S(), ilvl);
|
||||
S() << "NULL" << std::endl;
|
||||
if(format.xfmt==PVStructure::Formatter::JSON) {
|
||||
#if EPICS_VERSION_INT>=VERSION_INT(3,15,0,1)
|
||||
JSONPrintOptions opts;
|
||||
opts.multiLine = false;
|
||||
printJSON(strm, format.xtop, format.xshow ? *format.xshow : BitSet().set(0), opts);
|
||||
strm<<'\n';
|
||||
return strm;
|
||||
#else
|
||||
// fall through to Raw
|
||||
#endif
|
||||
|
||||
} else if(format.xfmt==PVStructure::Formatter::NT) {
|
||||
std::string id(format.xtop.getStructure()->getID()),
|
||||
idprefix(id.substr(0, id.find_first_of('.')));
|
||||
|
||||
// NTTable
|
||||
if(idprefix=="epics:nt/NTTable:1") {
|
||||
if(printTable(strm, format.xtop))
|
||||
return strm;
|
||||
} else {
|
||||
//NTScalar, NTScalarArray, NTEnum, or anything with '.value'
|
||||
|
||||
PVField::const_shared_pointer value(format.xtop.getSubField("value"));
|
||||
if(value) {
|
||||
switch(value->getField()->getType()) {
|
||||
case scalar:
|
||||
strm<<format::indent();
|
||||
printTimeT(strm, format.xtop);
|
||||
strm<<std::setprecision(6)<<*static_cast<const PVScalar*>(value.get())<<' ';
|
||||
printAlarmT(strm, format.xtop);
|
||||
strm<<'\n';
|
||||
return strm;
|
||||
|
||||
case scalarArray:
|
||||
strm<<format::indent();
|
||||
printTimeT(strm, format.xtop);
|
||||
printAlarmT(strm, format.xtop);
|
||||
strm<<std::setprecision(6)<<*static_cast<const PVScalarArray*>(value.get())<<'\n';
|
||||
return strm;
|
||||
|
||||
case structure:
|
||||
if(printEnumT(strm, format.xtop, true)) {
|
||||
strm<<'\n';
|
||||
return strm;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// fall through unhandled as Raw
|
||||
|
||||
PVStructure::Formatter format2(format);
|
||||
|
||||
if(format2.xmode==PVStructure::Formatter::Auto)
|
||||
format2.xmode = useEscapes(strm) ? PVStructure::Formatter::ANSI : PVStructure::Formatter::Plain;
|
||||
|
||||
printRaw(strm, format2, format.xtop);
|
||||
|
||||
return strm;
|
||||
}
|
||||
|
||||
}}
|
||||
static char hexdigit(char c) {
|
||||
c &= 0xf;
|
||||
if(c<9)
|
||||
return '0'+c;
|
||||
else
|
||||
return 'A'+c-10;
|
||||
}
|
||||
|
||||
escape::~escape() {}
|
||||
|
||||
std::string escape::str() const
|
||||
{
|
||||
std::ostringstream strm;
|
||||
strm<<(*this);
|
||||
return strm.str();
|
||||
}
|
||||
|
||||
epicsShareFunc
|
||||
std::ostream& operator<<(std::ostream& strm, const escape& Q)
|
||||
{
|
||||
for(size_t pos = 0, len = Q.orig.size(); pos < len; pos++) {
|
||||
const char C = Q.orig[pos];
|
||||
char quote = '\\', next;
|
||||
// compare me with epicsStrnEscapedFromRaw()
|
||||
switch(C) {
|
||||
case '\a': next = 'a'; break;
|
||||
case '\b': next = 'b'; break;
|
||||
case '\f': next = 'f'; break;
|
||||
case '\n': next = 'n'; break;
|
||||
case '\r': next = 'r'; break;
|
||||
case '\t': next = 't'; break;
|
||||
case '\v': next = 'v'; break;
|
||||
case '\\': next = '\\'; break;
|
||||
case '\'': next = '\''; break;
|
||||
case '\"': next = '\"'; if(Q.S==escape::CSV) quote = '"'; break;
|
||||
default:
|
||||
if(!isprint(C)) {
|
||||
// print three charator escape
|
||||
strm<<"\\x"<<hexdigit(C>>4)<<hexdigit(C);
|
||||
} else {
|
||||
// literal
|
||||
strm.put(C);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
// print two charactor escape
|
||||
strm.put(quote);
|
||||
strm.put(next);
|
||||
}
|
||||
|
||||
return strm;
|
||||
}
|
||||
|
||||
}} //epics::pvData
|
||||
|
||||
17
src/factory/pv/factory.h
Normal file
17
src/factory/pv/factory.h
Normal file
@@ -0,0 +1,17 @@
|
||||
/*factory.h*/
|
||||
/*
|
||||
* Copyright information and license terms for this software can be
|
||||
* found in the file LICENSE that is included with the distribution
|
||||
*/
|
||||
/**
|
||||
* @author mrk
|
||||
*/
|
||||
#ifndef FACTORY_H
|
||||
#define FACTORY_H
|
||||
|
||||
namespace epics { namespace pvData {
|
||||
|
||||
enum DebugLevel{noDebug,lowDebug,highDebug};
|
||||
|
||||
}}
|
||||
#endif /*FACTORY_H */
|
||||
@@ -1,7 +1,6 @@
|
||||
/**
|
||||
* Copyright - See the COPYRIGHT that is included with this distribution.
|
||||
* EPICS pvData is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
/*
|
||||
* Copyright information and license terms for this software can be
|
||||
* found in the file LICENSE that is included with the distribution
|
||||
*/
|
||||
/**
|
||||
* @author Marty Kraimer
|
||||
|
||||
10
src/json/Makefile
Normal file
10
src/json/Makefile
Normal file
@@ -0,0 +1,10 @@
|
||||
# This is a Makefile fragment, see ../Makefile
|
||||
|
||||
SRC_DIRS += $(PVDATA_SRC)/json
|
||||
|
||||
INC += pv/json.h
|
||||
|
||||
LIBSRCS += parsehelper.cpp
|
||||
LIBSRCS += parseany.cpp
|
||||
LIBSRCS += parseinto.cpp
|
||||
LIBSRCS += print.cpp
|
||||
284
src/json/parseany.cpp
Normal file
284
src/json/parseany.cpp
Normal file
@@ -0,0 +1,284 @@
|
||||
/*
|
||||
* Copyright information and license terms for this software can be
|
||||
* found in the file LICENSE that is included with the distribution
|
||||
*/
|
||||
|
||||
#include <sstream>
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include <pv/pvdVersion.h>
|
||||
#include <pv/pvData.h>
|
||||
#include <pv/valueBuilder.h>
|
||||
|
||||
#if EPICS_VERSION_INT>=VERSION_INT(3,15,0,1)
|
||||
|
||||
#include "pv/json.h"
|
||||
|
||||
namespace pvd = epics::pvData;
|
||||
using pvd::yajl::integer_arg;
|
||||
using pvd::yajl::size_arg;
|
||||
|
||||
namespace {
|
||||
|
||||
struct context {
|
||||
|
||||
unsigned depth;
|
||||
|
||||
enum state_t {
|
||||
Undefined,
|
||||
Key,
|
||||
Array,
|
||||
} state;
|
||||
|
||||
pvd::shared_vector<void> arr;
|
||||
|
||||
pvd::ValueBuilder root,
|
||||
*cur;
|
||||
|
||||
std::string msg,
|
||||
key;
|
||||
|
||||
context() :depth(0u), state(Undefined), cur(&root) {}
|
||||
};
|
||||
|
||||
#define TRY context *self = (context*)ctx; try
|
||||
|
||||
#define CATCH() catch(std::exception& e) { self->msg = e.what(); return 0; }
|
||||
|
||||
int jtree_null(void * ctx)
|
||||
{
|
||||
TRY {
|
||||
self->msg = "NULL value not permitted";
|
||||
return 0;
|
||||
}CATCH()
|
||||
}
|
||||
|
||||
int jtree_boolean(void * ctx, int boolVal)
|
||||
{
|
||||
TRY {
|
||||
if(self->depth==0) throw std::runtime_error("Bare value not supported");
|
||||
switch(self->state) {
|
||||
case context::Key:
|
||||
self->cur = &self->cur->add<pvd::pvBoolean>(self->key, boolVal);
|
||||
self->key.clear();
|
||||
self->state = context::Undefined;
|
||||
break;
|
||||
case context::Array:
|
||||
{
|
||||
if(self->arr.size()>0 && self->arr.original_type()!=pvd::pvBoolean)
|
||||
throw std::runtime_error("Mixed type array not supported");
|
||||
pvd::shared_vector<pvd::boolean> arr(pvd::static_shared_vector_cast<pvd::boolean>(self->arr));
|
||||
arr.push_back(boolVal);
|
||||
self->arr = pvd::static_shared_vector_cast<void>(arr);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw std::logic_error("boolean in bad state");
|
||||
}
|
||||
return 1;
|
||||
}CATCH()
|
||||
}
|
||||
|
||||
int jtree_integer(void * ctx, integer_arg integerVal)
|
||||
{
|
||||
TRY {
|
||||
if(self->depth==0) throw std::runtime_error("Bare value not supported");
|
||||
switch(self->state) {
|
||||
case context::Key:
|
||||
self->cur = &self->cur->add<pvd::pvLong>(self->key, integerVal);
|
||||
self->key.clear();
|
||||
self->state = context::Undefined;
|
||||
break;
|
||||
case context::Array:
|
||||
{
|
||||
if(self->arr.size()>0 && self->arr.original_type()!=pvd::pvLong)
|
||||
throw std::runtime_error("Mixed type array not supported");
|
||||
pvd::shared_vector<pvd::int64> arr(pvd::static_shared_vector_cast<pvd::int64>(self->arr));
|
||||
arr.push_back(integerVal);
|
||||
self->arr = pvd::static_shared_vector_cast<void>(arr);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw std::logic_error("int64 in bad state");
|
||||
}
|
||||
return 1;
|
||||
}CATCH()
|
||||
}
|
||||
|
||||
int jtree_double(void * ctx, double doubleVal)
|
||||
{
|
||||
TRY {
|
||||
if(self->depth==0) throw std::runtime_error("Bare value not supported");
|
||||
switch(self->state) {
|
||||
case context::Key:
|
||||
self->cur = &self->cur->add<pvd::pvDouble>(self->key, doubleVal);
|
||||
self->key.clear();
|
||||
self->state = context::Undefined;
|
||||
break;
|
||||
case context::Array:
|
||||
{
|
||||
if(self->arr.size()>0 && self->arr.original_type()!=pvd::pvDouble)
|
||||
throw std::runtime_error("Mixed type array not supported");
|
||||
pvd::shared_vector<double> arr(pvd::static_shared_vector_cast<double>(self->arr));
|
||||
arr.push_back(doubleVal);
|
||||
self->arr = pvd::static_shared_vector_cast<void>(arr);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw std::logic_error("double in bad state");
|
||||
}
|
||||
return 1;
|
||||
}CATCH()
|
||||
}
|
||||
|
||||
int jtree_string(void * ctx, const unsigned char * stringVal,
|
||||
size_arg stringLen)
|
||||
{
|
||||
TRY {
|
||||
if(self->depth==0) throw std::runtime_error("Bare value not supported");
|
||||
std::string sval((const char*)stringVal, stringLen);
|
||||
switch(self->state) {
|
||||
case context::Key:
|
||||
self->cur = &self->cur->add<pvd::pvString>(self->key, sval);
|
||||
self->key.clear();
|
||||
self->state = context::Undefined;
|
||||
break;
|
||||
case context::Array:
|
||||
{
|
||||
if(self->arr.size()>0 && self->arr.original_type()!=pvd::pvString)
|
||||
throw std::runtime_error("Mixed type array not supported");
|
||||
pvd::shared_vector<std::string> arr(pvd::static_shared_vector_cast<std::string>(self->arr));
|
||||
arr.push_back(sval);
|
||||
self->arr = pvd::static_shared_vector_cast<void>(arr);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw std::logic_error("double in bad state");
|
||||
}
|
||||
return 1;
|
||||
}CATCH()
|
||||
}
|
||||
|
||||
int jtree_start_map(void * ctx)
|
||||
{
|
||||
TRY {
|
||||
if(self->depth>0) {
|
||||
if(self->key.empty())
|
||||
throw std::logic_error("anonymous dict not top level?");
|
||||
self->cur = &self->cur->addNested(self->key);
|
||||
self->key.clear();
|
||||
}
|
||||
self->depth++;
|
||||
return 1;
|
||||
}CATCH()
|
||||
}
|
||||
|
||||
int jtree_map_key(void * ctx, const unsigned char * key,
|
||||
size_arg stringLen)
|
||||
{
|
||||
TRY {
|
||||
if(!self->key.empty())
|
||||
throw std::logic_error("double key?");
|
||||
if(stringLen==0)
|
||||
throw std::runtime_error("empty key not allowed");
|
||||
self->key = std::string((const char*)key, stringLen);
|
||||
self->state = context::Key;
|
||||
return 1;
|
||||
}CATCH()
|
||||
}
|
||||
|
||||
int jtree_end_map(void * ctx)
|
||||
{
|
||||
TRY {
|
||||
if(self->depth>1)
|
||||
self->cur = &self->cur->endNested();
|
||||
else if(self->depth==0)
|
||||
throw std::logic_error("Unbalenced dict");
|
||||
self->depth--;
|
||||
return 1;
|
||||
}CATCH()
|
||||
}
|
||||
|
||||
int jtree_start_array(void * ctx)
|
||||
{
|
||||
TRY {
|
||||
if(self->depth==0) throw std::runtime_error("Bare array not supported");
|
||||
if(self->state!=context::Key)
|
||||
throw std::logic_error("bare array not supported");
|
||||
self->state = context::Array;
|
||||
return 1;
|
||||
}CATCH()
|
||||
}
|
||||
int jtree_end_array(void * ctx)
|
||||
{
|
||||
TRY {
|
||||
if(self->state!=context::Array)
|
||||
throw std::logic_error("Bad array parse");
|
||||
self->cur = &self->cur->add(self->key, pvd::freeze(self->arr));
|
||||
self->key.clear();
|
||||
self->state = context::Undefined;
|
||||
return 1;
|
||||
}CATCH()
|
||||
}
|
||||
|
||||
|
||||
yajl_callbacks jtree_cbs = {
|
||||
&jtree_null,
|
||||
&jtree_boolean,
|
||||
&jtree_integer,
|
||||
&jtree_double,
|
||||
NULL, // number
|
||||
&jtree_string,
|
||||
&jtree_start_map,
|
||||
&jtree_map_key,
|
||||
&jtree_end_map,
|
||||
&jtree_start_array,
|
||||
&jtree_end_array,
|
||||
};
|
||||
|
||||
struct handler {
|
||||
yajl_handle handle;
|
||||
handler(yajl_handle handle) :handle(handle)
|
||||
{
|
||||
if(!handle)
|
||||
throw std::runtime_error("Failed to allocate yajl handle");
|
||||
}
|
||||
~handler() {
|
||||
yajl_free(handle);
|
||||
}
|
||||
operator yajl_handle() { return handle; }
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace epics{namespace pvData{
|
||||
|
||||
epics::pvData::PVStructure::shared_pointer
|
||||
parseJSON(std::istream& strm)
|
||||
{
|
||||
#ifndef EPICS_YAJL_VERSION
|
||||
yajl_parser_config conf;
|
||||
memset(&conf, 0, sizeof(conf));
|
||||
conf.allowComments = 1;
|
||||
conf.checkUTF8 = 1;
|
||||
#endif
|
||||
|
||||
context ctxt;
|
||||
|
||||
#ifndef EPICS_YAJL_VERSION
|
||||
handler handle(yajl_alloc(&jtree_cbs, &conf, NULL, &ctxt));
|
||||
#else
|
||||
handler handle(yajl_alloc(&jtree_cbs, NULL, &ctxt));
|
||||
|
||||
yajl_config(handle, yajl_allow_comments, 1);
|
||||
#endif
|
||||
|
||||
if(!yajl_parse_helper(strm, handle))
|
||||
throw std::runtime_error(ctxt.msg);
|
||||
|
||||
return ctxt.cur->buildPVStructure();
|
||||
}
|
||||
|
||||
}} // namespace epics::pvData
|
||||
|
||||
#endif // EPICS_VERSION_INT
|
||||
122
src/json/parsehelper.cpp
Normal file
122
src/json/parsehelper.cpp
Normal file
@@ -0,0 +1,122 @@
|
||||
/*
|
||||
* Copyright information and license terms for this software can be
|
||||
* found in the file LICENSE that is included with the distribution
|
||||
*/
|
||||
|
||||
#include <stdexcept>
|
||||
#include <sstream>
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include <pv/pvdVersion.h>
|
||||
|
||||
#if EPICS_VERSION_INT>=VERSION_INT(3,15,0,1)
|
||||
|
||||
#include "pv/json.h"
|
||||
|
||||
namespace {
|
||||
|
||||
void check_trailing(const std::string& line)
|
||||
{
|
||||
size_t idx = line.find_first_not_of(" \t\n\r");
|
||||
if(idx==line.npos) return;
|
||||
// TODO: detect the end of potentially multi-line comments...
|
||||
// for now trailing comments not allowed
|
||||
throw std::runtime_error("Trailing junk");
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace epics{namespace pvData{
|
||||
|
||||
bool yajl_parse_helper(std::istream& src,
|
||||
yajl_handle handle)
|
||||
{
|
||||
unsigned linenum=0;
|
||||
#ifndef EPICS_YAJL_VERSION
|
||||
bool done = false;
|
||||
#endif
|
||||
|
||||
std::string line;
|
||||
while(std::getline(src, line)) {
|
||||
linenum++;
|
||||
|
||||
#ifndef EPICS_YAJL_VERSION
|
||||
if(done) {
|
||||
check_trailing(line);
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
|
||||
yajl_status sts = yajl_parse(handle, (const unsigned char*)line.c_str(), line.size());
|
||||
|
||||
switch(sts) {
|
||||
case yajl_status_ok: {
|
||||
size_t consumed = yajl_get_bytes_consumed(handle);
|
||||
|
||||
if(consumed<line.size()) {
|
||||
check_trailing(line.substr(consumed));
|
||||
}
|
||||
|
||||
#ifndef EPICS_YAJL_VERSION
|
||||
done = true;
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
case yajl_status_client_canceled:
|
||||
return false;
|
||||
#ifndef EPICS_YAJL_VERSION
|
||||
case yajl_status_insufficient_data:
|
||||
// continue with next line
|
||||
break;
|
||||
#endif
|
||||
case yajl_status_error:
|
||||
{
|
||||
std::ostringstream msg;
|
||||
unsigned char *raw = yajl_get_error(handle, 1, (const unsigned char*)line.c_str(), line.size());
|
||||
if(!raw) {
|
||||
msg<<"Unknown error on line "<<linenum;
|
||||
} else {
|
||||
try {
|
||||
msg<<"Error on line "<<linenum<<" : "<<(const char*)raw;
|
||||
}catch(...){
|
||||
yajl_free_error(handle, raw);
|
||||
throw;
|
||||
}
|
||||
yajl_free_error(handle, raw);
|
||||
}
|
||||
throw std::runtime_error(msg.str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(!src.eof() || src.bad()) {
|
||||
std::ostringstream msg;
|
||||
msg<<"I/O error after line "<<linenum;
|
||||
throw std::runtime_error(msg.str());
|
||||
|
||||
#ifndef EPICS_YAJL_VERSION
|
||||
} else if(!done) {
|
||||
switch(yajl_parse_complete(handle)) {
|
||||
#else
|
||||
} else {
|
||||
switch(yajl_complete_parse(handle)) {
|
||||
#endif
|
||||
case yajl_status_ok:
|
||||
break;
|
||||
case yajl_status_client_canceled:
|
||||
return false;
|
||||
#ifndef EPICS_YAJL_VERSION
|
||||
case yajl_status_insufficient_data:
|
||||
throw std::runtime_error("unexpected end of input");
|
||||
#endif
|
||||
case yajl_status_error:
|
||||
throw std::runtime_error("Error while completing parsing");
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}} // namespace epics::pvData
|
||||
|
||||
#endif // EPICS_VERSION_INT
|
||||
352
src/json/parseinto.cpp
Normal file
352
src/json/parseinto.cpp
Normal file
@@ -0,0 +1,352 @@
|
||||
/*
|
||||
* Copyright information and license terms for this software can be
|
||||
* found in the file LICENSE that is included with the distribution
|
||||
*/
|
||||
|
||||
#include <vector>
|
||||
#include <sstream>
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include <pv/pvdVersion.h>
|
||||
#include <pv/pvData.h>
|
||||
#include <pv/valueBuilder.h>
|
||||
#include <pv/bitSet.h>
|
||||
|
||||
#if EPICS_VERSION_INT>=VERSION_INT(3,15,0,1)
|
||||
|
||||
#include "pv/json.h"
|
||||
|
||||
namespace pvd = epics::pvData;
|
||||
using pvd::yajl::integer_arg;
|
||||
using pvd::yajl::size_arg;
|
||||
|
||||
namespace {
|
||||
struct context {
|
||||
|
||||
std::string msg;
|
||||
|
||||
struct frame {
|
||||
pvd::PVFieldPtr fld;
|
||||
pvd::BitSet *assigned;
|
||||
frame(const pvd::PVFieldPtr& fld, pvd::BitSet *assigned)
|
||||
:fld(fld), assigned(assigned)
|
||||
{}
|
||||
};
|
||||
|
||||
typedef std::vector<frame> stack_t;
|
||||
stack_t stack;
|
||||
|
||||
context(const pvd::PVFieldPtr& root, pvd::BitSet *assigned)
|
||||
{
|
||||
stack.push_back(frame(root, assigned));
|
||||
}
|
||||
};
|
||||
|
||||
#define TRY context *self = (context*)ctx; assert(!self->stack.empty()); try
|
||||
|
||||
#define CATCH() catch(std::exception& e) { if(self->msg.empty()) self->msg = e.what(); return 0; }
|
||||
|
||||
int jtree_null(void * ctx)
|
||||
{
|
||||
TRY {
|
||||
self->msg = "NULL value not permitted";
|
||||
return 0;
|
||||
}CATCH()
|
||||
}
|
||||
|
||||
template<typename PVScalarT, typename PVArrayT>
|
||||
void valueAssign(context *self, typename PVScalarT::value_type val)
|
||||
{
|
||||
assert(!self->stack.empty());
|
||||
context::frame& back = self->stack.back();
|
||||
pvd::Type type(back.fld->getField()->getType());
|
||||
if(type==pvd::scalar) {
|
||||
pvd::PVScalar* fld(static_cast<pvd::PVScalar*>(back.fld.get()));
|
||||
|
||||
fld->putFrom(val);
|
||||
if(back.assigned)
|
||||
back.assigned->set(fld->getFieldOffset());
|
||||
self->stack.pop_back();
|
||||
// structure at the top of the stack
|
||||
|
||||
} else if(type==pvd::scalarArray) {
|
||||
pvd::PVScalarArray *fld(static_cast<pvd::PVScalarArray*>(back.fld.get()));
|
||||
|
||||
pvd::shared_vector<const void> carr;
|
||||
fld->getAs(carr);
|
||||
|
||||
switch(carr.original_type())
|
||||
{
|
||||
#define CASE_STRING
|
||||
#define CASE_REAL_INT64
|
||||
#define CASE(BASETYPE, PVATYPE, DBFTYPE, PVACODE) case epics::pvData::pv##PVACODE: { \
|
||||
pvd::shared_vector<const PVATYPE> arr(pvd::static_shared_vector_cast<const PVATYPE>(carr)); \
|
||||
pvd::shared_vector<PVATYPE> tarr(pvd::thaw(arr)); \
|
||||
tarr.push_back(pvd::castUnsafe<PVATYPE>(val)); \
|
||||
carr = pvd::static_shared_vector_cast<const void>(pvd::freeze(tarr)); \
|
||||
} break;
|
||||
#include <pv/typemap.h>
|
||||
#undef CASE
|
||||
#undef CASE_REAL_INT64
|
||||
#undef CASE_STRING
|
||||
}
|
||||
|
||||
fld->putFrom(carr);
|
||||
|
||||
// leave array field at top of stack
|
||||
|
||||
} else if(type==pvd::union_) {
|
||||
pvd::PVUnion* fld(static_cast<pvd::PVUnion*>(back.fld.get()));
|
||||
pvd::UnionConstPtr utype(fld->getUnion());
|
||||
|
||||
if(utype->isVariant()) {
|
||||
typename PVScalarT::shared_pointer elem(pvd::getPVDataCreate()->createPVScalar<PVScalarT>());
|
||||
|
||||
elem->put(val);
|
||||
|
||||
fld->set(elem);
|
||||
|
||||
} else {
|
||||
// attempt automagic assignment
|
||||
|
||||
const pvd::StringArray& names = utype->getFieldNames();
|
||||
const pvd::FieldConstPtrArray types = utype->getFields();
|
||||
assert(names.size()==types.size());
|
||||
|
||||
bool assigned = false;
|
||||
for(size_t i=0, N=names.size(); i<N; i++) {
|
||||
if(types[i]->getType()!=pvd::scalar) continue;
|
||||
|
||||
pvd::PVScalarPtr ufld(fld->select<pvd::PVScalar>(i));
|
||||
try{
|
||||
ufld->putFrom(val);
|
||||
assigned = true;
|
||||
}catch(std::runtime_error&){
|
||||
if(i==N-1)
|
||||
throw;
|
||||
continue;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
if(!assigned)
|
||||
throw std::runtime_error("Unable to select union member");
|
||||
}
|
||||
if(back.assigned)
|
||||
back.assigned->set(fld->getFieldOffset());
|
||||
self->stack.pop_back();
|
||||
// structure back at the top of the stack
|
||||
|
||||
} else {
|
||||
throw std::invalid_argument("Can't assign value");
|
||||
}
|
||||
}
|
||||
|
||||
int jtree_boolean(void * ctx, int boolVal)
|
||||
{
|
||||
TRY {
|
||||
valueAssign<pvd::PVBoolean, pvd::PVBooleanArray>(self, !!boolVal);
|
||||
return 1;
|
||||
}CATCH()
|
||||
}
|
||||
|
||||
int jtree_integer(void * ctx, integer_arg integerVal)
|
||||
{
|
||||
TRY {
|
||||
valueAssign<pvd::PVLong, pvd::PVLongArray>(self, integerVal);
|
||||
return 1;
|
||||
}CATCH()
|
||||
}
|
||||
|
||||
int jtree_double(void * ctx, double doubleVal)
|
||||
{
|
||||
TRY {
|
||||
valueAssign<pvd::PVDouble, pvd::PVDoubleArray>(self, doubleVal);
|
||||
return 1;
|
||||
}CATCH()
|
||||
}
|
||||
|
||||
int jtree_string(void * ctx, const unsigned char * stringVal,
|
||||
size_arg stringLen)
|
||||
{
|
||||
TRY {
|
||||
std::string val((const char*)stringVal, stringLen);
|
||||
valueAssign<pvd::PVString, pvd::PVStringArray>(self, val);
|
||||
return 1;
|
||||
}CATCH()
|
||||
}
|
||||
|
||||
int jtree_start_map(void * ctx)
|
||||
{
|
||||
TRY {
|
||||
assert(!self->stack.empty());
|
||||
|
||||
context::frame& back = self->stack.back();
|
||||
pvd::Type type = back.fld->getField()->getType();
|
||||
if(type==pvd::structure) {
|
||||
// will fill in
|
||||
} else if(type==pvd::structureArray) {
|
||||
// starting new element in structure array
|
||||
pvd::PVStructureArray* sarr(static_cast<pvd::PVStructureArray*>(back.fld.get()));
|
||||
|
||||
pvd::PVStructurePtr elem(pvd::getPVDataCreate()->createPVStructure(sarr->getStructureArray()->getStructure()));
|
||||
|
||||
self->stack.push_back(context::frame(elem, 0));
|
||||
} else {
|
||||
throw std::runtime_error("Can't map (sub)structure");
|
||||
}
|
||||
|
||||
assert(self->stack.back().fld->getField()->getType()==pvd::structure);
|
||||
return 1;
|
||||
}CATCH()
|
||||
}
|
||||
|
||||
int jtree_map_key(void * ctx, const unsigned char * key,
|
||||
size_arg stringLen)
|
||||
{
|
||||
TRY {
|
||||
assert(!self->stack.empty());
|
||||
std::string name((const char*)key, stringLen);
|
||||
|
||||
// start_map() ensures we have a structure at the top of the stack
|
||||
pvd::PVStructure *fld = static_cast<pvd::PVStructure*>(self->stack.back().fld.get());
|
||||
|
||||
try {
|
||||
self->stack.push_back(context::frame(fld->getSubFieldT(name), self->stack.back().assigned));
|
||||
}catch(std::runtime_error& e){
|
||||
std::ostringstream strm;
|
||||
strm<<"At "<<fld->getFullName()<<" : "<<e.what()<<"\n";
|
||||
throw std::runtime_error(strm.str());
|
||||
}
|
||||
|
||||
return 1;
|
||||
}CATCH()
|
||||
}
|
||||
|
||||
int jtree_end_map(void * ctx)
|
||||
{
|
||||
TRY {
|
||||
assert(!self->stack.empty());
|
||||
assert(self->stack.back().fld->getField()->getType()==pvd::structure);
|
||||
|
||||
context::frame elem(self->stack.back());
|
||||
self->stack.pop_back();
|
||||
|
||||
if(!self->stack.empty() && self->stack.back().fld->getField()->getType()==pvd::structureArray) {
|
||||
// append element to struct array
|
||||
pvd::PVStructureArray *sarr = static_cast<pvd::PVStructureArray*>(self->stack.back().fld.get());
|
||||
|
||||
pvd::PVStructureArray::const_svector cval;
|
||||
sarr->swap(cval);
|
||||
|
||||
pvd::PVStructureArray::svector val(pvd::thaw(cval));
|
||||
|
||||
val.push_back(std::tr1::static_pointer_cast<pvd::PVStructure>(elem.fld));
|
||||
|
||||
sarr->replace(pvd::freeze(val));
|
||||
}
|
||||
|
||||
return 1;
|
||||
}CATCH()
|
||||
}
|
||||
|
||||
int jtree_start_array(void * ctx)
|
||||
{
|
||||
TRY {
|
||||
assert(!self->stack.empty());
|
||||
pvd::PVFieldPtr& back(self->stack.back().fld);
|
||||
pvd::Type type = back->getField()->getType();
|
||||
if(type!=pvd::structureArray && type!=pvd::scalarArray)
|
||||
throw std::runtime_error("Can't assign array");
|
||||
|
||||
return 1;
|
||||
}CATCH()
|
||||
}
|
||||
int jtree_end_array(void * ctx)
|
||||
{
|
||||
TRY {
|
||||
assert(!self->stack.empty());
|
||||
|
||||
if(self->stack.back().assigned)
|
||||
self->stack.back().assigned->set(self->stack.back().fld->getFieldOffset());
|
||||
self->stack.pop_back();
|
||||
return 1;
|
||||
}CATCH()
|
||||
}
|
||||
|
||||
|
||||
yajl_callbacks jtree_cbs = {
|
||||
&jtree_null,
|
||||
&jtree_boolean,
|
||||
&jtree_integer,
|
||||
&jtree_double,
|
||||
NULL, // number
|
||||
&jtree_string,
|
||||
&jtree_start_map,
|
||||
&jtree_map_key,
|
||||
&jtree_end_map,
|
||||
&jtree_start_array,
|
||||
&jtree_end_array,
|
||||
};
|
||||
|
||||
struct handler {
|
||||
yajl_handle handle;
|
||||
handler(yajl_handle handle) :handle(handle)
|
||||
{
|
||||
if(!handle)
|
||||
throw std::runtime_error("Failed to allocate yajl handle");
|
||||
}
|
||||
~handler() {
|
||||
yajl_free(handle);
|
||||
}
|
||||
operator yajl_handle() { return handle; }
|
||||
};
|
||||
|
||||
struct noop {
|
||||
void operator()(pvd::PVField*) {}
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace epics{namespace pvData{
|
||||
|
||||
epicsShareFunc
|
||||
void parseJSON(std::istream& strm,
|
||||
PVField& dest,
|
||||
BitSet *assigned)
|
||||
{
|
||||
#ifndef EPICS_YAJL_VERSION
|
||||
yajl_parser_config conf;
|
||||
memset(&conf, 0, sizeof(conf));
|
||||
conf.allowComments = 1;
|
||||
conf.checkUTF8 = 1;
|
||||
#endif
|
||||
|
||||
// we won't create refs to 'dest' which presist beyond this call.
|
||||
// however, it is convienent to treat 'dest' in the same manner as
|
||||
// any union/structureArray memebers it may contain.
|
||||
PVFieldPtr fakedest(&dest, noop());
|
||||
|
||||
context ctxt(fakedest, assigned);
|
||||
|
||||
#ifndef EPICS_YAJL_VERSION
|
||||
handler handle(yajl_alloc(&jtree_cbs, &conf, NULL, &ctxt));
|
||||
#else
|
||||
handler handle(yajl_alloc(&jtree_cbs, NULL, &ctxt));
|
||||
|
||||
yajl_config(handle, yajl_allow_comments, 1);
|
||||
#endif
|
||||
|
||||
|
||||
if(!yajl_parse_helper(strm, handle))
|
||||
throw std::runtime_error(ctxt.msg);
|
||||
|
||||
if(!ctxt.stack.empty())
|
||||
throw std::logic_error("field stack not empty");
|
||||
assert(fakedest.use_count()==1);
|
||||
}
|
||||
|
||||
}} // namespace epics::pvData
|
||||
|
||||
#endif // EPICS_VERSION_INT
|
||||
|
||||
236
src/json/print.cpp
Normal file
236
src/json/print.cpp
Normal file
@@ -0,0 +1,236 @@
|
||||
/*
|
||||
* Copyright information and license terms for this software can be
|
||||
* found in the file LICENSE that is included with the distribution
|
||||
*/
|
||||
|
||||
#include <vector>
|
||||
#include <sstream>
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include <pv/pvdVersion.h>
|
||||
#include <pv/pvData.h>
|
||||
#include <pv/valueBuilder.h>
|
||||
#include <pv/bitSet.h>
|
||||
|
||||
#if EPICS_VERSION_INT>=VERSION_INT(3,15,0,1)
|
||||
|
||||
#include "pv/json.h"
|
||||
|
||||
namespace pvd = epics::pvData;
|
||||
|
||||
namespace {
|
||||
|
||||
struct args {
|
||||
std::ostream& strm;
|
||||
const pvd::JSONPrintOptions& opts;
|
||||
|
||||
unsigned indent;
|
||||
|
||||
args(std::ostream& strm,
|
||||
const pvd::JSONPrintOptions& opts)
|
||||
:strm(strm)
|
||||
,opts(opts)
|
||||
,indent(opts.indent)
|
||||
{}
|
||||
|
||||
void doIntent() {
|
||||
if(!opts.multiLine) return;
|
||||
strm.put('\n');
|
||||
unsigned i=indent;
|
||||
while(i--) strm.put(' ');
|
||||
}
|
||||
};
|
||||
|
||||
void show_field(args& A, const pvd::PVField* fld, const pvd::BitSet *mask);
|
||||
|
||||
void show_struct(args& A, const pvd::PVStructure* fld, const pvd::BitSet *mask)
|
||||
{
|
||||
const pvd::StructureConstPtr& type = fld->getStructure();
|
||||
const pvd::PVFieldPtrArray& children = fld->getPVFields();
|
||||
|
||||
const pvd::StringArray& names = type->getFieldNames();
|
||||
|
||||
A.strm.put('{');
|
||||
A.indent++;
|
||||
|
||||
bool first = true;
|
||||
for(size_t i=0, N=names.size(); i<N; i++)
|
||||
{
|
||||
if(mask && !mask->get(children[i]->getFieldOffset())) continue;
|
||||
|
||||
if(first)
|
||||
first = false;
|
||||
else
|
||||
A.strm.put(',');
|
||||
A.doIntent();
|
||||
A.strm<<'\"'<<names[i]<<"\": ";
|
||||
show_field(A, children[i].get(), mask);
|
||||
}
|
||||
|
||||
A.indent--;
|
||||
A.doIntent();
|
||||
A.strm.put('}');
|
||||
}
|
||||
|
||||
void show_field(args& A, const pvd::PVField* fld, const pvd::BitSet *mask)
|
||||
{
|
||||
switch(fld->getField()->getType())
|
||||
{
|
||||
case pvd::scalar:
|
||||
{
|
||||
const pvd::PVScalar *scalar=static_cast<const pvd::PVScalar*>(fld);
|
||||
if(scalar->getScalar()->getScalarType()==pvd::pvString) {
|
||||
A.strm<<'\"'<<scalar->getAs<std::string>()<<'\"';
|
||||
} else {
|
||||
A.strm<<scalar->getAs<std::string>();
|
||||
}
|
||||
}
|
||||
return;
|
||||
case pvd::scalarArray:
|
||||
{
|
||||
const pvd::PVScalarArray *scalar=static_cast<const pvd::PVScalarArray*>(fld);
|
||||
const bool isstring = scalar->getScalarArray()->getElementType()==pvd::pvString;
|
||||
|
||||
pvd::shared_vector<const void> arr;
|
||||
scalar->getAs<void>(arr);
|
||||
|
||||
pvd::shared_vector<const std::string> sarr(pvd::shared_vector_convert<const std::string>(arr));
|
||||
|
||||
A.strm.put('[');
|
||||
for(size_t i=0, N=sarr.size(); i<N; i++) {
|
||||
if(i!=0)
|
||||
A.strm.put(',');
|
||||
if(isstring)
|
||||
A.strm.put('\"');
|
||||
A.strm<<sarr[i];
|
||||
if(isstring)
|
||||
A.strm.put('\"');
|
||||
}
|
||||
A.strm.put(']');
|
||||
}
|
||||
return;
|
||||
case pvd::structure:
|
||||
show_struct(A, static_cast<const pvd::PVStructure*>(fld), mask);
|
||||
return;
|
||||
case pvd::structureArray:
|
||||
{
|
||||
pvd::PVStructureArray::const_svector arr(static_cast<const pvd::PVStructureArray*>(fld)->view());
|
||||
A.strm.put('[');
|
||||
A.indent++;
|
||||
|
||||
for(size_t i=0, N=arr.size(); i<N; i++) {
|
||||
if(i!=0)
|
||||
A.strm.put(',');
|
||||
A.doIntent();
|
||||
if(arr[i])
|
||||
show_struct(A, arr[i].get(), 0);
|
||||
else
|
||||
A.strm<<"NULL";
|
||||
}
|
||||
|
||||
A.indent--;
|
||||
A.doIntent();
|
||||
A.strm.put(']');
|
||||
}
|
||||
return;
|
||||
case pvd::union_:
|
||||
{
|
||||
const pvd::PVUnion *U=static_cast<const pvd::PVUnion*>(fld);
|
||||
const pvd::PVField::const_shared_pointer& C(U->get());
|
||||
|
||||
if(!C) {
|
||||
A.strm<<"null";
|
||||
} else {
|
||||
show_field(A, C.get(), 0);
|
||||
}
|
||||
}
|
||||
return;
|
||||
case pvd::unionArray: {
|
||||
const pvd::PVUnionArray *U=static_cast<const pvd::PVUnionArray*>(fld);
|
||||
pvd::PVUnionArray::const_svector arr(U->view());
|
||||
A.strm.put('[');
|
||||
A.indent++;
|
||||
|
||||
for(size_t i=0, N=arr.size(); i<N; i++) {
|
||||
if(i!=0)
|
||||
A.strm.put(',');
|
||||
A.doIntent();
|
||||
if(arr[i])
|
||||
show_field(A, arr[i].get(), 0);
|
||||
else
|
||||
A.strm<<"NULL";
|
||||
}
|
||||
|
||||
A.indent--;
|
||||
A.doIntent();
|
||||
A.strm.put(']');
|
||||
|
||||
}
|
||||
return;
|
||||
}
|
||||
// should not be reached
|
||||
if(A.opts.ignoreUnprintable)
|
||||
A.strm<<"// unprintable field type";
|
||||
else
|
||||
throw std::runtime_error("Encountered unprintable field type");
|
||||
}
|
||||
|
||||
void expandBS(const pvd::PVStructure& top, pvd::BitSet& mask, bool parents) {
|
||||
if(mask.get(0)) { // special handling because getSubField(0) not allowed
|
||||
// wildcard
|
||||
for(size_t idx=1, N=top.getNumberFields(); idx<N; idx++) {
|
||||
mask.set(idx);
|
||||
}
|
||||
|
||||
} else {
|
||||
for(pvd::int32 idx = mask.nextSetBit(0), N=top.getNumberFields(); idx>=0 && idx<N; idx=mask.nextSetBit(idx+1)) {
|
||||
pvd::PVField::const_shared_pointer fld = top.getSubFieldT(idx);
|
||||
|
||||
// look forward and mark all children
|
||||
for(size_t i=idx+1, N=fld->getNextFieldOffset(); i<N; i++)
|
||||
mask.set(i);
|
||||
|
||||
if(parents) {
|
||||
// look back and mark all parents
|
||||
// we've already stepped past all parents so siblings will not be automatically marked
|
||||
for(const pvd::PVStructure *parent = fld->getParent(); parent; parent = parent->getParent()) {
|
||||
mask.set(parent->getFieldOffset());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace epics{namespace pvData{
|
||||
|
||||
JSONPrintOptions::JSONPrintOptions()
|
||||
:multiLine(true)
|
||||
,ignoreUnprintable(true)
|
||||
,indent(0)
|
||||
{}
|
||||
|
||||
void printJSON(std::ostream& strm,
|
||||
const PVStructure& val,
|
||||
const BitSet& mask,
|
||||
const JSONPrintOptions& opts)
|
||||
{
|
||||
args A(strm, opts);
|
||||
pvd::BitSet emask(mask);
|
||||
expandBS(val, emask, true);
|
||||
if(!emask.get(0)) return;
|
||||
show_struct(A, &val, &emask);
|
||||
}
|
||||
|
||||
void printJSON(std::ostream& strm,
|
||||
const PVField& val,
|
||||
const JSONPrintOptions& opts)
|
||||
{
|
||||
args A(strm, opts);
|
||||
show_field(A, &val, 0);
|
||||
}
|
||||
|
||||
}} // namespace epics::pvData
|
||||
|
||||
#endif // EPICS_VERSION_INT
|
||||
154
src/json/pv/json.h
Normal file
154
src/json/pv/json.h
Normal file
@@ -0,0 +1,154 @@
|
||||
/*
|
||||
* Copyright information and license terms for this software can be
|
||||
* found in the file LICENSE that is included with the distribution
|
||||
*/
|
||||
#ifndef PV_JSON_H
|
||||
#define PV_JSON_H
|
||||
|
||||
#include <istream>
|
||||
#include <ostream>
|
||||
#include <string>
|
||||
#include <map>
|
||||
|
||||
#include <pv/pvdVersion.h>
|
||||
#include <pv/pvData.h>
|
||||
|
||||
#if EPICS_VERSION_INT>=VERSION_INT(3,15,0,1)
|
||||
|
||||
#ifdef epicsExportSharedSymbols
|
||||
# define pvjson_epicsExportSharedSymbols
|
||||
# undef epicsExportSharedSymbols
|
||||
#endif
|
||||
|
||||
#include <yajl_parse.h>
|
||||
|
||||
#ifdef pvjson_epicsExportSharedSymbols
|
||||
# define epicsExportSharedSymbols
|
||||
# include "shareLib.h"
|
||||
#endif
|
||||
|
||||
#include <shareLib.h>
|
||||
|
||||
namespace epics{namespace pvData{
|
||||
|
||||
class BitSet;
|
||||
|
||||
/** @defgroup pvjson JSON print/parse
|
||||
*
|
||||
* Printing PVField as JSON and parsing JSON into PVField.
|
||||
*
|
||||
* @{
|
||||
*/
|
||||
|
||||
//! Options used during printing
|
||||
struct epicsShareClass JSONPrintOptions
|
||||
{
|
||||
bool multiLine; //!< include new lines
|
||||
bool ignoreUnprintable;//!< ignore union/union array when encountered
|
||||
unsigned indent; //!< Initial indentation (# of spaces)
|
||||
JSONPrintOptions();
|
||||
};
|
||||
|
||||
/** Print PVStructure as JSON
|
||||
*
|
||||
* 'mask' selects those fields which will be printed.
|
||||
* @version Overload added after 7.0.0
|
||||
*/
|
||||
epicsShareFunc
|
||||
void printJSON(std::ostream& strm,
|
||||
const PVStructure& val,
|
||||
const BitSet& mask,
|
||||
const JSONPrintOptions& opts = JSONPrintOptions());
|
||||
|
||||
/** Print PVField as JSON
|
||||
* @version Overload added after 7.0.0
|
||||
*/
|
||||
epicsShareFunc
|
||||
void printJSON(std::ostream& strm,
|
||||
const PVField& val,
|
||||
const JSONPrintOptions& opts = JSONPrintOptions());
|
||||
|
||||
// To be deprecated in favor of previous form
|
||||
FORCE_INLINE
|
||||
void printJSON(std::ostream& strm,
|
||||
const PVField::const_shared_pointer& val,
|
||||
const JSONPrintOptions& opts = JSONPrintOptions())
|
||||
{
|
||||
printJSON(strm, *val, opts);
|
||||
}
|
||||
|
||||
/** Parse JSON text into a PVStructure
|
||||
*
|
||||
* Restrictions:
|
||||
*
|
||||
* - Top level must be {} dict/object
|
||||
* - field values must be number, string, array, or dict/object
|
||||
* - array values must be number or string
|
||||
*/
|
||||
epicsShareFunc
|
||||
PVStructure::shared_pointer parseJSON(std::istream& strm);
|
||||
|
||||
/** Parse JSON and store into the provided PVStructure.
|
||||
*
|
||||
* Restrictions:
|
||||
*
|
||||
* - array of union not supported
|
||||
* - Only scalar value assigned to union
|
||||
*
|
||||
* @param strm Read JSON text from stream
|
||||
* @param dest Store in fields of this structure
|
||||
* @param assigned Which fields of _dest_ were assigned. (Optional)
|
||||
* @throws std::runtime_error on failure. dest and assigned may be modified.
|
||||
* @version Overload added after 7.0.0
|
||||
*/
|
||||
epicsShareFunc
|
||||
void parseJSON(std::istream& strm,
|
||||
PVField& dest,
|
||||
BitSet *assigned=0);
|
||||
|
||||
// To be deprecated in favor of previous form
|
||||
FORCE_INLINE
|
||||
void parseJSON(std::istream& strm,
|
||||
const PVField::shared_pointer& dest,
|
||||
BitSet *assigned=0)
|
||||
{
|
||||
parseJSON(strm, *dest, assigned);
|
||||
}
|
||||
|
||||
|
||||
/** Wrapper around yajl_parse()
|
||||
*
|
||||
* Parse entire input stream.
|
||||
* Errors if extranious non-whitespace found after the point were parsing completes.
|
||||
*
|
||||
* @param src The stream from which input charactors are read
|
||||
* @param handle A parser handle previously allocated with yajl_alloc(). Not free'd on success or failure.
|
||||
*
|
||||
* @returns true if parsing completes successfully. false if parsing cancelled by callback. throws other errors
|
||||
*
|
||||
* @note The form of this call depends on EPICS_YAJL_VERSION
|
||||
*/
|
||||
epicsShareFunc
|
||||
bool yajl_parse_helper(std::istream& src,
|
||||
yajl_handle handle);
|
||||
|
||||
namespace yajl {
|
||||
// undef implies API version 0
|
||||
#ifndef EPICS_YAJL_VERSION
|
||||
typedef long integer_arg;
|
||||
typedef unsigned size_arg;
|
||||
#else
|
||||
typedef long long integer_arg;
|
||||
typedef size_t size_arg;
|
||||
#endif
|
||||
} // namespace epics::pvData::yajl
|
||||
|
||||
/** @} */
|
||||
|
||||
}} // namespace epics::pvData
|
||||
|
||||
#else
|
||||
# error JSON parser requires EPICS Base >= 3.15.0.1
|
||||
#endif // EPICS_VERSION_INT
|
||||
|
||||
#endif // PV_JSON_H
|
||||
@@ -2,43 +2,40 @@
|
||||
|
||||
SRC_DIRS += $(PVDATA_SRC)/misc
|
||||
|
||||
INC += noDefaultMethods.h
|
||||
INC += lock.h
|
||||
INC += requester.h
|
||||
INC += serialize.h
|
||||
INC += bitSet.h
|
||||
INC += byteBuffer.h
|
||||
INC += epicsException.h
|
||||
INC += serializeHelper.h
|
||||
INC += event.h
|
||||
INC += thread.h
|
||||
INC += executor.h
|
||||
INC += timeFunction.h
|
||||
INC += timer.h
|
||||
INC += queue.h
|
||||
INC += messageQueue.h
|
||||
INC += destroyable.h
|
||||
INC += status.h
|
||||
INC += sharedPtr.h
|
||||
INC += localStaticLock.h
|
||||
INC += typeCast.h
|
||||
INC += printer.h
|
||||
INC += sharedVector.h
|
||||
INC += templateMeta.h
|
||||
INC += current_function.h
|
||||
INC += pv/noDefaultMethods.h
|
||||
INC += pv/lock.h
|
||||
INC += pv/serialize.h
|
||||
INC += pv/bitSet.h
|
||||
INC += pv/byteBuffer.h
|
||||
INC += pv/epicsException.h
|
||||
INC += pv/serializeHelper.h
|
||||
INC += pv/event.h
|
||||
INC += pv/thread.h
|
||||
INC += pv/timer.h
|
||||
INC += pv/status.h
|
||||
INC += pv/sharedPtr.h
|
||||
INC += pv/debugPtr.h
|
||||
INC += pv/localStaticLock.h
|
||||
INC += pv/typeCast.h
|
||||
INC += pv/sharedVector.h
|
||||
INC += pv/templateMeta.h
|
||||
INC += pv/current_function.h
|
||||
INC += pv/pvUnitTest.h
|
||||
INC += pv/reftrack.h
|
||||
INC += pv/anyscalar.h
|
||||
|
||||
LIBSRCS += byteBuffer.cpp
|
||||
LIBSRCS += bitSet.cpp
|
||||
LIBSRCS += epicsException.cpp
|
||||
LIBSRCS += requester.cpp
|
||||
LIBSRCS += serializeHelper.cpp
|
||||
LIBSRCS += event.cpp
|
||||
LIBSRCS += executor.cpp
|
||||
LIBSRCS += timeFunction.cpp
|
||||
LIBSRCS += timer.cpp
|
||||
LIBSRCS += status.cpp
|
||||
LIBSRCS += messageQueue.cpp
|
||||
LIBSRCS += localStaticLock.cpp
|
||||
LIBSRCS += typeCast.cpp
|
||||
LIBSRCS += thread.cpp
|
||||
LIBSRCS += parseToPOD.cpp
|
||||
|
||||
LIBSRCS += pvUnitTest.cpp
|
||||
LIBSRCS += debugPtr.cpp
|
||||
LIBSRCS += reftrack.cpp
|
||||
LIBSRCS += anyscalar.cpp
|
||||
|
||||
161
src/misc/anyscalar.cpp
Normal file
161
src/misc/anyscalar.cpp
Normal file
@@ -0,0 +1,161 @@
|
||||
|
||||
#include <epicsAssert.h>
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include <shareLib.h>
|
||||
|
||||
#include "pv/anyscalar.h"
|
||||
|
||||
namespace epics {namespace pvData {
|
||||
|
||||
AnyScalar::AnyScalar(ScalarType type, const void *buf)
|
||||
{
|
||||
if(type==pvString) {
|
||||
new (_wrap.blob) std::string(*static_cast<const std::string*>(buf));
|
||||
|
||||
} else {
|
||||
memcpy(_wrap.blob, buf, ScalarTypeFunc::elementSize(type));
|
||||
}
|
||||
|
||||
_stype = type;
|
||||
}
|
||||
|
||||
AnyScalar::AnyScalar(const AnyScalar& o)
|
||||
:_stype(o._stype)
|
||||
{
|
||||
if(o._stype==pvString) {
|
||||
new (_wrap.blob) std::string(o._as<std::string>());
|
||||
} else if(o._stype!=(ScalarType)-1) {
|
||||
memcpy(_wrap.blob, o._wrap.blob, sizeof(_largest_blob));
|
||||
}
|
||||
}
|
||||
|
||||
#if __cplusplus>=201103L
|
||||
AnyScalar::AnyScalar(AnyScalar&& o)
|
||||
:_stype(o._stype)
|
||||
{
|
||||
typedef std::string string;
|
||||
if(o._stype==pvString) {
|
||||
new (_wrap.blob) std::string();
|
||||
_as<std::string>() = std::move(o._as<std::string>());
|
||||
o._as<string>().~string();
|
||||
} else if(o._stype!=(ScalarType)-1) {
|
||||
memcpy(_wrap.blob, o._wrap.blob, sizeof(_largest_blob));
|
||||
}
|
||||
o._stype = (ScalarType)-1;
|
||||
}
|
||||
#endif
|
||||
|
||||
void AnyScalar::clear() {
|
||||
if(_stype==pvString) {
|
||||
typedef std::string string;
|
||||
_as<string>().~string();
|
||||
}
|
||||
// other types need no cleanup
|
||||
_stype = (ScalarType)-1;
|
||||
}
|
||||
|
||||
void AnyScalar::swap(AnyScalar& o) {
|
||||
typedef std::string string;
|
||||
switch((int)_stype) {
|
||||
case -1:
|
||||
switch((int)o._stype) {
|
||||
case -1:
|
||||
// nil <-> nil
|
||||
break;
|
||||
case pvString:
|
||||
// nil <-> string
|
||||
new (_wrap.blob) std::string();
|
||||
_as<std::string>().swap(o._as<std::string>());
|
||||
o._as<std::string>().~string();
|
||||
break;
|
||||
default:
|
||||
// nil <-> non-string
|
||||
memcpy(_wrap.blob, o._wrap.blob, sizeof(_largest_blob));
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case pvString:
|
||||
switch((int)o._stype) {
|
||||
case -1:
|
||||
// string <-> nil
|
||||
new (o._wrap.blob) std::string();
|
||||
_as<std::string>().swap(o._as<std::string>());
|
||||
_as<std::string>().~string();
|
||||
break;
|
||||
case pvString:
|
||||
// string <-> string
|
||||
_as<std::string>().swap(o._as<std::string>());
|
||||
break;
|
||||
default: {
|
||||
// string <-> non-string
|
||||
std::string temp;
|
||||
temp.swap(_as<std::string>());
|
||||
|
||||
_as<std::string>().~string();
|
||||
|
||||
memcpy(_wrap.blob, o._wrap.blob, sizeof(_largest_blob));
|
||||
|
||||
new (o._wrap.blob) std::string();
|
||||
temp.swap(o._as<std::string>());
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
switch((int)o._stype) {
|
||||
case -1:
|
||||
// non-string <-> nil
|
||||
memcpy(o._wrap.blob, _wrap.blob, sizeof(_largest_blob));
|
||||
break;
|
||||
case pvString: {
|
||||
// non-string <-> string
|
||||
std::string temp;
|
||||
temp.swap(o._as<std::string>());
|
||||
|
||||
o._as<std::string>().~string();
|
||||
|
||||
memcpy(o._wrap.blob, _wrap.blob, sizeof(_largest_blob));
|
||||
|
||||
new (_wrap.blob) std::string();
|
||||
temp.swap(_as<std::string>());
|
||||
}
|
||||
break;
|
||||
default:
|
||||
// non-string <-> non-string
|
||||
_largest_blob temp;
|
||||
memcpy(&temp, _wrap.blob, sizeof(_largest_blob));
|
||||
memcpy(_wrap.blob, o._wrap.blob, sizeof(_largest_blob));
|
||||
memcpy(o._wrap.blob, &temp, sizeof(_largest_blob));
|
||||
// std::swap(o._wrap.blob, _wrap.blob); // gcc <=4.3 doesn't like this
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
std::swap(_stype, o._stype);
|
||||
}
|
||||
const void* AnyScalar::bufferUnsafe() const {
|
||||
if(_stype==pvString) {
|
||||
return as<std::string>().c_str();
|
||||
} else {
|
||||
return _wrap.blob;
|
||||
}
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& strm, const AnyScalar& v)
|
||||
{
|
||||
switch(v.type()) {
|
||||
#define CASE(BASETYPE, PVATYPE, DBFTYPE, PVACODE) case pv ## PVACODE: strm<<v._as<PVATYPE>(); break;
|
||||
#define CASE_REAL_INT64
|
||||
#define CASE_STRING
|
||||
#include "pv/typemap.h"
|
||||
#undef CASE
|
||||
#undef CASE_REAL_INT64
|
||||
#undef CASE_STRING
|
||||
default:
|
||||
strm<<"(nil)"; break;
|
||||
}
|
||||
return strm;
|
||||
}
|
||||
|
||||
}} // namespace epics::pvData
|
||||
@@ -1,8 +1,7 @@
|
||||
/* bitSet.cpp */
|
||||
/**
|
||||
* Copyright - See the COPYRIGHT that is included with this distribution.
|
||||
* EPICS pvData is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
/*
|
||||
* Copyright information and license terms for this software can be
|
||||
* found in the file LICENSE that is included with the distribution
|
||||
*/
|
||||
/**
|
||||
* @author mes
|
||||
@@ -10,12 +9,38 @@
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <iostream>
|
||||
#include <stdexcept>
|
||||
#include <algorithm>
|
||||
|
||||
#include <epicsMutex.h>
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include <pv/lock.h>
|
||||
#include <pv/serializeHelper.h>
|
||||
#include <pv/bitSet.h>
|
||||
|
||||
/*
|
||||
* BitSets are packed into arrays of "words." Currently a word is
|
||||
* a long, which consists of 64 bits, requiring 6 address bits.
|
||||
* The choice of word size is determined purely by performance concerns.
|
||||
*/
|
||||
#define ADDRESS_BITS_PER_WORD 6u
|
||||
#define BITS_PER_WORD (1u << ADDRESS_BITS_PER_WORD)
|
||||
#define BYTES_PER_WORD sizeof(uint64)
|
||||
#define BIT_INDEX_MASK (BITS_PER_WORD - 1u)
|
||||
|
||||
/** Used to shift left or right for a partial word mask */
|
||||
#define WORD_MASK ~((uint64)0)
|
||||
|
||||
// index of work containing this bit
|
||||
#define WORD_INDEX(bitn) ((bitn)>>ADDRESS_BITS_PER_WORD)
|
||||
// bit offset within word
|
||||
#define WORD_OFFSET(bitn) ((bitn)&BIT_INDEX_MASK)
|
||||
|
||||
// the words vector should be size()d as small as posible,
|
||||
// so the last word should always have a bit set when the set is not empty
|
||||
#define CHECK_POST() assert(words.empty() || words.back()!=0)
|
||||
|
||||
namespace epics { namespace pvData {
|
||||
|
||||
BitSet::shared_pointer BitSet::create(uint32 nbits)
|
||||
@@ -23,90 +48,76 @@ namespace epics { namespace pvData {
|
||||
return BitSet::shared_pointer(new BitSet(nbits));
|
||||
}
|
||||
|
||||
BitSet::BitSet() : words(0), wordsLength(0), wordsInUse(0) {
|
||||
initWords(BITS_PER_WORD);
|
||||
BitSet::BitSet() {}
|
||||
|
||||
BitSet::BitSet(uint32 nbits)
|
||||
{
|
||||
words.reserve((nbits == 0) ? 1 : WORD_INDEX(nbits-1) + 1);
|
||||
}
|
||||
|
||||
BitSet::BitSet(uint32 nbits) : words(0), wordsLength(0), wordsInUse(0) {
|
||||
initWords(nbits);
|
||||
#if __cplusplus>=201103L
|
||||
BitSet::BitSet(std::initializer_list<uint32> I)
|
||||
{
|
||||
// optimistically guess that highest bit is last (not required)
|
||||
words.reserve((I.size() == 0) ? 1 : WORD_INDEX(*(I.end()-1)) + 1);
|
||||
|
||||
for(uint32 idx : I)
|
||||
{
|
||||
set(idx);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
BitSet::~BitSet() {
|
||||
delete[] words;
|
||||
}
|
||||
|
||||
void BitSet::initWords(uint32 nbits) {
|
||||
uint32 length = (nbits <= 0) ? 1 : wordIndex(nbits-1) + 1;
|
||||
if (words) delete[] words;
|
||||
words = new uint64[length];
|
||||
memset(words, 0, sizeof(uint64)*length);
|
||||
wordsLength = length;
|
||||
}
|
||||
BitSet::~BitSet() {}
|
||||
|
||||
void BitSet::recalculateWordsInUse() {
|
||||
// wordsInUse is unsigned
|
||||
if (wordsInUse == 0)
|
||||
return;
|
||||
|
||||
// Traverse the bitset until a used word is found
|
||||
int32 i;
|
||||
for (i = (int32)wordsInUse-1; i >= 0; i--)
|
||||
if (words[i] != 0)
|
||||
// step back from the end to find the first non-zero element
|
||||
size_t nsize = words.size();
|
||||
for(; nsize; nsize--) {
|
||||
if(words[nsize-1])
|
||||
break;
|
||||
|
||||
wordsInUse = i+1; // The new logical size
|
||||
}
|
||||
words.resize(nsize);
|
||||
CHECK_POST();
|
||||
}
|
||||
|
||||
void BitSet::ensureCapacity(uint32 wordsRequired) {
|
||||
if (wordsLength < wordsRequired) {
|
||||
|
||||
// create and copy
|
||||
uint64* newwords = new uint64[wordsRequired];
|
||||
memset(newwords, 0, sizeof(uint64)*wordsRequired);
|
||||
memcpy(newwords, words, sizeof(uint64)*wordsLength);
|
||||
if (words) delete[] words;
|
||||
words = newwords;
|
||||
wordsLength = wordsRequired;
|
||||
}
|
||||
words.resize(std::max(words.size(), (size_t)wordsRequired), 0);
|
||||
}
|
||||
|
||||
void BitSet::expandTo(uint32 wordIndex) {
|
||||
uint32 wordsRequired = wordIndex+1;
|
||||
if (wordsInUse < wordsRequired) {
|
||||
ensureCapacity(wordsRequired);
|
||||
wordsInUse = wordsRequired;
|
||||
ensureCapacity(wordIndex+1);
|
||||
}
|
||||
|
||||
BitSet& BitSet::flip(uint32 bitIndex) {
|
||||
|
||||
uint32 wordIdx = WORD_INDEX(bitIndex);
|
||||
expandTo(wordIdx);
|
||||
|
||||
words[wordIdx] ^= (((uint64)1) << WORD_OFFSET(bitIndex));
|
||||
|
||||
recalculateWordsInUse();
|
||||
return *this;
|
||||
}
|
||||
|
||||
BitSet& BitSet::set(uint32 bitIndex) {
|
||||
|
||||
uint32 wordIdx = WORD_INDEX(bitIndex);
|
||||
expandTo(wordIdx);
|
||||
|
||||
words[wordIdx] |= (((uint64)1) << WORD_OFFSET(bitIndex));
|
||||
return *this;
|
||||
}
|
||||
|
||||
BitSet& BitSet::clear(uint32 bitIndex) {
|
||||
|
||||
uint32 wordIdx = WORD_INDEX(bitIndex);
|
||||
if (wordIdx < words.size()) {
|
||||
words[wordIdx] &= ~(((uint64)1) << WORD_OFFSET(bitIndex));
|
||||
|
||||
recalculateWordsInUse();
|
||||
}
|
||||
}
|
||||
|
||||
void BitSet::flip(uint32 bitIndex) {
|
||||
|
||||
uint32 wordIdx = wordIndex(bitIndex);
|
||||
expandTo(wordIdx);
|
||||
|
||||
words[wordIdx] ^= (((uint64)1) << (bitIndex % BITS_PER_WORD));
|
||||
|
||||
recalculateWordsInUse();
|
||||
}
|
||||
|
||||
void BitSet::set(uint32 bitIndex) {
|
||||
|
||||
uint32 wordIdx = wordIndex(bitIndex);
|
||||
expandTo(wordIdx);
|
||||
|
||||
words[wordIdx] |= (((uint64)1) << (bitIndex % BITS_PER_WORD));
|
||||
}
|
||||
|
||||
void BitSet::clear(uint32 bitIndex) {
|
||||
|
||||
uint32 wordIdx = wordIndex(bitIndex);
|
||||
if (wordIdx >= wordsInUse)
|
||||
return;
|
||||
|
||||
words[wordIdx] &= ~(((uint64)1) << (bitIndex % BITS_PER_WORD));
|
||||
|
||||
recalculateWordsInUse();
|
||||
return *this;
|
||||
}
|
||||
|
||||
void BitSet::set(uint32 bitIndex, bool value) {
|
||||
@@ -117,14 +128,13 @@ namespace epics { namespace pvData {
|
||||
}
|
||||
|
||||
bool BitSet::get(uint32 bitIndex) const {
|
||||
uint32 wordIdx = wordIndex(bitIndex);
|
||||
return ((wordIdx < wordsInUse)
|
||||
&& ((words[wordIdx] & (((uint64)1) << (bitIndex % BITS_PER_WORD))) != 0));
|
||||
uint32 wordIdx = WORD_INDEX(bitIndex);
|
||||
return ((wordIdx < words.size())
|
||||
&& ((words[wordIdx] & (((uint64)1) << WORD_OFFSET(bitIndex))) != 0));
|
||||
}
|
||||
|
||||
void BitSet::clear() {
|
||||
while (wordsInUse > 0)
|
||||
words[--wordsInUse] = 0;
|
||||
words.clear();
|
||||
}
|
||||
|
||||
uint32 BitSet::numberOfTrailingZeros(uint64 i) {
|
||||
@@ -153,8 +163,8 @@ namespace epics { namespace pvData {
|
||||
|
||||
int32 BitSet::nextSetBit(uint32 fromIndex) const {
|
||||
|
||||
uint32 u = wordIndex(fromIndex);
|
||||
if (u >= wordsInUse)
|
||||
uint32 u = WORD_INDEX(fromIndex);
|
||||
if (u >= words.size())
|
||||
return -1;
|
||||
|
||||
uint64 word = words[u] & (WORD_MASK << (fromIndex % BITS_PER_WORD));
|
||||
@@ -162,7 +172,7 @@ namespace epics { namespace pvData {
|
||||
while (true) {
|
||||
if (word != 0)
|
||||
return (u * BITS_PER_WORD) + numberOfTrailingZeros(word);
|
||||
if (++u == wordsInUse)
|
||||
if (++u == words.size())
|
||||
return -1;
|
||||
word = words[u];
|
||||
}
|
||||
@@ -171,8 +181,8 @@ namespace epics { namespace pvData {
|
||||
int32 BitSet::nextClearBit(uint32 fromIndex) const {
|
||||
// Neither spec nor implementation handle bitsets of maximal length.
|
||||
|
||||
uint32 u = wordIndex(fromIndex);
|
||||
if (u >= wordsInUse)
|
||||
uint32 u = WORD_INDEX(fromIndex);
|
||||
if (u >= words.size())
|
||||
return fromIndex;
|
||||
|
||||
uint64 word = ~words[u] & (WORD_MASK << (fromIndex % BITS_PER_WORD));
|
||||
@@ -180,79 +190,77 @@ namespace epics { namespace pvData {
|
||||
while (true) {
|
||||
if (word != 0)
|
||||
return (u * BITS_PER_WORD) + numberOfTrailingZeros(word);
|
||||
if (++u == wordsInUse)
|
||||
return wordsInUse * BITS_PER_WORD;
|
||||
if (++u == words.size())
|
||||
return words.size() * BITS_PER_WORD;
|
||||
word = ~words[u];
|
||||
}
|
||||
}
|
||||
|
||||
bool BitSet::isEmpty() const {
|
||||
return (wordsInUse == 0);
|
||||
return words.empty();
|
||||
}
|
||||
|
||||
uint32 BitSet::cardinality() const {
|
||||
uint32 sum = 0;
|
||||
for (uint32 i = 0; i < wordsInUse; i++)
|
||||
for (uint32 i = 0; i < words.size(); i++)
|
||||
sum += bitCount(words[i]);
|
||||
return sum;
|
||||
}
|
||||
|
||||
uint32 BitSet::size() const {
|
||||
return wordsLength * BITS_PER_WORD;
|
||||
return words.size() * BITS_PER_WORD;
|
||||
}
|
||||
|
||||
bool BitSet::logical_and(const BitSet& set) const
|
||||
{
|
||||
size_t nwords = std::min(words.size(), set.words.size());
|
||||
for(size_t i=0; i<nwords; i++) {
|
||||
if(words[i] & set.words[i])
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool BitSet::logical_or(const BitSet& set) const
|
||||
{
|
||||
return !words.empty() || !set.words.empty();
|
||||
}
|
||||
|
||||
BitSet& BitSet::operator&=(const BitSet& set) {
|
||||
// Check for self-assignment!
|
||||
if (this == &set) return *this;
|
||||
|
||||
while (wordsInUse > set.wordsInUse)
|
||||
words[--wordsInUse] = 0;
|
||||
// the result length will be <= the shorter of the two inputs
|
||||
words.resize(std::min(words.size(), set.words.size()), 0);
|
||||
|
||||
// Perform logical AND on words in common
|
||||
for (uint32 i = 0; i < wordsInUse; i++)
|
||||
for(size_t i=0, e=words.size(); i<e; i++)
|
||||
words[i] &= set.words[i];
|
||||
|
||||
recalculateWordsInUse();
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
BitSet& BitSet::operator|=(const BitSet& set) {
|
||||
// Check for self-assignment!
|
||||
if (this == &set) return *this;
|
||||
uint32 wordsInCommon = wordsInUse;
|
||||
if(wordsInUse>set.wordsInUse) wordsInCommon = set.wordsInUse;
|
||||
if (wordsInUse < set.wordsInUse) {
|
||||
ensureCapacity(set.wordsInUse);
|
||||
wordsInUse = set.wordsInUse;
|
||||
}
|
||||
// Perform logical OR on words in common
|
||||
for (uint32 i =0; i < wordsInCommon; i++) {
|
||||
|
||||
// result length will be the same as the longer of the two inputs
|
||||
words.resize(std::max(words.size(), set.words.size()), 0);
|
||||
|
||||
// since we expand w/ zeros, then iterate using the size of the other vector
|
||||
for(size_t i=0, e=set.words.size(); i<e; i++)
|
||||
words[i] |= set.words[i];
|
||||
}
|
||||
// Copy any remaining words
|
||||
for(uint32 i=wordsInCommon; i<set.wordsInUse; ++i) {
|
||||
words[i] = set.words[i];
|
||||
}
|
||||
// recalculateWordsInUse() is not needed
|
||||
|
||||
CHECK_POST();
|
||||
return *this;
|
||||
}
|
||||
|
||||
BitSet& BitSet::operator^=(const BitSet& set) {
|
||||
uint32 wordsInCommon = wordsInUse;
|
||||
if(wordsInUse>set.wordsInUse) wordsInCommon = set.wordsInUse;
|
||||
if (wordsInUse < set.wordsInUse) {
|
||||
ensureCapacity(set.wordsInUse);
|
||||
wordsInUse = set.wordsInUse;
|
||||
}
|
||||
// Perform logical OR on words in common
|
||||
for (uint32 i =0; i < wordsInCommon; i++) {
|
||||
// result length will <= the longer of the two inputs
|
||||
words.resize(std::max(words.size(), set.words.size()), 0);
|
||||
|
||||
for(size_t i=0, e=set.words.size(); i<e; i++)
|
||||
words[i] ^= set.words[i];
|
||||
}
|
||||
// Copy any remaining words
|
||||
for(uint32 i=wordsInCommon; i<set.wordsInUse; ++i) {
|
||||
words[i] = set.words[i];
|
||||
}
|
||||
|
||||
recalculateWordsInUse();
|
||||
return *this;
|
||||
}
|
||||
@@ -260,32 +268,27 @@ namespace epics { namespace pvData {
|
||||
|
||||
BitSet& BitSet::operator=(const BitSet &set) {
|
||||
// Check for self-assignment!
|
||||
if (this == &set) return *this;
|
||||
|
||||
// we ensure that words array size is adequate (and not wordsInUse to ensure capacity to the future)
|
||||
if (wordsLength < set.wordsLength)
|
||||
{
|
||||
if (words) delete[] words;
|
||||
words = new uint64[set.wordsLength];
|
||||
wordsLength = set.wordsLength;
|
||||
if (this != &set) {
|
||||
words = set.words;
|
||||
}
|
||||
memcpy(words, set.words, sizeof(uint64)*set.wordsInUse);
|
||||
wordsInUse = set.wordsInUse;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
void BitSet::or_and(const BitSet& set1, const BitSet& set2) {
|
||||
uint32 inUse = (set1.wordsInUse < set2.wordsInUse) ? set1.wordsInUse : set2.wordsInUse;
|
||||
void BitSet::swap(BitSet& set)
|
||||
{
|
||||
words.swap(set.words);
|
||||
}
|
||||
|
||||
ensureCapacity(inUse);
|
||||
wordsInUse = inUse;
|
||||
void BitSet::or_and(const BitSet& set1, const BitSet& set2) {
|
||||
|
||||
const size_t andlen = std::min(set1.words.size(), set2.words.size());
|
||||
words.resize(std::max(words.size(), andlen), 0);
|
||||
|
||||
// Perform logical AND on words in common
|
||||
for (uint32 i = 0; i < inUse; i++)
|
||||
for (uint32 i = 0; i < andlen; i++)
|
||||
words[i] |= (set1.words[i] & set2.words[i]);
|
||||
|
||||
// recalculateWordsInUse()...
|
||||
recalculateWordsInUse();
|
||||
}
|
||||
|
||||
bool BitSet::operator==(const BitSet &set) const
|
||||
@@ -293,11 +296,11 @@ namespace epics { namespace pvData {
|
||||
if (this == &set)
|
||||
return true;
|
||||
|
||||
if (wordsInUse != set.wordsInUse)
|
||||
if (words.size() != set.words.size())
|
||||
return false;
|
||||
|
||||
// Check words in use by both BitSets
|
||||
for (uint32 i = 0; i < wordsInUse; i++)
|
||||
for (uint32 i = 0; i < words.size(); i++)
|
||||
if (words[i] != set.words[i])
|
||||
return false;
|
||||
|
||||
@@ -310,56 +313,55 @@ namespace epics { namespace pvData {
|
||||
}
|
||||
|
||||
void BitSet::serialize(ByteBuffer* buffer, SerializableControl* flusher) const {
|
||||
|
||||
uint32 n = wordsInUse;
|
||||
|
||||
uint32 n = words.size();
|
||||
if (n == 0) {
|
||||
SerializeHelper::writeSize(0, buffer, flusher);
|
||||
return;
|
||||
}
|
||||
uint32 len = 8 * (n-1);
|
||||
uint32 len = BYTES_PER_WORD * (n-1); // length excluding bits in the last word
|
||||
// count non-zero bytes in the last word
|
||||
for (uint64 x = words[n - 1]; x != 0; x >>= 8)
|
||||
len++;
|
||||
|
||||
|
||||
SerializeHelper::writeSize(len, buffer, flusher);
|
||||
flusher->ensureBuffer(len);
|
||||
|
||||
for (uint32 i = 0; i < n - 1; i++)
|
||||
|
||||
n = len / 8;
|
||||
for (uint32 i = 0; i < n; i++)
|
||||
buffer->putLong(words[i]);
|
||||
|
||||
for (uint64 x = words[n - 1]; x != 0; x >>= 8)
|
||||
buffer->putByte((int8) (x & 0xff));
|
||||
|
||||
if (n < words.size())
|
||||
for (uint64 x = words[words.size() - 1]; x != 0; x >>= 8)
|
||||
buffer->putByte((int8) (x & 0xff));
|
||||
}
|
||||
|
||||
|
||||
void BitSet::deserialize(ByteBuffer* buffer, DeserializableControl* control) {
|
||||
|
||||
|
||||
uint32 bytes = static_cast<uint32>(SerializeHelper::readSize(buffer, control)); // in bytes
|
||||
|
||||
wordsInUse = (bytes + 7) / 8;
|
||||
if (wordsInUse > wordsLength)
|
||||
{
|
||||
if (words) delete[] words;
|
||||
words = new uint64[wordsInUse];
|
||||
wordsLength = wordsInUse;
|
||||
}
|
||||
|
||||
|
||||
size_t wordsInUse = (bytes + 7) / BYTES_PER_WORD;
|
||||
words.resize(wordsInUse);
|
||||
|
||||
if (wordsInUse == 0)
|
||||
return;
|
||||
|
||||
|
||||
control->ensureData(bytes);
|
||||
|
||||
|
||||
uint32 i = 0;
|
||||
uint32 longs = bytes / 8;
|
||||
while (i < longs)
|
||||
words[i++] = buffer->getLong();
|
||||
|
||||
|
||||
for (uint32 j = i; j < wordsInUse; j++)
|
||||
words[j] = 0;
|
||||
|
||||
|
||||
for (uint32 remaining = (bytes - longs * 8), j = 0; j < remaining; j++)
|
||||
words[i] |= (buffer->getByte() & 0xffL) << (8 * j);
|
||||
|
||||
words[i] |= (buffer->getByte() & 0xffLL) << (8 * j);
|
||||
|
||||
recalculateWordsInUse(); // Sender shouldn't add extra zero bytes, but don't fail it it does
|
||||
}
|
||||
|
||||
|
||||
epicsShareExtern std::ostream& operator<<(std::ostream& o, const BitSet& b)
|
||||
{
|
||||
o << '{';
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
/**
|
||||
* Copyright - See the COPYRIGHT that is included with this distribution.
|
||||
* EPICS pvData is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
/*
|
||||
* Copyright information and license terms for this software can be
|
||||
* found in the file LICENSE that is included with the distribution
|
||||
*/
|
||||
/**
|
||||
* @author mse
|
||||
*/
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include <pv/byteBuffer.h>
|
||||
|
||||
@@ -1,934 +0,0 @@
|
||||
/* byteBuffer.h */
|
||||
/**
|
||||
* Copyright - See the COPYRIGHT that is included with this distribution.
|
||||
* EPICS pvData is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
*/
|
||||
/**
|
||||
* @author mse
|
||||
*/
|
||||
#ifndef BYTEBUFFER_H
|
||||
#define BYTEBUFFER_H
|
||||
|
||||
#include <string>
|
||||
#include <string.h>
|
||||
|
||||
#ifdef epicsExportSharedSymbols
|
||||
#define byteBufferepicsExportSharedSymbols
|
||||
#undef epicsExportSharedSymbols
|
||||
#endif
|
||||
|
||||
#include <epicsEndian.h>
|
||||
|
||||
#ifdef byteBufferepicsExportSharedSymbols
|
||||
#define epicsExportSharedSymbols
|
||||
#undef byteBufferepicsExportSharedSymbols
|
||||
#endif
|
||||
|
||||
#include <pv/pvType.h>
|
||||
#include <pv/epicsException.h>
|
||||
|
||||
#include <shareLib.h>
|
||||
|
||||
namespace epics {
|
||||
namespace pvData {
|
||||
|
||||
/*
|
||||
TODO can be used:
|
||||
|
||||
MS Visual C++:
|
||||
|
||||
You include intrin.h and call the following functions:
|
||||
|
||||
For 16 bit numbers:
|
||||
unsigned short _byteswap_ushort(unsigned short value);
|
||||
|
||||
For 32 bit numbers:
|
||||
unsigned long _byteswap_ulong(unsigned long value);
|
||||
|
||||
For 64 bit numbers:
|
||||
unsigned __int64 _byteswap_uint64(unsigned __int64 value);
|
||||
*/
|
||||
|
||||
/*
|
||||
For floats and doubles it's more difficult as with plain integers as these may or not may be in the host machines byte-order.
|
||||
You can get little-endian floats on big-endian machines and vice versa.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
#define GCC_VERSION_SINCE(major, minor, patchlevel) \
|
||||
(defined(__GNUC__) && !defined(__INTEL_COMPILER) && \
|
||||
((__GNUC__ > (major)) || \
|
||||
(__GNUC__ == (major) && __GNUC_MINOR__ > (minor)) || \
|
||||
(__GNUC__ == (major) && __GNUC_MINOR__ == (minor) && __GNUC_PATCHLEVEL__ >= (patchlevel))))
|
||||
|
||||
|
||||
#if GCC_VERSION_SINCE(4,3,0)
|
||||
|
||||
#define swap32(x) __builtin_bswap32(x)
|
||||
#define swap64(x) __builtin_bswap64(x)
|
||||
|
||||
#define __byte_swap16(x) \
|
||||
(((x) >> 8) | \
|
||||
((x) << 8))
|
||||
|
||||
static inline uint16_t
|
||||
swap16(uint16_t _x)
|
||||
{
|
||||
return (__byte_swap16(_x));
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
|
||||
#define __byte_swap16(x) \
|
||||
(((x) >> 8) | \
|
||||
((x) << 8))
|
||||
|
||||
#define __byte_swap32(x) \
|
||||
((((x) & 0xff000000) >> 24) | \
|
||||
(((x) & 0x00ff0000) >> 8) | \
|
||||
(((x) & 0x0000ff00) << 8) | \
|
||||
(((x) & 0x000000ff) << 24))
|
||||
|
||||
#define __byte_swap64(x) \
|
||||
(((x) >> 56) | \
|
||||
(((x) >> 40) & 0xff00) | \
|
||||
(((x) >> 24) & 0xff0000) | \
|
||||
(((x) >> 8) & 0xff000000) | \
|
||||
(((x) << 8) & ((uint64_t)0xff << 32)) | \
|
||||
(((x) << 24) & ((uint64_t)0xff << 40)) | \
|
||||
(((x) << 40) & ((uint64_t)0xff << 48)) | \
|
||||
(((x) << 56)))
|
||||
|
||||
static inline uint16_t
|
||||
swap16(uint16_t _x)
|
||||
{
|
||||
return (__byte_swap16(_x));
|
||||
}
|
||||
|
||||
static inline uint32_t
|
||||
swap32(uint32_t _x)
|
||||
{
|
||||
return (__byte_swap32(_x));
|
||||
}
|
||||
|
||||
static inline uint64_t
|
||||
swap64(uint64_t _x)
|
||||
{
|
||||
return (__byte_swap64(_x));
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
template<typename T>
|
||||
inline T swap(T val) { return val; } // not valid
|
||||
|
||||
template<>
|
||||
inline int16 swap(int16 val)
|
||||
{
|
||||
return swap16(val);
|
||||
}
|
||||
|
||||
template<>
|
||||
inline int32 swap(int32 val)
|
||||
{
|
||||
return swap32(val);
|
||||
}
|
||||
|
||||
template<>
|
||||
inline int64 swap(int64 val)
|
||||
{
|
||||
return swap64(val);
|
||||
}
|
||||
|
||||
template<>
|
||||
inline float swap(float val)
|
||||
{
|
||||
union {
|
||||
int32 i;
|
||||
float f;
|
||||
} conv;
|
||||
conv.f = val;
|
||||
conv.i = swap32(conv.i);
|
||||
return conv.f;
|
||||
}
|
||||
|
||||
template<>
|
||||
inline double swap(double val)
|
||||
{
|
||||
union {
|
||||
int64 i;
|
||||
double d;
|
||||
} conv;
|
||||
conv.d = val;
|
||||
conv.i = swap64(conv.i);
|
||||
return conv.d;
|
||||
}
|
||||
|
||||
#define is_aligned(POINTER, BYTE_COUNT) \
|
||||
(((std::ptrdiff_t)(const void *)(POINTER)) % (BYTE_COUNT) == 0)
|
||||
|
||||
/*template <bool ENDIANESS_SUPPORT = false,
|
||||
bool UNALIGNED_ACCESS = false,
|
||||
bool ADAPTIVE_ACCESS = true,
|
||||
bool USE_INLINE_MEMCPY = true>*/
|
||||
|
||||
#define ENDIANESS_SUPPORT true
|
||||
#define UNALIGNED_ACCESS true
|
||||
#define ADAPTIVE_ACCESS true
|
||||
#define USE_INLINE_MEMCPY true
|
||||
|
||||
#if defined (__GNUC__) && (__GNUC__ < 3)
|
||||
#define GET(T) get((T*)0)
|
||||
#else
|
||||
#define GET(T) get<T>()
|
||||
#endif
|
||||
|
||||
/**
|
||||
* This class implements {@code Bytebuffer} that is like the {@code java.nio.ByteBuffer}.
|
||||
* <p>A {@code BitSet} is not safe for multithreaded use without
|
||||
* external synchronization.
|
||||
*
|
||||
* Based on Java implementation.
|
||||
*/
|
||||
class ByteBuffer
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param size The number of bytes.
|
||||
* @param byteOrder The byte order.
|
||||
* Must be one of EPICS_BYTE_ORDER,EPICS_ENDIAN_LITTLE,EPICS_ENDIAN_BIG.
|
||||
*/
|
||||
ByteBuffer(std::size_t size, int byteOrder = EPICS_BYTE_ORDER) :
|
||||
_buffer(0), _size(size),
|
||||
_reverseEndianess(byteOrder != EPICS_BYTE_ORDER),
|
||||
_reverseFloatEndianess(byteOrder != EPICS_FLOAT_WORD_ORDER),
|
||||
_wrapped(false)
|
||||
{
|
||||
_buffer = (char*)malloc(size);
|
||||
clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for wrapping existing buffers.
|
||||
* Given buffer will not be released by the ByteBuffer instance.
|
||||
* @param buffer Existing buffer.
|
||||
* @param size The number of bytes.
|
||||
* @param byteOrder The byte order.
|
||||
* Must be one of EPICS_BYTE_ORDER,EPICS_ENDIAN_LITTLE,EPICS_ENDIAN_BIG.
|
||||
*/
|
||||
ByteBuffer(char* buffer, std::size_t size, int byteOrder = EPICS_BYTE_ORDER) :
|
||||
_buffer(buffer), _size(size),
|
||||
_reverseEndianess(byteOrder != EPICS_BYTE_ORDER),
|
||||
_reverseFloatEndianess(byteOrder != EPICS_FLOAT_WORD_ORDER),
|
||||
_wrapped(true)
|
||||
{
|
||||
clear();
|
||||
}
|
||||
/**
|
||||
* Destructor
|
||||
*/
|
||||
~ByteBuffer()
|
||||
{
|
||||
if (_buffer && !_wrapped) free(_buffer);
|
||||
}
|
||||
/**
|
||||
* Set the byte order.
|
||||
*
|
||||
* @param byteOrder The byte order.
|
||||
* Must be one of EPICS_BYTE_ORDER,EPICS_ENDIAN_LITTLE,EPICS_ENDIAN_BIG,
|
||||
*/
|
||||
inline void setEndianess(int byteOrder)
|
||||
{
|
||||
_reverseEndianess = (byteOrder != EPICS_BYTE_ORDER);
|
||||
_reverseFloatEndianess = (byteOrder != EPICS_FLOAT_WORD_ORDER);
|
||||
}
|
||||
/**
|
||||
* Get the raw buffer data.
|
||||
* @return the raw buffer data.
|
||||
*/
|
||||
inline const char* getBuffer()
|
||||
{
|
||||
return _buffer;
|
||||
}
|
||||
/**
|
||||
* Makes a buffer ready for a new sequence of channel-read or relative put operations:
|
||||
* It sets the limit to the capacity and the position to zero.
|
||||
*/
|
||||
inline void clear()
|
||||
{
|
||||
_position = _buffer;
|
||||
_limit = _buffer + _size;
|
||||
}
|
||||
/**
|
||||
* Makes a buffer ready for a new sequence of channel-write or relative get operations:
|
||||
* It sets the limit to the current position and then sets the position to zero.
|
||||
*/
|
||||
inline void flip() {
|
||||
_limit = _position;
|
||||
_position = _buffer;
|
||||
}
|
||||
/**
|
||||
* Makes a buffer ready for re-reading the data that it already contains:
|
||||
* It leaves the limit unchanged and sets the position to zero.
|
||||
*/
|
||||
inline void rewind() {
|
||||
_position = _buffer;
|
||||
}
|
||||
/**
|
||||
* Returns the current position.
|
||||
* @return The current position in the raw data.
|
||||
*/
|
||||
inline std::size_t getPosition()
|
||||
{
|
||||
return (std::size_t)(((std::ptrdiff_t)(const void *)_position) - ((std::ptrdiff_t)(const void *)_buffer));
|
||||
}
|
||||
/**
|
||||
* Sets the buffer position.
|
||||
* If the mark is defined and larger than the new position then it is discarded.
|
||||
*
|
||||
* @param pos The offset into the raw buffer.
|
||||
* The new position value; must be no larger than the current limit
|
||||
*/
|
||||
inline void setPosition(std::size_t pos)
|
||||
{
|
||||
_position = _buffer + pos;
|
||||
}
|
||||
/**
|
||||
* Returns this buffer's limit.
|
||||
*
|
||||
* @return The offset into the raw buffer.
|
||||
*/
|
||||
inline std::size_t getLimit()
|
||||
{
|
||||
return (std::size_t)(((std::ptrdiff_t)(const void *)_limit) - ((std::ptrdiff_t)(const void *)_buffer));
|
||||
}
|
||||
/**
|
||||
* Sets this buffer's limit.
|
||||
* If the position is larger than the new limit then it is set to the new limit.s
|
||||
* If the mark is defined and larger than the new limit then it is discarded.
|
||||
*
|
||||
* @param limit The new position value;
|
||||
* must be no larger than the current limit
|
||||
*/
|
||||
inline void setLimit(std::size_t limit)
|
||||
{
|
||||
_limit = _buffer + limit;
|
||||
}
|
||||
/**
|
||||
* Returns the number of elements between the current position and the limit.
|
||||
*
|
||||
* @return The number of elements remaining in this buffer.
|
||||
*/
|
||||
inline std::size_t getRemaining()
|
||||
{
|
||||
return (std::size_t)(((std::ptrdiff_t)(const void *)_limit) - ((std::ptrdiff_t)(const void *)_position));
|
||||
}
|
||||
/**
|
||||
* Returns The size, i.e. capacity of the raw data buffer in bytes.
|
||||
*
|
||||
* @return The size of the raw data buffer.
|
||||
*/
|
||||
inline std::size_t getSize()
|
||||
{
|
||||
return _size;
|
||||
}
|
||||
/**
|
||||
* Put the value into the raw buffer as a byte stream in the current byte order.
|
||||
*
|
||||
* @param value The value to be put into the byte buffer.
|
||||
*/
|
||||
template<typename T>
|
||||
inline void put(T value);
|
||||
/**
|
||||
* Put the value into the raw buffer at the specified index as a byte stream in the current byte order.
|
||||
*
|
||||
* @param index Offset in the byte buffer.
|
||||
* @param value The value to be put into the byte buffer.
|
||||
*/
|
||||
template<typename T>
|
||||
inline void put(std::size_t index, T value);
|
||||
/**
|
||||
* Get the new object from the byte buffer. The item MUST have type {@code T}.
|
||||
* The position is adjusted based on the type.
|
||||
*
|
||||
* @return The object.
|
||||
*/
|
||||
#if defined (__GNUC__) && (__GNUC__ < 3)
|
||||
template<typename T>
|
||||
inline T get(const T*);
|
||||
#else
|
||||
template<typename T>
|
||||
inline T get();
|
||||
#endif
|
||||
/**
|
||||
* Get the new object from the byte buffer at the specified index.
|
||||
* The item MUST have type {@code T}.
|
||||
* The position is adjusted based on the type.
|
||||
*
|
||||
* @param index The location in the byte buffer.
|
||||
* @return The object.
|
||||
*/
|
||||
template<typename T>
|
||||
inline T get(std::size_t index);
|
||||
/**
|
||||
* Put a sub-array of bytes into the byte buffer.
|
||||
* The position is increased by the count.
|
||||
*
|
||||
* @param src The source array.
|
||||
* @param offset The starting position within src.
|
||||
* @param count The number of bytes to put into the byte buffer,
|
||||
*/
|
||||
inline void put(const char* src, std::size_t src_offset, std::size_t count) {
|
||||
//if(count>getRemaining()) THROW_BASE_EXCEPTION("buffer overflow");
|
||||
memcpy(_position, src + src_offset, count);
|
||||
_position += count;
|
||||
}
|
||||
/**
|
||||
* Get a sub-array of bytes from the byte buffer.
|
||||
* The position is increased by the count.
|
||||
*
|
||||
* @param dest The destination array.
|
||||
* @param offset The starting position within src.
|
||||
* @param count The number of bytes to put into the byte buffer,
|
||||
*/
|
||||
inline void get(char* dest, std::size_t dest_offset, std::size_t count) {
|
||||
//if(count>getRemaining()) THROW_BASE_EXCEPTION("buffer overflow");
|
||||
memcpy(dest + dest_offset, _position, count);
|
||||
_position += count;
|
||||
}
|
||||
/**
|
||||
* Put an array of type {@code T} into the byte buffer.
|
||||
* The position is adjusted.
|
||||
*
|
||||
* @param values The input array.
|
||||
* @param count The number of elements.
|
||||
*/
|
||||
template<typename T>
|
||||
inline void putArray(const T* values, std::size_t count);
|
||||
/**
|
||||
* Get an array of type {@code T} from the byte buffer.
|
||||
* The position is adjusted.
|
||||
*
|
||||
* @param values The destination array.
|
||||
* @param count The number of elements.
|
||||
*/
|
||||
template<typename T>
|
||||
inline void getArray(T* values, std::size_t count);
|
||||
/**
|
||||
* Is the byte order the EPICS_BYTE_ORDER
|
||||
* @return (false,true) if (is, is not) the EPICS_BYTE_ORDER
|
||||
*/
|
||||
template<typename T>
|
||||
inline bool reverse()
|
||||
{
|
||||
return _reverseEndianess;
|
||||
}
|
||||
/**
|
||||
* Adjust position so that it is aligned to the specified size.
|
||||
* Size MUST be a power of 2.
|
||||
* @param size The alignment requirement.
|
||||
*/
|
||||
inline void align(std::size_t size)
|
||||
{
|
||||
const std::size_t k = size - 1;
|
||||
_position = (char*)((((std::ptrdiff_t)(const void *)_position) + k) & ~(k));
|
||||
}
|
||||
/**
|
||||
* Put a boolean value into the byte buffer.
|
||||
*
|
||||
* @param value The value.
|
||||
*/
|
||||
inline void putBoolean( bool value) { put< int8>(value ? 1 : 0); }
|
||||
/**
|
||||
* Put a byte value into the byte buffer.
|
||||
*
|
||||
* @param value The value.
|
||||
*/
|
||||
inline void putByte ( int8 value) { put< int8>(value); }
|
||||
/**
|
||||
* Put a short value into the byte buffer.
|
||||
*
|
||||
* @param value The value.
|
||||
*/
|
||||
inline void putShort ( int16 value) { put< int16>(value); }
|
||||
/**
|
||||
* Put an int value into the byte buffer.
|
||||
*
|
||||
* @param value The value.
|
||||
*/
|
||||
inline void putInt ( int32 value) { put< int32>(value); }
|
||||
/**
|
||||
* Put a long value into the byte buffer.
|
||||
*
|
||||
* @param value The value.
|
||||
*/
|
||||
inline void putLong ( int64 value) { put< int64>(value); }
|
||||
/**
|
||||
* Put a float value into the byte buffer.
|
||||
*
|
||||
* @param value The value.
|
||||
*/
|
||||
inline void putFloat ( float value) { put< float>(value); }
|
||||
/**
|
||||
* Put a double value into the byte buffer.
|
||||
*
|
||||
* @param value The value.
|
||||
*/
|
||||
inline void putDouble (double value) { put<double>(value); }
|
||||
|
||||
/**
|
||||
* Put a boolean value into the byte buffer at the specified index.
|
||||
*
|
||||
* @param index The offset in the byte buffer,
|
||||
* @param value The value.
|
||||
*/
|
||||
inline void putBoolean(std::size_t index, bool value) { put< int8>(index, value); }
|
||||
/**
|
||||
* Put a byte value into the byte buffer at the specified index.
|
||||
*
|
||||
* @param index The offset in the byte buffer,
|
||||
* @param value The value.
|
||||
*/
|
||||
inline void putByte (std::size_t index, int8 value) { put< int8>(index, value); }
|
||||
/**
|
||||
* Put a short value into the byte buffer at the specified index.
|
||||
*
|
||||
* @param index The offset in the byte buffer,
|
||||
* @param value The value.
|
||||
*/
|
||||
inline void putShort (std::size_t index, int16 value) { put< int16>(index, value); }
|
||||
/**
|
||||
* Put an int value into the byte buffer at the specified index.
|
||||
*
|
||||
* @param index The offset in the byte buffer,
|
||||
* @param value The value.
|
||||
*/
|
||||
inline void putInt (std::size_t index, int32 value) { put< int32>(index, value); }
|
||||
/**
|
||||
* Put a long value into the byte buffer at the specified index.
|
||||
*
|
||||
* @param index The offset in the byte buffer,
|
||||
* @param value The value.
|
||||
*/
|
||||
inline void putLong (std::size_t index, int64 value) { put< int64>(index, value); }
|
||||
/**
|
||||
* Put a float value into the byte buffer at the specified index.
|
||||
*
|
||||
* @param index The offset in the byte buffer,
|
||||
* @param value The value.
|
||||
*/
|
||||
inline void putFloat (std::size_t index, float value) { put< float>(index, value); }
|
||||
/**
|
||||
* Put a double value into the byte buffer at the specified index.
|
||||
*
|
||||
* @param index The offset in the byte buffer,
|
||||
* @param value The value.
|
||||
*/
|
||||
inline void putDouble (std::size_t index, double value) { put<double>(index, value); }
|
||||
/**
|
||||
* Get a boolean value from the byte buffer.
|
||||
*
|
||||
* @return The value.
|
||||
*/
|
||||
inline bool getBoolean() { return GET( int8) != 0; }
|
||||
/**
|
||||
* Get a byte value from the byte buffer.
|
||||
*
|
||||
* @return The value.
|
||||
*/
|
||||
inline int8 getByte () { return GET( int8); }
|
||||
/**
|
||||
* Get a short value from the byte buffer.
|
||||
*
|
||||
* @return The value.
|
||||
*/
|
||||
inline int16 getShort () { return GET( int16); }
|
||||
/**
|
||||
* Get a int value from the byte buffer.
|
||||
*
|
||||
* @return The value.
|
||||
*/
|
||||
inline int32 getInt () { return GET( int32); }
|
||||
/**
|
||||
* Get a long value from the byte buffer.
|
||||
*
|
||||
* @return The value.
|
||||
*/
|
||||
inline int64 getLong () { return GET( int64); }
|
||||
/**
|
||||
* Get a float value from the byte buffer.
|
||||
*
|
||||
* @return The value.
|
||||
*/
|
||||
inline float getFloat () { return GET( float); }
|
||||
/**
|
||||
* Get a double value from the byte buffer.
|
||||
*
|
||||
* @return The value.
|
||||
*/
|
||||
inline double getDouble () { return GET(double); }
|
||||
/**
|
||||
* Get a boolean value from the byte buffer at the specified index.
|
||||
*
|
||||
* @param index The offset in the byte buffer.
|
||||
* @return The value.
|
||||
*/
|
||||
inline bool getBoolean(std::size_t index) { return get< int8>(index) != 0; }
|
||||
/**
|
||||
* Get a byte value from the byte buffer at the specified index.
|
||||
*
|
||||
* @param index The offset in the byte buffer.
|
||||
* @return The value.
|
||||
*/
|
||||
inline int8 getByte (std::size_t index) { return get< int8>(index); }
|
||||
/**
|
||||
* Get a short value from the byte buffer at the specified index.
|
||||
*
|
||||
* @param index The offset in the byte buffer.
|
||||
* @return The value.
|
||||
*/
|
||||
inline int16 getShort (std::size_t index) { return get< int16>(index); }
|
||||
/**
|
||||
* Get an int value from the byte buffer at the specified index.
|
||||
*
|
||||
* @param index The offset in the byte buffer.
|
||||
* @return The value.
|
||||
*/
|
||||
inline int32 getInt (std::size_t index) { return get< int32>(index); }
|
||||
/**
|
||||
* Get a long value from the byte buffer at the specified index.
|
||||
*
|
||||
* @param index The offset in the byte buffer.
|
||||
* @return The value.
|
||||
*/
|
||||
inline int64 getLong (std::size_t index) { return get< int64>(index); }
|
||||
/**
|
||||
* Get a float value from the byte buffer at the specified index.
|
||||
*
|
||||
* @param index The offset in the byte buffer.
|
||||
* @return The value.
|
||||
*/
|
||||
inline float getFloat (std::size_t index) { return get< float>(index); }
|
||||
/**
|
||||
* Get a boolean value from the byte buffer at the specified index.
|
||||
*
|
||||
* @param double The offset in the byte buffer.
|
||||
* @return The value.
|
||||
*/
|
||||
inline double getDouble (std::size_t index) { return get<double>(index); }
|
||||
|
||||
// TODO remove
|
||||
inline const char* getArray()
|
||||
{
|
||||
return _buffer;
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
char* _buffer;
|
||||
char* _position;
|
||||
char* _limit;
|
||||
std::size_t _size;
|
||||
bool _reverseEndianess;
|
||||
bool _reverseFloatEndianess;
|
||||
bool _wrapped;
|
||||
};
|
||||
|
||||
template<>
|
||||
inline bool ByteBuffer::reverse<bool>()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
template<>
|
||||
inline bool ByteBuffer::reverse<int8>()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
template<>
|
||||
inline bool ByteBuffer::reverse<uint8>()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
template<>
|
||||
inline bool ByteBuffer::reverse<float>()
|
||||
{
|
||||
return _reverseFloatEndianess;
|
||||
}
|
||||
|
||||
template<>
|
||||
inline bool ByteBuffer::reverse<double>()
|
||||
{
|
||||
return _reverseFloatEndianess;
|
||||
}
|
||||
|
||||
// the following methods must come after the specialized reverse<>() methods to make pre-gcc3 happy
|
||||
|
||||
template<typename T>
|
||||
inline void ByteBuffer::put(T value)
|
||||
{
|
||||
// this avoids int8 specialization, compiler will take care if optimization, -O2 or more
|
||||
if (sizeof(T) == 1)
|
||||
{
|
||||
*(_position++) = (int8)value;
|
||||
return;
|
||||
}
|
||||
|
||||
if (ENDIANESS_SUPPORT && reverse<T>())
|
||||
{
|
||||
value = swap<T>(value);
|
||||
}
|
||||
|
||||
if (UNALIGNED_ACCESS)
|
||||
{
|
||||
// NOTE: some CPU handle unaligned access pretty good (e.g. x86)
|
||||
*((T*)_position) = value;
|
||||
_position += sizeof(T);
|
||||
}
|
||||
else
|
||||
{
|
||||
// NOTE: this check and branching does not always payoff
|
||||
if (ADAPTIVE_ACCESS && is_aligned(_position, sizeof(T)))
|
||||
{
|
||||
*((T*)_position) = value;
|
||||
_position += sizeof(T);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (USE_INLINE_MEMCPY)
|
||||
{
|
||||
// NOTE: it turns out that this compiler can optimize this with inline code, e.g. gcc
|
||||
memcpy(_position, &value, sizeof(T));
|
||||
_position += sizeof(T);
|
||||
}
|
||||
else
|
||||
{
|
||||
// NOTE: compiler should optimize this and unroll the loop
|
||||
for (size_t i = 0; i < sizeof(T); i++)
|
||||
_position[i] = ((char*)&value)[i];
|
||||
_position += sizeof(T);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline void ByteBuffer::put(std::size_t index, T value)
|
||||
{
|
||||
// this avoids int8 specialization, compiler will take care if optimization, -O2 or more
|
||||
if (sizeof(T) == 1)
|
||||
{
|
||||
*(_buffer + index) = (int8)value;
|
||||
return;
|
||||
}
|
||||
|
||||
if (ENDIANESS_SUPPORT && reverse<T>())
|
||||
{
|
||||
value = swap<T>(value);
|
||||
}
|
||||
|
||||
if (UNALIGNED_ACCESS)
|
||||
{
|
||||
// NOTE: some CPU handle unaligned access preety good (e.g. x86)
|
||||
*((T*)(_buffer + index)) = value;
|
||||
}
|
||||
else
|
||||
{
|
||||
// NOTE: this check and branching does not always payoff
|
||||
if (ADAPTIVE_ACCESS && is_aligned(_position, sizeof(T)))
|
||||
{
|
||||
*((T*)(_buffer + index)) = value;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (USE_INLINE_MEMCPY)
|
||||
{
|
||||
// NOTE: it turns out that this compiler can optimize this with inline code, e.g. gcc
|
||||
memcpy(_buffer + index, &value, sizeof(T));
|
||||
}
|
||||
else
|
||||
{
|
||||
// NOTE: compiler should optimize this and unroll the loop
|
||||
char *p = _buffer + index;
|
||||
for (size_t i = 0; i < sizeof(T); i++)
|
||||
p[i] = ((char*)&value)[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#if defined (__GNUC__) && (__GNUC__ < 3)
|
||||
template<typename T>
|
||||
inline T ByteBuffer::get(const T*)
|
||||
#else
|
||||
template<typename T>
|
||||
inline T ByteBuffer::get()
|
||||
#endif
|
||||
{
|
||||
// this avoids int8 specialization, compiler will take care if optimization, -O2 or more
|
||||
if (sizeof(T) == 1)
|
||||
{
|
||||
return (int8)(*(_position++));
|
||||
}
|
||||
|
||||
|
||||
T value;
|
||||
|
||||
if (UNALIGNED_ACCESS)
|
||||
{
|
||||
// NOTE: some CPU handle unaligned access preety good (e.g. x86)
|
||||
value = *((T*)_position);
|
||||
_position += sizeof(T);
|
||||
}
|
||||
else
|
||||
{
|
||||
// NOTE: this check and branching does not always payoff
|
||||
if (ADAPTIVE_ACCESS && is_aligned(_position, sizeof(T)))
|
||||
{
|
||||
value = *((T*)_position);
|
||||
_position += sizeof(T);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (USE_INLINE_MEMCPY)
|
||||
{
|
||||
// NOTE: it turns out that this compiler can optimize this with inline code, e.g. gcc
|
||||
memcpy(&value, _position, sizeof(T));
|
||||
_position += sizeof(T);
|
||||
}
|
||||
else
|
||||
{
|
||||
// NOTE: compiler should optimize this and unroll the loop
|
||||
for (size_t i = 0; i < sizeof(T); i++)
|
||||
((char*)&value)[i] = _position[i];
|
||||
_position += sizeof(T);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ENDIANESS_SUPPORT && reverse<T>())
|
||||
{
|
||||
value = swap<T>(value);
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline T ByteBuffer::get(std::size_t index)
|
||||
{
|
||||
// this avoids int8 specialization, compiler will take care if optimization, -O2 or more
|
||||
if (sizeof(T) == 1)
|
||||
{
|
||||
return (int8)(*(_buffer + index));
|
||||
}
|
||||
|
||||
|
||||
T value;
|
||||
|
||||
if (UNALIGNED_ACCESS)
|
||||
{
|
||||
// NOTE: some CPU handle unaligned access preety good (e.g. x86)
|
||||
value = *((T*)(_buffer + index));
|
||||
}
|
||||
else
|
||||
{
|
||||
// NOTE: this check and branching does not always payoff
|
||||
if (ADAPTIVE_ACCESS && is_aligned(_position, sizeof(T)))
|
||||
{
|
||||
value = *((T*)(_buffer + index));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (USE_INLINE_MEMCPY)
|
||||
{
|
||||
// NOTE: it turns out that this compiler can optimize this with inline code, e.g. gcc
|
||||
memcpy(&value, _buffer + index, sizeof(T));
|
||||
}
|
||||
else
|
||||
{
|
||||
// NOTE: compiler should optimize this and unroll the loop
|
||||
char* p = _buffer + index;
|
||||
for (size_t i = 0; i < sizeof(T); i++)
|
||||
((char*)&value)[i] = p[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ENDIANESS_SUPPORT && reverse<T>())
|
||||
{
|
||||
value = swap<T>(value);
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline void ByteBuffer::putArray(const T* values, std::size_t count)
|
||||
{
|
||||
// this avoids int8 specialization, compiler will take care if optimization, -O2 or more
|
||||
if (sizeof(T) == 1)
|
||||
{
|
||||
put((const char*)values, 0, count);
|
||||
return;
|
||||
}
|
||||
|
||||
T* start = (T*)_position;
|
||||
|
||||
size_t n = sizeof(T)*count;
|
||||
// we require aligned arrays...
|
||||
memcpy(_position, values, n);
|
||||
_position += n;
|
||||
|
||||
// ... so that we can be fast changing endianess
|
||||
if (ENDIANESS_SUPPORT && reverse<T>())
|
||||
{
|
||||
for (std::size_t i = 0; i < count; i++)
|
||||
{
|
||||
*start = swap<T>(*start);
|
||||
start++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline void ByteBuffer::getArray(T* values, std::size_t count)
|
||||
{
|
||||
// this avoids int8 specialization, compiler will take care if optimization, -O2 or more
|
||||
if (sizeof(T) == 1)
|
||||
{
|
||||
get((char*)values, 0, count);
|
||||
return;
|
||||
}
|
||||
|
||||
T* start = (T*)values;
|
||||
|
||||
size_t n = sizeof(T)*count;
|
||||
// we require aligned arrays...
|
||||
memcpy(values, _position, n);
|
||||
_position += n;
|
||||
|
||||
// ... so that we can be fast changing endianess
|
||||
if (ENDIANESS_SUPPORT && reverse<T>())
|
||||
{
|
||||
for (std::size_t i = 0; i < count; i++)
|
||||
{
|
||||
*start = swap<T>(*start);
|
||||
start++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
#endif /* BYTEBUFFER_H */
|
||||
165
src/misc/debugPtr.cpp
Normal file
165
src/misc/debugPtr.cpp
Normal file
@@ -0,0 +1,165 @@
|
||||
/*
|
||||
* Copyright information and license terms for this software can be
|
||||
* found in the file LICENSE that is included with the distribution
|
||||
*/
|
||||
|
||||
#if __cplusplus>=201103L
|
||||
|
||||
#include <epicsMutex.h>
|
||||
#include <epicsGuard.h>
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include <pv/debugPtr.h>
|
||||
|
||||
namespace {
|
||||
typedef epicsGuard<epicsMutex> Guard;
|
||||
}
|
||||
|
||||
namespace epics {
|
||||
namespace debug {
|
||||
|
||||
// joins together a group of ptr_base instances
|
||||
// which all have the same dtor
|
||||
struct tracker {
|
||||
epicsMutex mutex;
|
||||
ptr_base::ref_set_t refs;
|
||||
};
|
||||
|
||||
void shared_ptr_base::track_new()
|
||||
{
|
||||
if(track) {
|
||||
Guard G(track->mutex);
|
||||
track->refs.insert(this);
|
||||
}
|
||||
snap_stack();
|
||||
}
|
||||
|
||||
// create new tracker if ptr!=nullptr, otherwise clear
|
||||
void shared_ptr_base::track_new(void* ptr)
|
||||
{
|
||||
track_clear();
|
||||
if(ptr){
|
||||
track.reset(new tracker);
|
||||
Guard G(track->mutex);
|
||||
track->refs.insert(this);
|
||||
}
|
||||
snap_stack();
|
||||
}
|
||||
|
||||
void shared_ptr_base::track_assign(const shared_ptr_base &o)
|
||||
{
|
||||
if(track!=o.track) {
|
||||
track_clear();
|
||||
track = o.track;
|
||||
if(track) {
|
||||
Guard G(track->mutex);
|
||||
track->refs.insert(this);
|
||||
}
|
||||
snap_stack();
|
||||
}
|
||||
}
|
||||
|
||||
void shared_ptr_base::track_clear()
|
||||
{
|
||||
if(track) {
|
||||
Guard G(track->mutex);
|
||||
track->refs.erase(this);
|
||||
}
|
||||
track.reset();
|
||||
#ifndef EXCEPT_USE_NONE
|
||||
m_depth = 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
void shared_ptr_base::swap(shared_ptr_base &o)
|
||||
{
|
||||
// we cheat a bit here to avoid lock order, and to lock only twice
|
||||
if(track) {
|
||||
Guard G(track->mutex);
|
||||
track->refs.insert(&o);
|
||||
track->refs.erase(this);
|
||||
}
|
||||
track.swap(o.track);
|
||||
if(track) {
|
||||
Guard G(track->mutex);
|
||||
track->refs.insert(this);
|
||||
track->refs.erase(&o);
|
||||
}
|
||||
//TODO: keep original somehow???
|
||||
snap_stack();
|
||||
o.snap_stack();
|
||||
}
|
||||
|
||||
void shared_ptr_base::snap_stack()
|
||||
{
|
||||
if(!track) {
|
||||
#ifndef EXCEPT_USE_NONE
|
||||
m_depth = 0;
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
#if defined(EXCEPT_USE_BACKTRACE)
|
||||
{
|
||||
m_depth=backtrace(m_stack,EXCEPT_DEPTH);
|
||||
}
|
||||
#else
|
||||
{}
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
void shared_ptr_base::show_stack(std::ostream& strm) const
|
||||
{
|
||||
strm<<"ptr "<<this;
|
||||
#ifndef EXCEPT_USE_NONE
|
||||
if(m_depth<=0) return;
|
||||
#endif
|
||||
#if 0 && defined(EXCEPT_USE_BACKTRACE)
|
||||
{
|
||||
|
||||
char **symbols=backtrace_symbols(m_stack, m_depth);
|
||||
|
||||
strm<<": ";
|
||||
for(int i=0; i<m_depth; i++) {
|
||||
strm<<symbols[i]<<", ";
|
||||
}
|
||||
|
||||
std::free(symbols);
|
||||
}
|
||||
#elif !defined(EXCEPT_USE_NONE)
|
||||
{
|
||||
strm<<": ";
|
||||
for(int i=0; i<m_depth; i++) {
|
||||
strm<<std::hex<<m_stack[i]<<" ";
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
void ptr_base::show_refs(std::ostream& strm, bool self, bool weak) const
|
||||
{
|
||||
if(!track) {
|
||||
strm<<"# No refs\n";
|
||||
} else {
|
||||
Guard G(track->mutex);
|
||||
for(auto ref : track->refs) {
|
||||
if(!self && ref==this) continue;
|
||||
strm<<'#';
|
||||
ref->show_stack(strm);
|
||||
strm<<'\n';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ptr_base::spy_refs(ref_set_t &refs) const
|
||||
{
|
||||
if(track) {
|
||||
Guard G(track->mutex);
|
||||
refs.insert(track->refs.begin(), track->refs.end());
|
||||
}
|
||||
}
|
||||
|
||||
}} // namespace epics::debug
|
||||
|
||||
#endif // __cplusplus>=201103L
|
||||
@@ -1,40 +0,0 @@
|
||||
/* destroyable.h */
|
||||
/**
|
||||
* Copyright - See the COPYRIGHT that is included with this distribution.
|
||||
* EPICS pvData is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
*/
|
||||
/**
|
||||
* @author mse
|
||||
*/
|
||||
#ifndef DESTROYABLE_H
|
||||
#define DESTROYABLE_H
|
||||
|
||||
#include <pv/sharedPtr.h>
|
||||
|
||||
#include <shareLib.h>
|
||||
|
||||
namespace epics { namespace pvData {
|
||||
|
||||
|
||||
/**
|
||||
* Instance declaring destroy method.
|
||||
* @author mse
|
||||
*/
|
||||
class epicsShareClass Destroyable {
|
||||
public:
|
||||
POINTER_DEFINITIONS(Destroyable);
|
||||
/**
|
||||
* Destroy this instance.
|
||||
*/
|
||||
virtual void destroy() = 0;
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Do not allow delete on this instance and derived classes, destroy() must be used instead.
|
||||
*/
|
||||
virtual ~Destroyable() {};
|
||||
};
|
||||
|
||||
}}
|
||||
#endif /* DESTROYABLE_H */
|
||||
@@ -1,7 +1,6 @@
|
||||
/**
|
||||
* Copyright - See the COPYRIGHT that is included with this distribution.
|
||||
* EPICS pvData is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
/*
|
||||
* Copyright information and license terms for this software can be
|
||||
* found in the file LICENSE that is included with the distribution
|
||||
*/
|
||||
/**
|
||||
* @author mes
|
||||
@@ -10,6 +9,7 @@
|
||||
#include <sstream>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <cstdlib>
|
||||
#include <string>
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
@@ -50,7 +50,7 @@ ExceptionMixin::show() const
|
||||
out<<symbols[i]<<"\n";
|
||||
}
|
||||
|
||||
free(symbols);
|
||||
std::free(symbols);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
/* event.cpp */
|
||||
/**
|
||||
* Copyright - See the COPYRIGHT that is included with this distribution.
|
||||
* EPICS pvData is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
/*
|
||||
* Copyright information and license terms for this software can be
|
||||
* found in the file LICENSE that is included with the distribution
|
||||
*/
|
||||
/**
|
||||
* @author mrk
|
||||
@@ -38,8 +37,7 @@ Event::~Event() {
|
||||
|
||||
|
||||
Event::Event(bool full)
|
||||
: id(epicsEventCreate(full?epicsEventFull : epicsEventEmpty)),
|
||||
alreadyOn("already on list")
|
||||
: id(epicsEventCreate(full?epicsEventFull : epicsEventEmpty))
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@@ -1,53 +0,0 @@
|
||||
/* event.h */
|
||||
/**
|
||||
* Copyright - See the COPYRIGHT that is included with this distribution.
|
||||
* EPICS pvData is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
*/
|
||||
/**
|
||||
* @author mrk
|
||||
*/
|
||||
#ifndef EVENT_H
|
||||
#define EVENT_H
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#ifdef epicsExportSharedSymbols
|
||||
#define eventepicsExportSharedSymbols
|
||||
#undef epicsExportSharedSymbols
|
||||
#endif
|
||||
|
||||
#include <epicsEvent.h>
|
||||
|
||||
#ifdef eventepicsExportSharedSymbols
|
||||
#define epicsExportSharedSymbols
|
||||
#undef eventepicsExportSharedSymbols
|
||||
#endif
|
||||
|
||||
#include <pv/pvType.h>
|
||||
#include <pv/sharedPtr.h>
|
||||
|
||||
#include <shareLib.h>
|
||||
|
||||
namespace epics { namespace pvData {
|
||||
|
||||
class Event;
|
||||
typedef std::tr1::shared_ptr<Event> EventPtr;
|
||||
|
||||
class epicsShareClass Event {
|
||||
public:
|
||||
POINTER_DEFINITIONS(Event);
|
||||
explicit Event(bool = false);
|
||||
~Event();
|
||||
void signal();
|
||||
bool wait (); /* blocks until full */
|
||||
bool wait ( double timeOut ); /* false if empty at time out */
|
||||
bool tryWait (); /* false if empty */
|
||||
private:
|
||||
epicsEventId id;
|
||||
std::string alreadyOn;
|
||||
};
|
||||
|
||||
}}
|
||||
#endif /* EVENT_H */
|
||||
@@ -1,95 +0,0 @@
|
||||
/* executor.cpp */
|
||||
/**
|
||||
* Copyright - See the COPYRIGHT that is included with this distribution.
|
||||
* EPICS pvData is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
*/
|
||||
/**
|
||||
* @author mrk
|
||||
*/
|
||||
#include <cstddef>
|
||||
#include <cstdlib>
|
||||
#include <cstddef>
|
||||
#include <string>
|
||||
#include <cstdio>
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include <pv/executor.h>
|
||||
|
||||
using std::string;
|
||||
|
||||
namespace epics { namespace pvData {
|
||||
|
||||
// special instance to stop the executor thread
|
||||
class ExecutorShutdown : public Command {
|
||||
virtual void command();
|
||||
};
|
||||
|
||||
void ExecutorShutdown::command()
|
||||
{
|
||||
}
|
||||
|
||||
static
|
||||
std::tr1::shared_ptr<Command> shutdown(new ExecutorShutdown());
|
||||
|
||||
|
||||
Executor::Executor(string const & threadName,ThreadPriority priority)
|
||||
: thread(threadName,priority,this)
|
||||
{
|
||||
}
|
||||
|
||||
Executor::~Executor()
|
||||
{
|
||||
execute(shutdown);
|
||||
stopped.wait();
|
||||
// The thread signals 'stopped' while still holding
|
||||
// the lock. By taking it we wait for the run() function
|
||||
// to actually return
|
||||
Lock xx(mutex);
|
||||
head.reset();
|
||||
tail.reset();
|
||||
}
|
||||
|
||||
void Executor::run()
|
||||
{
|
||||
Lock xx(mutex);
|
||||
while(true) {
|
||||
while(!head.get()) {
|
||||
xx.unlock();
|
||||
moreWork.wait();
|
||||
xx.lock();
|
||||
}
|
||||
CommandPtr command = head;
|
||||
head = command->next;
|
||||
if(!command.get()) continue;
|
||||
if(command.get()==shutdown.get()) break;
|
||||
xx.unlock();
|
||||
try {
|
||||
command->command();
|
||||
}catch(std::exception& e){
|
||||
//TODO: feed into logging mechanism
|
||||
fprintf(stderr, "Executor: Unhandled exception: %s",e.what());
|
||||
}catch(...){
|
||||
fprintf(stderr, "Executor: Unhandled exception");
|
||||
}
|
||||
|
||||
xx.lock();
|
||||
}
|
||||
stopped.signal();
|
||||
}
|
||||
|
||||
void Executor::execute(CommandPtr const & command)
|
||||
{
|
||||
Lock xx(mutex);
|
||||
command->next.reset();
|
||||
if(!head.get()) {
|
||||
head = command;
|
||||
moreWork.signal();
|
||||
return;
|
||||
}
|
||||
CommandPtr tail = head;
|
||||
while(tail->next) tail = tail->next;
|
||||
tail->next = command;
|
||||
}
|
||||
|
||||
}}
|
||||
@@ -1,57 +0,0 @@
|
||||
/* executor.h */
|
||||
/**
|
||||
* Copyright - See the COPYRIGHT that is included with this distribution.
|
||||
* EPICS pvData is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
*/
|
||||
/**
|
||||
* @author mrk
|
||||
*/
|
||||
#ifndef EXECUTOR_H
|
||||
#define EXECUTOR_H
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include <pv/pvType.h>
|
||||
#include <pv/lock.h>
|
||||
#include <pv/event.h>
|
||||
#include <pv/thread.h>
|
||||
#include <pv/sharedPtr.h>
|
||||
|
||||
#include <shareLib.h>
|
||||
|
||||
namespace epics { namespace pvData {
|
||||
|
||||
class Command;
|
||||
class Executor;
|
||||
typedef std::tr1::shared_ptr<Command> CommandPtr;
|
||||
typedef std::tr1::shared_ptr<Executor> ExecutorPtr;
|
||||
|
||||
class epicsShareClass Command {
|
||||
public:
|
||||
POINTER_DEFINITIONS(Command);
|
||||
virtual ~Command(){}
|
||||
virtual void command() = 0;
|
||||
private:
|
||||
CommandPtr next;
|
||||
friend class Executor;
|
||||
};
|
||||
|
||||
class epicsShareClass Executor : public Runnable{
|
||||
public:
|
||||
POINTER_DEFINITIONS(Executor);
|
||||
Executor(std::string const & threadName,ThreadPriority priority);
|
||||
~Executor();
|
||||
void execute(CommandPtr const &node);
|
||||
virtual void run();
|
||||
private:
|
||||
CommandPtr head;
|
||||
CommandPtr tail;
|
||||
epics::pvData::Mutex mutex;
|
||||
epics::pvData::Event moreWork;
|
||||
epics::pvData::Event stopped;
|
||||
epics::pvData::Thread thread;
|
||||
};
|
||||
|
||||
}}
|
||||
#endif /* EXECUTOR_H */
|
||||
@@ -1,8 +1,7 @@
|
||||
/* localStaticLock.cpp */
|
||||
/**
|
||||
* Copyright - See the COPYRIGHT that is included with this distribution.
|
||||
* EPICS pvData is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
/*
|
||||
* Copyright information and license terms for this software can be
|
||||
* found in the file LICENSE that is included with the distribution
|
||||
*/
|
||||
/**
|
||||
* @author mse
|
||||
|
||||
@@ -1,116 +0,0 @@
|
||||
/* messageQueue.cpp */
|
||||
/**
|
||||
* Copyright - See the COPYRIGHT that is included with this distribution.
|
||||
* EPICS pvData is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
*/
|
||||
/**
|
||||
* @author mrk
|
||||
*/
|
||||
#include <string>
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include <pv/messageQueue.h>
|
||||
|
||||
using std::string;
|
||||
|
||||
namespace epics { namespace pvData {
|
||||
|
||||
MessageNode::MessageNode()
|
||||
: messageType(infoMessage)
|
||||
{}
|
||||
|
||||
string MessageNode::getMessage() const
|
||||
{
|
||||
return message;
|
||||
}
|
||||
|
||||
MessageType MessageNode::getMessageType() const
|
||||
{
|
||||
return messageType;
|
||||
}
|
||||
|
||||
MessageQueuePtr MessageQueue::create(int size)
|
||||
{
|
||||
MessageNodePtrArray nodeArray;
|
||||
nodeArray.reserve(size);
|
||||
for(int i=0; i<size; i++) {
|
||||
nodeArray.push_back(
|
||||
MessageNodePtr(new MessageNode()));
|
||||
}
|
||||
return std::tr1::shared_ptr<MessageQueue>(new MessageQueue(nodeArray));
|
||||
}
|
||||
|
||||
MessageQueue::MessageQueue(MessageNodePtrArray &data)
|
||||
: Queue<MessageNode>(data),
|
||||
overrun(0)
|
||||
{ }
|
||||
|
||||
MessageQueue::~MessageQueue()
|
||||
{
|
||||
}
|
||||
|
||||
MessageNodePtr &MessageQueue::get() {
|
||||
if(getNumberUsed()==0) return nullNode;
|
||||
lastGet = getUsed();
|
||||
return lastGet;
|
||||
}
|
||||
|
||||
void MessageQueue::release() {
|
||||
if(lastGet.get()==NULL) return;
|
||||
releaseUsed(lastGet);
|
||||
lastGet.reset();
|
||||
}
|
||||
bool MessageQueue::put(string message,MessageType messageType,bool replaceLast)
|
||||
{
|
||||
MessageNodePtr node = getFree();
|
||||
if(node.get()!= NULL) {
|
||||
node->message = message;
|
||||
node->messageType = messageType;
|
||||
lastPut = node;
|
||||
setUsed(node);
|
||||
return true;
|
||||
}
|
||||
overrun++;
|
||||
if(replaceLast) {
|
||||
node = lastPut;
|
||||
node->message = message;
|
||||
node->messageType = messageType;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool MessageQueue::isEmpty()
|
||||
{
|
||||
int free = getNumberFree();
|
||||
if(free==capacity()) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool MessageQueue::isFull()
|
||||
{
|
||||
if(getNumberFree()==0) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
int MessageQueue::getClearOverrun()
|
||||
{
|
||||
int num = overrun;
|
||||
overrun = 0;
|
||||
return num;
|
||||
}
|
||||
|
||||
MessageQueuePtr createMessageQueue(int size)
|
||||
{
|
||||
MessageNodePtrArray nodeArray;
|
||||
nodeArray.reserve(size);
|
||||
for(int i=0; i<size; i++) {
|
||||
nodeArray.push_back(
|
||||
MessageNodePtr(new MessageNode()));
|
||||
}
|
||||
return std::tr1::shared_ptr<MessageQueue>(new MessageQueue(nodeArray));
|
||||
}
|
||||
|
||||
|
||||
}}
|
||||
@@ -1,67 +0,0 @@
|
||||
/* messageQueue.h */
|
||||
/**
|
||||
* Copyright - See the COPYRIGHT that is included with this distribution.
|
||||
* EPICS pvData is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
*/
|
||||
/**
|
||||
* @author mrk
|
||||
*/
|
||||
#ifndef MESSAGEQUEUE_H
|
||||
#define MESSAGEQUEUE_H
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <cstddef>
|
||||
#include <stdexcept>
|
||||
|
||||
#include <pv/pvType.h>
|
||||
#include <pv/requester.h>
|
||||
#include <pv/queue.h>
|
||||
|
||||
#include <shareLib.h>
|
||||
|
||||
namespace epics { namespace pvData {
|
||||
|
||||
class MessageNode;
|
||||
class MessageQueue;
|
||||
typedef std::tr1::shared_ptr<MessageNode> MessageNodePtr;
|
||||
typedef std::vector<MessageNodePtr> MessageNodePtrArray;
|
||||
typedef std::tr1::shared_ptr<MessageQueue> MessageQueuePtr;
|
||||
|
||||
class epicsShareClass MessageNode {
|
||||
public:
|
||||
MessageNode();
|
||||
std::string getMessage() const;
|
||||
MessageType getMessageType() const;
|
||||
private:
|
||||
std::string message;
|
||||
MessageType messageType;
|
||||
friend class MessageQueue;
|
||||
};
|
||||
|
||||
class epicsShareClass MessageQueue : public Queue<MessageNode> {
|
||||
public:
|
||||
POINTER_DEFINITIONS(MessageQueue);
|
||||
static MessageQueuePtr create(int size);
|
||||
MessageQueue(MessageNodePtrArray &nodeArray);
|
||||
virtual ~MessageQueue();
|
||||
MessageNodePtr &get();
|
||||
// must call release before next get
|
||||
void release();
|
||||
// return (false,true) if message (was not, was) put into queue
|
||||
bool put(std::string message,MessageType messageType,bool replaceLast);
|
||||
bool isEmpty() ;
|
||||
bool isFull() ;
|
||||
int getClearOverrun();
|
||||
private:
|
||||
MessageNodePtr nullNode;
|
||||
MessageNodePtr lastGet;
|
||||
MessageNodePtr lastPut;
|
||||
uint32 overrun;
|
||||
};
|
||||
|
||||
}}
|
||||
#endif /* MESSAGEQUEUE_H */
|
||||
|
||||
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
/* noDefaultMethods.h */
|
||||
/**
|
||||
* Copyright - See the COPYRIGHT that is included with this distribution.
|
||||
* EPICS pvData is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
*/
|
||||
/**
|
||||
* @author mrk
|
||||
*/
|
||||
#ifndef NO_DEFAULT_METHODS_H
|
||||
#define NO_DEFAULT_METHODS_H
|
||||
|
||||
#include <shareLib.h>
|
||||
|
||||
namespace epics { namespace pvData {
|
||||
/* This is based on Item 6 of
|
||||
* Effective C++, Third Edition, Scott Meyers
|
||||
*/
|
||||
|
||||
|
||||
class epicsShareClass NoDefaultMethods {
|
||||
protected:
|
||||
// allow by derived objects
|
||||
NoDefaultMethods(){};
|
||||
~NoDefaultMethods(){}
|
||||
private:
|
||||
// do not implment
|
||||
NoDefaultMethods(const NoDefaultMethods&);
|
||||
NoDefaultMethods & operator=(const NoDefaultMethods &);
|
||||
};
|
||||
|
||||
}}
|
||||
#endif /* NO_DEFAULT_METHODS_H */
|
||||
@@ -1,3 +1,7 @@
|
||||
/*
|
||||
* Copyright information and license terms for this software can be
|
||||
* found in the file LICENSE that is included with the distribution
|
||||
*/
|
||||
#include <ctype.h>
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
@@ -12,7 +16,7 @@
|
||||
#include <epicsConvert.h>
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include "typeCast.h"
|
||||
#include "pv/typeCast.h"
|
||||
|
||||
using std::string;
|
||||
|
||||
@@ -27,7 +31,7 @@ using std::string;
|
||||
#endif
|
||||
|
||||
#if EPICS_VERSION_INT < VERSION_INT(3,15,0,1)
|
||||
/* integer conversion primitives added to epicsStdlib.c in 3.15.0.1 */
|
||||
/* These integer conversion primitives added to epicsStdlib.c in 3.15.0.1 */
|
||||
|
||||
#define S_stdlib_noConversion 1 /* No digits to convert */
|
||||
#define S_stdlib_extraneous 2 /* Extraneous characters */
|
||||
@@ -249,18 +253,19 @@ epicsParseFloat(const char *str, float *to, char **units)
|
||||
}
|
||||
#endif
|
||||
|
||||
// MS Visual Studio 2013 defines strtoll, etc.
|
||||
#if defined(_WIN32)
|
||||
# if (_MSC_VER >= 1800)
|
||||
# define WIN_NEEDS_OLL_FUNC 0
|
||||
# else
|
||||
# define WIN_NEEDS_OLL_FUNC 1
|
||||
# endif
|
||||
// Sometimes we have to provide our own copy of strtoll()
|
||||
#if defined(_WIN32) && !defined(_MINGW)
|
||||
// On Windows with MSVC, Base-3.15 provides strtoll()
|
||||
# define NEED_OLL_FUNCS (EPICS_VERSION_INT < VERSION_INT(3,15,0,1))
|
||||
#elif defined(vxWorks)
|
||||
// On VxWorks, Base-3.15 provides strtoll()
|
||||
# define NEED_OLL_FUNCS (EPICS_VERSION_INT < VERSION_INT(3,15,0,1))
|
||||
#else
|
||||
# define WIN_NEEDS_OLL_FUNC 0
|
||||
// Other architectures all provide strtoll()
|
||||
# define NEED_OLL_FUNCS 0
|
||||
#endif
|
||||
|
||||
#if defined(NEED_LONGLONG) && (defined(__vxworks) || WIN_NEEDS_OLL_FUNC)
|
||||
#if defined(NEED_LONGLONG) && NEED_OLL_FUNCS
|
||||
static
|
||||
long long strtoll(const char *ptr, char ** endp, int base)
|
||||
{
|
||||
@@ -308,8 +313,10 @@ noconvert:
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if defined(__vxworks)
|
||||
/* vxworks version of std::istringstream >>uint64_t is buggy, we use out own implementation */
|
||||
#if defined(vxWorks)
|
||||
/* The VxWorks version of std::istringstream >> uint64_t is buggy,
|
||||
* provide our own implementation
|
||||
*/
|
||||
static
|
||||
unsigned long long strtoull(const char *nptr, char **endptr, int base)
|
||||
{
|
||||
@@ -500,20 +507,20 @@ void handleParseError(int err)
|
||||
|
||||
namespace epics { namespace pvData { namespace detail {
|
||||
|
||||
void parseToPOD(const string & in, boolean *out)
|
||||
void parseToPOD(const char* in, boolean *out)
|
||||
{
|
||||
if(epicsStrCaseCmp(in.c_str(),"true")==0)
|
||||
if(epicsStrCaseCmp(in,"true")==0)
|
||||
*out = 1;
|
||||
else if(epicsStrCaseCmp(in.c_str(),"false")==0)
|
||||
else if(epicsStrCaseCmp(in,"false")==0)
|
||||
*out = 0;
|
||||
else
|
||||
throw std::runtime_error("parseToPOD: string no match true/false");
|
||||
}
|
||||
|
||||
#define INTFN(T, S) \
|
||||
void parseToPOD(const string& in, T *out) { \
|
||||
void parseToPOD(const char* in, T *out) { \
|
||||
epics ## S temp; \
|
||||
int err = epicsParse ## S (in.c_str(), &temp, 0, NULL); \
|
||||
int err = epicsParse ## S (in, &temp, 0, NULL); \
|
||||
if(err) handleParseError(err); \
|
||||
else *out = temp; \
|
||||
}
|
||||
@@ -525,38 +532,39 @@ INTFN(uint16_t, UInt16);
|
||||
INTFN(int32_t, Int32);
|
||||
INTFN(uint32_t, UInt32);
|
||||
|
||||
void parseToPOD(const string& in, int64_t *out) {
|
||||
void parseToPOD(const char* in, int64_t *out) {
|
||||
#ifdef NEED_LONGLONG
|
||||
int err = epicsParseLongLong(in.c_str(), out, 0, NULL);
|
||||
int err = epicsParseLongLong(in, out, 0, NULL);
|
||||
#else
|
||||
int err = epicsParseLong(in.c_str(), out, 0, NULL);
|
||||
int err = epicsParseLong(in, out, 0, NULL);
|
||||
#endif
|
||||
if(err) handleParseError(err);
|
||||
}
|
||||
|
||||
void parseToPOD(const string& in, uint64_t *out) {
|
||||
void parseToPOD(const char* in, uint64_t *out) {
|
||||
#ifdef NEED_LONGLONG
|
||||
int err = epicsParseULongLong(in.c_str(), out, 0, NULL);
|
||||
int err = epicsParseULongLong(in, out, 0, NULL);
|
||||
#else
|
||||
int err = epicsParseULong(in.c_str(), out, 0, NULL);
|
||||
int err = epicsParseULong(in, out, 0, NULL);
|
||||
#endif
|
||||
if(err) handleParseError(err);
|
||||
}
|
||||
|
||||
void parseToPOD(const string& in, float *out) {
|
||||
int err = epicsParseFloat(in.c_str(), out, NULL);
|
||||
void parseToPOD(const char* in, float *out) {
|
||||
int err = epicsParseFloat(in, out, NULL);
|
||||
if(err) handleParseError(err);
|
||||
}
|
||||
|
||||
void parseToPOD(const string& in, double *out) {
|
||||
int err = epicsParseDouble(in.c_str(), out, NULL);
|
||||
void parseToPOD(const char* in, double *out) {
|
||||
int err = epicsParseDouble(in, out, NULL);
|
||||
if(err) handleParseError(err);
|
||||
#if defined(__vxworks)
|
||||
/* vxWorks strtod returns [-]epicsINF when it should return ERANGE error
|
||||
* if [-]epicsINF is returned and first char is a digit then translate this into ERANGE error
|
||||
#if defined(vxWorks)
|
||||
/* vxWorks strtod returns [-]epicsINF when it should return ERANGE error.
|
||||
* If [-]epicsINF is returned and the first char is a digit we translate
|
||||
* this into an ERANGE error
|
||||
*/
|
||||
else if (*out == epicsINF || *out == -epicsINF) {
|
||||
const char* s = in.c_str();
|
||||
const char* s = in;
|
||||
int c;
|
||||
|
||||
/* skip spaces and the sign */
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user