diff --git a/.ci/travis-build.sh b/.ci/travis-build.sh new file mode 100755 index 0000000..6c76689 --- /dev/null +++ b/.ci/travis-build.sh @@ -0,0 +1,21 @@ +#!/bin/sh +set -e -x + +# set RTEMS to eg. "4.9" or "4.10" +# requires qemu, bison, flex, texinfo, install-info +if [ -n "$RTEMS" ] +then + # find local qemu-system-i386 + export PATH="$HOME/.cache/qemu/usr/bin:$PATH" + echo -n "Using QEMU: " + type qemu-system-i386 || echo "Missing qemu" + EXTRA=RTEMS_QEMU_FIXUPS=YES +fi + +make -j2 $EXTRA + +if [ "$TEST" != "NO" ] +then + make tapfiles + find . -name '*.tap' -print0 | xargs -0 -n1 prove -e cat -f +fi diff --git a/.ci/travis-prepare.sh b/.ci/travis-prepare.sh new file mode 100755 index 0000000..2251949 --- /dev/null +++ b/.ci/travis-prepare.sh @@ -0,0 +1,174 @@ +#!/bin/sh +set -e -x + +CURDIR="$PWD" + +QDIR="$HOME/.cache/qemu" + +if [ -n "$RTEMS" -a "$TEST" = "YES" ] +then + git clone --quiet --branch vme --depth 10 https://github.com/mdavidsaver/qemu.git "$HOME/.build/qemu" + cd "$HOME/.build/qemu" + + HEAD=`git log -n1 --pretty=format:%H` + echo "HEAD revision $HEAD" + + [ -e "$HOME/.cache/qemu/built" ] && BUILT=`cat "$HOME/.cache/qemu/built"` + echo "Cached revision $BUILT" + + if [ "$HEAD" != "$BUILT" ] + then + echo "Building QEMU" + git submodule --quiet update --init + + install -d "$HOME/.build/qemu/build" + cd "$HOME/.build/qemu/build" + + "$HOME/.build/qemu/configure" --prefix="$HOME/.cache/qemu/usr" --target-list=i386-softmmu --disable-werror + make -j2 + make install + + echo "$HEAD" > "$HOME/.cache/qemu/built" + fi +fi + +cat << EOF > $CURDIR/configure/RELEASE.local +EPICS_BASE=$HOME/.source/epics-base +EOF + +install -d "$HOME/.source" +cd "$HOME/.source" + +add_base_module() { + MODULE=$1 + BRANCH=$2 + ( cd epics-base/modules && \ + git clone --quiet --depth 5 --branch $MODULE/$BRANCH https://github.com/${REPOBASE:-epics-base}/epics-base.git $MODULE && \ + cd $MODULE && git log -n1 ) +} + +add_gh_module() { + MODULE=$1 + REPOOWNER=$2 + REPONAME=$3 + BRANCH=$4 + ( cd epics-base/modules && \ + git clone --quiet --depth 5 --branch $BRANCH https://github.com/$REPOOWNER/$REPONAME.git $MODULE && \ + cd $MODULE && git log -n1 ) +} + +add_gh_flat() { + MODULE=$1 + REPOOWNER=$2 + REPONAME=$3 + BRANCH=$4 + MODULE_UC=$(echo $MODULE | tr 'a-z' 'A-Z') + ( git clone --quiet --depth 5 --branch $BRANCH https://github.com/$REPOOWNER/$REPONAME.git $MODULE && \ + cd $MODULE && git log -n1 ) + cat < $CURDIR/configure/RELEASE.local > $MODULE/configure/RELEASE.local + cat << EOF >> $CURDIR/configure/RELEASE.local +${MODULE_UC}=$HOME/.source/$MODULE +EOF +} + +if [ "$BRBASE" ] +then + git clone --quiet --depth 5 --branch "$BRBASE" https://github.com/${REPOBASE:-epics-base}/epics-base.git epics-base + (cd epics-base && git log -n1 ) + add_gh_flat pvData ${REPOPVD:-epics-base} pvDataCPP ${BRPVD:-master} +else + git clone --quiet --depth 5 --branch core/"${BRCORE:-master}" https://github.com/${REPOBASE:-epics-base}/epics-base.git epics-base + ( cd epics-base && git log -n1 ) + add_base_module libcom "${BRLIBCOM:-master}" + add_base_module ca "${BRCA:-master}" + add_base_module database "${BRDATABASE:-master}" + add_gh_module pvData ${REPOPVD:-epics-base} pvDataCPP ${BRPVD:-master} +fi + +if [ -e $CURDIR/configure/RELEASE.local ] +then + cat $CURDIR/configure/RELEASE.local +fi + +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 <> 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" + install -d /home/travis/.cache + curl -L "https://github.com/mdavidsaver/rsb/releases/download/travis-20160306-2/rtems${RTEMS}-i386-trusty-20190306-2.tar.gz" \ + | tar -C /home/travis/.cache -xj + + 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/travis/.cache/rtems${RTEMS}-i386 +EOF + cat << EOF >> epics-base/configure/CONFIG_SITE +CROSS_COMPILER_TARGET_ARCHS+=RTEMS-pc386 +EOF + + # find local qemu-system-i386 + export PATH="$HOME/.cache/qemu/usr/bin:$PATH" + echo -n "Using QEMU: " + type qemu-system-i386 || echo "Missing qemu" + EXTRA=RTEMS_QEMU_FIXUPS=YES +fi + +make -j2 -C epics-base $EXTRA + +if [ "$BRBASE" ] +then + make -j2 -C pvData $EXTRA +fi diff --git a/.travis.yml b/.travis.yml index 7e62e96..2f838d3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,16 +11,19 @@ addons: - perl - clang - g++-mingw-w64-i686 +install: + - ./.ci/travis-prepare.sh +script: + - ./.ci/travis-build.sh env: + - BRCORE=master BRLIBCOM=master BRPVD=master + - CMPLR=clang + - EXTRA="CMD_CXXFLAGS=-std=c++11" + - CMPLR=clang EXTRA="CMD_CXXFLAGS=-std=c++11" + - WINE=32 TEST=NO STATIC=YES + - WINE=32 TEST=NO STATIC=NO + - RTEMS=4.10 TEST=NO + - RTEMS=4.9 TEST=NO - BRBASE=3.16 - - BRBASE=3.16 CMPLR=clang - - BRBASE=3.16 USR_CXXFLAGS=-std=c++11 - - BRBASE=3.16 USR_CXXFLAGS=-std=c++11 CMPLR=clang - - BRBASE=3.16 WINE=32 TEST=NO STATIC=YES - - BRBASE=3.16 WINE=32 TEST=NO STATIC=NO - BRBASE=3.15 - BRBASE=3.14 -install: - - ./ci/travis-prepare.sh -script: - - ./ci/travis-build.sh diff --git a/ci/travis-build.sh b/ci/travis-build.sh deleted file mode 100755 index 36c49cf..0000000 --- a/ci/travis-build.sh +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/sh -set -e -x - -make -j2 - -if [ "$TEST" != "NO" ] -then - make tapfiles - find . -name '*.tap' -print0 | xargs -0 -n1 prove -e cat -f -fi diff --git a/ci/travis-prepare.sh b/ci/travis-prepare.sh deleted file mode 100755 index 2362a35..0000000 --- a/ci/travis-prepare.sh +++ /dev/null @@ -1,69 +0,0 @@ -#!/bin/sh -set -e -x - -cat << EOF > configure/RELEASE.local -EPICS_BASE=$HOME/.source/epics-base -PVDATA=$HOME/.source/pvDataCPP -EOF -cat configure/RELEASE.local - -install -d "$HOME/.source" -cd "$HOME/.source" - -git clone --quiet --depth 5 --branch "${BRBASE:-master}" https://github.com/${SRCBASE:-epics-base}/epics-base.git epics-base -git clone --quiet --depth 5 --branch "${BRPVD:-master}" https://github.com/${SRCPVD:-epics-base}/pvDataCPP.git pvDataCPP - -(cd epics-base && git log -n1 ) -(cd pvDataCPP && 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 > pvDataCPP/configure/RELEASE.local -EPICS_BASE=$HOME/.source/epics-base -EOF - -make -j2 -C epics-base -make -j2 -C pvDataCPP diff --git a/pvtoolsSrc/pvput.cpp b/pvtoolsSrc/pvput.cpp index e4cb427..1da2473 100644 --- a/pvtoolsSrc/pvput.cpp +++ b/pvtoolsSrc/pvput.cpp @@ -1,28 +1,36 @@ #include -#include - -#include -#include -#include -#include -#include -#include -#include - #include #include #include #include #include -#include -#include +#include #include -#include "pvutils.cpp" +#include +#include +#include + +#include +#include +#include +#include + +#include + +#if EPICS_VERSION_INT>=VERSION_INT(3,15,0,1) +# include +# define USE_JSON +#endif + +#include +#include #include +#include "pvutils.cpp" + using namespace std; namespace TR1 = std::tr1; using namespace epics::pvData; @@ -30,237 +38,6 @@ using namespace epics::pvAccess; namespace { -size_t fromString(PVFieldPtr const & pv, StringArray const & from, size_t fromStartIndex); - -size_t fromString(PVScalarArrayPtr const &pv, StringArray const & from, size_t fromStartIndex = 0) -{ - int processed = 0; - size_t fromValueCount = from.size(); - - // first get count - if (fromStartIndex >= fromValueCount) - throw std::runtime_error("not enough values"); - - size_t count; - istringstream iss(from[fromStartIndex]); - iss >> count; - // not fail and entire value is parsed (e.g. to detect 1.2 parsing to 1) - if (iss.fail() || !iss.eof()) - throw runtime_error("failed to parse element count value (uint) of field '" + pv->getFieldName() + "' from string value '" + from[fromStartIndex] + "'"); - fromStartIndex++; - processed++; - - if ((fromStartIndex+count) > fromValueCount) - { - throw runtime_error("not enough array values for field " + pv->getFieldName()); - } - - PVStringArray::svector valueList(count); - std::copy(from.begin() + fromStartIndex, from.begin() + fromStartIndex + count, valueList.begin()); - processed += count; - - pv->putFrom(freeze(valueList)); - - return processed; -} - -size_t fromString(PVStructurePtr const & pvStructure, StringArray const & from, size_t fromStartIndex); - -size_t fromString(PVStructureArrayPtr const &pv, StringArray const & from, size_t fromStartIndex = 0) -{ - int processed = 0; - size_t fromValueCount = from.size(); - - // first get count - if (fromStartIndex >= fromValueCount) - throw std::runtime_error("not enough values"); - - size_t numberOfStructures; - istringstream iss(from[fromStartIndex]); - iss >> numberOfStructures; - // not fail and entire value is parsed (e.g. to detect 1.2 parsing to 1) - if (iss.fail() || !iss.eof()) - throw runtime_error("failed to parse element count value (uint) of field '" + pv->getFieldName() + "' from string value '" + from[fromStartIndex] + "'"); - fromStartIndex++; - processed++; - - PVStructureArray::svector pvStructures; - pvStructures.reserve(numberOfStructures); - - PVDataCreatePtr pvDataCreate = getPVDataCreate(); - for (size_t i = 0; i < numberOfStructures; ++i) - { - PVStructurePtr pvStructure = pvDataCreate->createPVStructure(pv->getStructureArray()->getStructure()); - size_t count = fromString(pvStructure, from, fromStartIndex); - processed += count; - fromStartIndex += count; - pvStructures.push_back(pvStructure); - } - - pv->replace(freeze(pvStructures)); - - return processed; -} - -size_t fromString(PVUnionArrayPtr const & pvUnionArray, StringArray const & from, size_t fromStartIndex); - -size_t fromString(PVUnionPtr const & pvUnion, StringArray const & from, size_t fromStartIndex = 0) -{ - if (pvUnion->getUnion()->isVariant()) - throw std::runtime_error("cannot handle variant unions"); - - size_t fromValueCount = from.size(); - - if (fromStartIndex >= fromValueCount) - throw std::runtime_error("not enough values"); - - string selector = from[fromStartIndex++]; - PVFieldPtr pv = pvUnion->select(selector); - if (!pv) - throw std::runtime_error("invalid union selector value '" + selector + "'"); - - size_t processed = fromString(pv, from, fromStartIndex); - return processed + 1; -} - -size_t fromString(PVUnionArrayPtr const &pv, StringArray const & from, size_t fromStartIndex = 0) -{ - int processed = 0; - size_t fromValueCount = from.size(); - - // first get count - if (fromStartIndex >= fromValueCount) - throw std::runtime_error("not enough values"); - - size_t numberOfUnions; - istringstream iss(from[fromStartIndex]); - iss >> numberOfUnions; - // not fail and entire value is parsed (e.g. to detect 1.2 parsing to 1) - if (iss.fail() || !iss.eof()) - throw runtime_error("failed to parse element count value (uint) of field '" + pv->getFieldName() + "' from string value '" + from[fromStartIndex] + "'"); - fromStartIndex++; - processed++; - - PVUnionArray::svector pvUnions; - pvUnions.reserve(numberOfUnions); - - PVDataCreatePtr pvDataCreate = getPVDataCreate(); - for (size_t i = 0; i < numberOfUnions; ++i) - { - PVUnionPtr pvUnion = pvDataCreate->createPVUnion(pv->getUnionArray()->getUnion()); - size_t count = fromString(pvUnion, from, fromStartIndex); - processed += count; - fromStartIndex += count; - pvUnions.push_back(pvUnion); - } - - pv->replace(freeze(pvUnions)); - - return processed; -} - -size_t fromString(PVStructurePtr const & pvStructure, StringArray const & from, size_t fromStartIndex = 0) -{ - // handle enum in a special way - if (pvStructure->getStructure()->getID() == "enum_t") - { - int32 index = -1; - PVInt::shared_pointer pvIndex = pvStructure->getSubField("index"); - if (!pvIndex) - throw std::runtime_error("enum_t structure does not have 'int index' field"); - - PVStringArray::shared_pointer pvChoices = pvStructure->getSubField("choices"); - if (!pvChoices) - throw std::runtime_error("enum_t structure does not have 'string choices[]' field"); - PVStringArray::const_svector choices(pvChoices->view()); - - if (enumMode == AutoEnum || enumMode == StringEnum) - { - shared_vector::const_iterator it = std::find(choices.begin(), choices.end(), from[fromStartIndex]); - if (it != choices.end()) - index = static_cast(it - choices.begin()); - else if (enumMode == StringEnum) - throw runtime_error("enum string value '" + from[fromStartIndex] + "' invalid"); - } - - if ((enumMode == AutoEnum && index == -1) || enumMode == NumberEnum) - { - istringstream iss(from[fromStartIndex]); - iss >> index; - // not fail and entire value is parsed (e.g. to detect 1.2 parsing to 1) - if (iss.fail() || !iss.eof()) - throw runtime_error("enum value '" + from[fromStartIndex] + "' invalid"); - - if (index < 0 || index >= static_cast(choices.size())) - throw runtime_error("index '" + from[fromStartIndex] + "' out of bounds"); - } - - pvIndex->put(index); - return 1; - } - - size_t processed = 0; - - PVFieldPtrArray const & fieldsData = pvStructure->getPVFields(); - if (fieldsData.size() != 0) { - size_t length = pvStructure->getStructure()->getNumberFields(); - for(size_t i = 0; i < length; i++) { - size_t count = fromString(fieldsData[i], from, fromStartIndex); - processed += count; - fromStartIndex += count; - } - } - - return processed; -} - -size_t fromString(PVFieldPtr const & fieldField, StringArray const & from, size_t fromStartIndex) -{ - try - { - switch (fieldField->getField()->getType()) - { - case scalar: - { - if (fromStartIndex >= from.size()) - throw std::runtime_error("not enough values"); - - PVScalarPtr pv = TR1::static_pointer_cast(fieldField); - getConvert()->fromString(pv, from[fromStartIndex]); - return 1; - } - - case scalarArray: - return fromString(TR1::static_pointer_cast(fieldField), from, fromStartIndex); - - case structure: - return fromString(TR1::static_pointer_cast(fieldField), from, fromStartIndex); - - case structureArray: - return fromString(TR1::static_pointer_cast(fieldField), from, fromStartIndex); - - case union_: - return fromString(TR1::static_pointer_cast(fieldField), from, fromStartIndex); - - case unionArray: - return fromString(TR1::static_pointer_cast(fieldField), from, fromStartIndex); - - default: - std::ostringstream oss; - oss << "fromString unsupported fieldType " << fieldField->getField()->getType(); - throw std::logic_error(oss.str()); - } - } - catch (std::exception &ex) - { - std::ostringstream os; - os << "failed to parse '" << fieldField->getField()->getID() << ' ' - << fieldField->getFieldName() << "'"; - os << ": " << ex.what(); - throw std::runtime_error(os.str()); - } -} - #define DEFAULT_TIMEOUT 3.0 #define DEFAULT_REQUEST "field(value)" @@ -276,9 +53,21 @@ PrintMode mode = ValueOnlyMode; char fieldSeparator = ' '; -void usage (void) +bool debug = false; + +void usage (bool details=false) { - fprintf (stderr, "\nUsage: pvput [options] ...\n\n" + fprintf (stderr, + "Usage: pvput [options] \n" + " pvput [options] [ ...]\n" + " pvput [options] = ...\n" + " pvput [options] \n"); +#ifdef USE_JSON + fprintf (stderr, + " pvput [options] \n"); +#endif + fprintf (stderr, + "\n" " -h: Help: Print this message\n" " -v: Print version and exit\n" "\noptions:\n" @@ -294,16 +83,48 @@ void usage (void) " default: Auto - try value as enum string, then as index number\n" " -n: Force enum interpretation of values as numbers\n" " -s: Force enum interpretation of values as strings\n" - "\nexample: pvput double01 1.234\n\n" , DEFAULT_REQUEST, DEFAULT_TIMEOUT, DEFAULT_PROVIDER); + if(details) { + fprintf (stderr, +#ifdef USE_JSON + "\n JSON support is present\n" +#else + "\n no JSON support (needs EPICS Base >=3.15.0.1)\n" +#endif + ); + fprintf (stderr, + "\nExamples:\n" + "\n" + " pvput double01 1.234 # shorthand\n" + " pvput double01 value=1.234\n" + "\n" + " pvput arr:pv X 1.0 2.0 # shorthand (X is arbitrary and ignored)\n" + " pvput arr:pv \"[1.0, 2.0]\" # shorthand\n" + " pvput arr:pv value=\"[1.0, 2.0]\"\n" + ); +#ifdef USE_JSON + fprintf (stderr, + "\n" + "Field values may be given with JSON syntax.\n" + "\n" + "Complete structure\n" + "\n" + " pvput double01 '{\"value\":1.234}'\n" + "\n" + "Sub-structure(s)\n" + "\n" + " pvput group:pv some='{\"value\":1.234}' other='{\"value\":\"a string\"}'\n" + "\n" + ); +#endif + } } - -void printValue(std::string const & channelName, PVStructure::shared_pointer const & pv) +void printValue(std::string const & channelName, PVStructure::const_shared_pointer const & pv) { if (mode == ValueOnlyMode) { - PVField::shared_pointer value = pv->getSubField("value"); + PVField::const_shared_pointer value = pv->getSubField("value"); if (value.get() == 0) { std::cerr << "no 'value' field" << std::endl; @@ -317,7 +138,7 @@ void printValue(std::string const & channelName, PVStructure::shared_pointer con // special case for enum if (valueType == structure) { - PVStructurePtr pvStructure = TR1::static_pointer_cast(value); + PVStructure::const_shared_pointer pvStructure = TR1::static_pointer_cast(value); if (pvStructure->getStructure()->getID() == "enum_t") { if (fieldSeparator == ' ') @@ -357,138 +178,214 @@ void printValue(std::string const & channelName, PVStructure::shared_pointer con std::cout << std::endl << *(pv.get()) << std::endl << std::endl; } -class ChannelPutRequesterImpl : public ChannelPutRequester +void early(const char *inp, unsigned pos) { -private: - PVStructure::shared_pointer m_pvStructure; - BitSet::shared_pointer m_bitSet; - Mutex m_pointerMutex; - Mutex m_eventMutex; - auto_ptr m_event; - string m_channelName; - AtomicBoolean m_done; + fprintf(stderr, "Unexpected end of input: %s\n", inp); + throw std::runtime_error("Unexpected end of input"); +} -public: +// rudimentory parser for json array +// needed as long as Base < 3.15 is supported. +// for consistency, used with all version +void jarray(shared_vector& out, const char *inp) +{ + assert(inp[0]=='['); + const char * const orig = inp; + inp++; - ChannelPutRequesterImpl(std::string channelName) : m_channelName(channelName) - { - resetEvent(); + while(true) { + // starting a new token + + for(; *inp==' '; inp++) {} // skip leading whitespace + + if(*inp=='\0') early(inp, inp-orig); + + if(isalnum(*inp) || *inp=='+' || *inp=='-') { + // number + + const char *start = inp; + + while(isalnum(*inp) || *inp=='.' || *inp=='+' || *inp=='-') + inp++; + + if(*inp=='\0') early(inp, inp-orig); + + // inp points to first char after token + + out.push_back(std::string(start, inp-start)); + + } else if(*inp=='"') { + // quoted string + + const char *start = ++inp; // skip quote + + while(*inp!='\0' && *inp!='"') + inp++; + + if(*inp=='\0') early(inp, inp-orig); + + // inp points to trailing " + + out.push_back(std::string(start, inp-start)); + + inp++; // skip trailing " + + } else if(*inp==']') { + // no-op + } else { + fprintf(stderr, "Unknown token '%c' in \"%s\"", *inp, inp); + throw std::runtime_error("Unknown token"); + } + + for(; *inp==' '; inp++) {} // skip trailing whitespace + + if(*inp==',') inp++; + else if(*inp==']') break; + else { + fprintf(stderr, "Unknown token '%c' in \"%s\"", *inp, inp); + throw std::runtime_error("Unknown token"); + } } - virtual string getRequesterName() - { - return "ChannelPutRequesterImpl"; - } +} - virtual void channelPutConnect(const epics::pvData::Status& status, - ChannelPut::shared_pointer const & channelPut, - epics::pvData::Structure::const_shared_pointer const & /*structure*/) +struct Putter : public pvac::ClientChannel::PutCallback +{ + epicsEvent wait; + epicsMutex lock; + bool done; + pvac::PutEvent::event_t result; + std::string message; + + Putter() :done(false) {} + + typedef shared_vector bare_t; + bare_t bare; + + typedef std::pair KV_t; + typedef std::vector pairs_t; + pairs_t pairs; + + shared_vector jarr; + + virtual void putBuild(const epics::pvData::StructureConstPtr& build, Args& args) { - if (status.isSuccess()) - { - // show warning - if (!status.isOK()) - { - std::cerr << "[" << m_channelName << "] channel put create: " << dump_stack_only_on_debug(status) << std::endl; + if(debug) std::cerr<<"Server defined structure\n"<createPVStructure(build)); + + if(bare.size()==1 && bare[0][0]=='{') { + if(debug) fprintf(stderr, "In JSON top mode\n"); +#ifdef USE_JSON + std::istringstream strm(bare[0]); + parseJSON(strm, root, &args.tosend); +#else +#endif + + } else if(pairs.empty()) { + if(debug) fprintf(stderr, "In plain value mode\n"); + + PVFieldPtr fld(root->getSubField("value")); + Type ftype = fld->getField()->getType(); + + if(ftype==scalar) { + if(bare.size()!=1) { + throw std::runtime_error("Can't assign multiple values to scalar"); + } + PVScalar* sfld(static_cast(fld.get())); + sfld->putFrom(bare[0]); + args.tosend.set(sfld->getFieldOffset()); + + } else if(ftype==scalarArray) { + PVScalarArray* sfld(static_cast(fld.get())); + + // first element is "length" which we ignore for compatibility + bare.slice(1); + + sfld->putFrom(freeze(bare)); + args.tosend.set(sfld->getFieldOffset()); + + } else if(ftype==structure && fld->getField()->getID()=="enum_t") { + if(bare.size()!=1) { + throw std::runtime_error("Can't assign multiple values to enum"); + } + PVStructure* sfld(static_cast(fld.get())); + + PVScalar* idxfld(sfld->getSubFieldT("index").get()); + PVStringArray::const_svector choices(sfld->getSubFieldT("choices")->view()); + + bool found=false; + for(size_t i=0; iputFrom(i); + found=true; + } + } + + if(!found) { + // try to parse as integer + idxfld->putFrom(bare[0]); + } + + args.tosend.set(idxfld->getFieldOffset()); } - // get immediately old value - channelPut->get(); - } - else - { - std::cerr << "[" << m_channelName << "] failed to create channel put: " << dump_stack_only_on_debug(status) << std::endl; - m_event->signal(); - } - } + } else { + if(debug) fprintf(stderr, "In field=value mode\n"); - virtual void getDone(const epics::pvData::Status& status, ChannelPut::shared_pointer const & /*channelPut*/, - epics::pvData::PVStructure::shared_pointer const & pvStructure, - epics::pvData::BitSet::shared_pointer const & bitSet) - { - if (status.isSuccess()) - { - // show warning - if (!status.isOK()) + for(pairs_t::const_iterator it=pairs.begin(), end=pairs.end(); it!=end; ++it) { - std::cerr << "[" << m_channelName << "] channel get: " << dump_stack_only_on_debug(status) << std::endl; + PVFieldPtr fld(root->getSubField(it->first)); + if(!fld) { + fprintf(stderr, "%s : Warning: no such field\n", it->first.c_str()); + // ignore + + } else if(it->second[0]=='[') { + shared_vector arr; + jarray(arr, it->second.c_str()); + + PVScalarArray* afld(dynamic_cast(fld.get())); + if(!afld) { + fprintf(stderr, "%s : Error not a scalar array field\n", it->first.c_str()); + throw std::runtime_error("Not a scalar array field"); + } + afld->putFrom(freeze(arr)); + args.tosend.set(afld->getFieldOffset()); + + } else if(it->second[0]=='{' || it->second[0]=='[') { + std::istringstream strm(it->second); +#ifdef USE_JSON + parseJSON(strm, fld, &args.tosend); +#else + throw std::runtime_error("JSON support not built"); +#endif + } else { + PVScalarPtr sfld(std::tr1::dynamic_pointer_cast(fld)); + if(!sfld) { + fprintf(stderr, "%s : Error: need a scalar field\n", it->first.c_str()); + } else { + sfld->putFrom(it->second); + args.tosend.set(sfld->getFieldOffset()); + } + } } - - m_done.set(); - - { - Lock lock(m_pointerMutex); - m_pvStructure = pvStructure; - // we always put all, so current bitSet is OK - m_bitSet = bitSet; - } - - } - else - { - std::cerr << "[" << m_channelName << "] failed to get: " << dump_stack_only_on_debug(status) << std::endl; } - m_event->signal(); + args.root = root; + if(debug) + std::cout<<"To be sent: "< G(lock); + result = evt.event; + message = evt.message; + done = true; } - m_event->signal(); + wait.signal(); } - - PVStructure::shared_pointer getStructure() - { - Lock lock(m_pointerMutex); - return m_pvStructure; - } - - BitSet::shared_pointer getBitSet() - { - Lock lock(m_pointerMutex); - return m_bitSet; - } - - void resetEvent() - { - Lock lock(m_eventMutex); - m_event.reset(new Event()); - m_done.clear(); - } - - bool waitUntilDone(double timeOut) - { - Event* event; - { - Lock lock(m_eventMutex); - event = m_event.get(); - } - - bool signaled = event->wait(timeOut); - if (!signaled) - { - std::cerr << "[" << m_channelName << "] timeout" << std::endl; - return false; - } - - return m_done.get(); - } - }; } // namespace @@ -496,7 +393,6 @@ public: int main (int argc, char *argv[]) { int opt; /* getopt() current option */ - bool debug = false; bool quiet = false; istream* inputStream = 0; @@ -509,7 +405,7 @@ int main (int argc, char *argv[]) while ((opt = getopt(argc, argv, ":hvr:w:tp:qdF:f:ns")) != -1) { switch (opt) { case 'h': /* Print usage */ - usage(); + usage(true); return 0; case 'v': /* Print version */ { @@ -618,14 +514,6 @@ int main (int argc, char *argv[]) address = uri.host; } - epics::pvAccess::ca::CAClientFactory::start(); - - ChannelProvider::shared_pointer provider(ChannelProviderRegistry::clients()->getProvider(providerName)); - if(!provider) { - std::cerr << "Unknown provider '"< 0) { @@ -657,6 +545,48 @@ int main (int argc, char *argv[]) values.push_back(argv[optind]); } + if(values.empty()) { + usage(); + fprintf(stderr, "\nNo values provided\n"); + return 1; + } + + Putter thework; + + for(size_t i=0, N=values.size(); icreateChannel(pvName, DefaultChannelRequester::build(), - ChannelProvider::PRIORITY_DEFAULT, address); - } catch(std::exception& e){ - std::cerr<<"Provider " << providerName<< " Failed to create channel \""< putRequesterImpl(new ChannelPutRequesterImpl(channel->getChannelName())); - if (mode != TerseMode && !quiet) - std::cout << "Old : "; - ChannelPut::shared_pointer channelPut = channel->createChannelPut(putRequesterImpl, pvRequest); - allOK &= putRequesterImpl->waitUntilDone(timeOut); - if (allOK) - { - if (mode != TerseMode && !quiet) - printValue(pvName, putRequesterImpl->getStructure()); - - // convert value from string - // since we access structure from another thread, we need to lock - { - ScopedLock lock(channelPut); - fromString(putRequesterImpl->getStructure(), values); - } - - // we do a put - putRequesterImpl->resetEvent(); - // note on bitSet: we get all, we set all - channelPut->put(putRequesterImpl->getStructure(), putRequesterImpl->getBitSet()); - allOK &= putRequesterImpl->waitUntilDone(timeOut); - - if (allOK) - { - // and than a get again to verify put - if (mode != TerseMode && !quiet) std::cout << "New : "; - putRequesterImpl->resetEvent(); - channelPut->get(); - allOK &= putRequesterImpl->waitUntilDone(timeOut); - if (allOK && !quiet) - printValue(pvName, putRequesterImpl->getStructure()); - } - } - } catch (std::out_of_range& oor) { - allOK = false; - std::cerr << "parse error: not enough values" << std::endl; - } catch (std::exception& ex) { - allOK = false; - std::cerr << ex.what() << std::endl; - } catch (...) { - allOK = false; - std::cerr << "unknown exception caught" << std::endl; + if (mode != TerseMode && !quiet) { + std::cout << "Old : "; + printValue(pvName, chan.get(timeOut, pvRequest)); } - return allOK ? 0 : 1; + { + pvac::Operation op(chan.put(&thework, pvRequest)); + + epicsGuard G(thework.lock); + while(!thework.done) { + epicsGuardRelease U(G); + if(!thework.wait.wait(timeOut)) { + fprintf(stderr, "Put timeout\n"); + return 1; + } + } + } + + if(thework.result==pvac::PutEvent::Fail) { + fprintf(stderr, "Error: %s\n", thework.message.c_str()); + } + + if (mode != TerseMode && !quiet) { + std::cout << "New : "; + } + printValue(pvName, chan.get(timeOut, pvRequest)); + + return thework.result!=pvac::PutEvent::Success; } diff --git a/pvtoolsSrc/pvutils.cpp b/pvtoolsSrc/pvutils.cpp index cdf8283..b0eab26 100644 --- a/pvtoolsSrc/pvutils.cpp +++ b/pvtoolsSrc/pvutils.cpp @@ -73,7 +73,7 @@ void printUserTag(bool flag) printUserTagFlag = flag; } -std::ostream& terse(std::ostream& o, PVField::shared_pointer const & pv) +std::ostream& terse(std::ostream& o, PVField::const_shared_pointer const & pv) { Type type = pv->getField()->getType(); switch (type) @@ -82,19 +82,19 @@ std::ostream& terse(std::ostream& o, PVField::shared_pointer const & pv) o << *(pv.get()); return o; case structure: - return terseStructure(o, TR1::static_pointer_cast(pv)); + return terseStructure(o, TR1::static_pointer_cast(pv)); break; case scalarArray: - return terseScalarArray(o, TR1::static_pointer_cast(pv)); + return terseScalarArray(o, TR1::static_pointer_cast(pv)); break; case structureArray: - return terseStructureArray(o, TR1::static_pointer_cast(pv)); + return terseStructureArray(o, TR1::static_pointer_cast(pv)); break; case union_: - return terseUnion(o, TR1::static_pointer_cast(pv)); + return terseUnion(o, TR1::static_pointer_cast(pv)); break; case unionArray: - return terseUnionArray(o, TR1::static_pointer_cast(pv)); + return terseUnionArray(o, TR1::static_pointer_cast(pv)); break; default: std::ostringstream msg("unknown Field type: "); @@ -127,45 +127,29 @@ std::ostream& printEnumT(std::ostream& o, epics::pvData::PVStructure const & pvE return o; } -std::ostream& printEnumT(std::ostream& o, epics::pvData::PVStructure::shared_pointer const & pvEnumT) +std::ostream& printEnumT(std::ostream& o, epics::pvData::PVStructure::const_shared_pointer const & pvEnumT) { return printEnumT(o, *pvEnumT); } -std::ostream& printTimeT(std::ostream& o, epics::pvData::PVStructure::shared_pointer const & pvTimeT) +std::ostream& printTimeT(std::ostream& o, epics::pvData::PVStructure::const_shared_pointer const & pvTimeT) { -#define TIMETEXTLEN 32 - char timeText[TIMETEXTLEN]; + char timeText[32]; epicsTimeStamp epicsTS; - int32 userTag; - PVTimeStamp pvTimeStamp; - if (pvTimeStamp.attach(pvTimeT)) - { - TimeStamp ts; - pvTimeStamp.get(ts); + PVScalar::const_shared_pointer secf(pvTimeT->getSubField("secondsPastEpoch")), + nsecf(pvTimeT->getSubField("nanoseconds")), + tagf(pvTimeT->getSubField("userTag")); - userTag = ts.getUserTag(); + epicsTS.secPastEpoch = secf ? secf->getAs() : 0; + epicsTS.nsec = nsecf ? nsecf->getAs() : 0; - if (ts.getSecondsPastEpoch() == 0 && - ts.getNanoseconds() == 0) - { - o << ""; - if (printUserTagFlag) - o << separator << userTag; - return o; - } + epicsTS.secPastEpoch -= POSIX_TIME_AT_EPICS_EPOCH; - epicsTS.secPastEpoch = ts.getEpicsSecondsPastEpoch(); - epicsTS.nsec = ts.getNanoseconds(); - } - else - throw std::runtime_error("invalid time_t structure"); - - epicsTimeToStrftime(timeText, TIMETEXTLEN, "%Y-%m-%dT%H:%M:%S.%03f", &epicsTS); + epicsTimeToStrftime(timeText, sizeof(timeText), "%Y-%m-%dT%H:%M:%S.%03f", &epicsTS); o << timeText; - if (printUserTagFlag) - o << separator << userTag; + if (printUserTagFlag && tagf) + o << separator << tagf->getAs(); return o; } @@ -189,17 +173,17 @@ const char* statusNames[] = { "CLIENT" // 7 }; -std::ostream& printAlarmT(std::ostream& o, epics::pvData::PVStructure::shared_pointer const & pvAlarmT) +std::ostream& printAlarmT(std::ostream& o, epics::pvData::PVStructure::const_shared_pointer const & pvAlarmT) { - PVInt::shared_pointer pvSeverity = pvAlarmT->getSubField("severity"); + PVInt::const_shared_pointer pvSeverity = pvAlarmT->getSubField("severity"); if (!pvSeverity) throw std::runtime_error("alarm_t structure does not have 'int severity' field"); - PVInt::shared_pointer pvStatus = pvAlarmT->getSubField("status"); + PVInt::const_shared_pointer pvStatus = pvAlarmT->getSubField("status"); if (!pvStatus) throw std::runtime_error("alarm_t structure does not have 'int status' field"); - PVString::shared_pointer pvMessage = pvAlarmT->getSubField("message"); + PVString::const_shared_pointer pvMessage = pvAlarmT->getSubField("message"); if (!pvMessage) throw std::runtime_error("alarm_t structure does not have 'string message' field"); @@ -224,7 +208,7 @@ std::ostream& printAlarmT(std::ostream& o, epics::pvData::PVStructure::shared_po } -bool isTType(epics::pvData::PVStructure::shared_pointer const & pvStructure) +bool isTType(epics::pvData::PVStructure::const_shared_pointer const & pvStructure) { // "forget" about Ttype if disabled if (!formatTTypesFlag) @@ -236,7 +220,7 @@ bool isTType(epics::pvData::PVStructure::shared_pointer const & pvStructure) id == "alarm_t"); } -bool formatTType(std::ostream& o, epics::pvData::PVStructure::shared_pointer const & pvStructure) +bool formatTType(std::ostream& o, epics::pvData::PVStructure::const_shared_pointer const & pvStructure) { // special t-types support (enum_t and time_t, etc.) if (formatTTypesFlag) @@ -263,7 +247,7 @@ bool formatTType(std::ostream& o, epics::pvData::PVStructure::shared_pointer con } -std::ostream& terseStructure(std::ostream& o, PVStructure::shared_pointer const & pvStructure) +std::ostream& terseStructure(std::ostream& o, PVStructure::const_shared_pointer const & pvStructure) { if (!pvStructure) { @@ -289,7 +273,7 @@ std::ostream& terseStructure(std::ostream& o, PVStructure::shared_pointer const return o; } -std::ostream& terseUnion(std::ostream& o, PVUnion::shared_pointer const & pvUnion) +std::ostream& terseUnion(std::ostream& o, PVUnion::const_shared_pointer const & pvUnion) { if (!pvUnion || !pvUnion->get()) { @@ -300,7 +284,7 @@ std::ostream& terseUnion(std::ostream& o, PVUnion::shared_pointer const & pvUnio return terse(o, pvUnion->get()); } -std::ostream& terseScalarArray(std::ostream& o, PVScalarArray::shared_pointer const & pvArray) +std::ostream& terseScalarArray(std::ostream& o, const PVScalarArray::const_shared_pointer &pvArray) { size_t length = pvArray->getLength(); if (arrayCountFlag) @@ -331,7 +315,7 @@ std::ostream& terseScalarArray(std::ostream& o, PVScalarArray::shared_pointer co */ } -std::ostream& terseStructureArray(std::ostream& o, PVStructureArray::shared_pointer const & pvArray) +std::ostream& terseStructureArray(std::ostream& o, PVStructureArray::const_shared_pointer const & pvArray) { size_t length = pvArray->getLength(); if (arrayCountFlag) @@ -357,7 +341,7 @@ std::ostream& terseStructureArray(std::ostream& o, PVStructureArray::shared_poin return o; } -std::ostream& terseUnionArray(std::ostream& o, PVUnionArray::shared_pointer const & pvArray) +std::ostream& terseUnionArray(std::ostream& o, PVUnionArray::const_shared_pointer const & pvArray) { size_t length = pvArray->getLength(); if (arrayCountFlag) diff --git a/pvtoolsSrc/pvutils.h b/pvtoolsSrc/pvutils.h index 944164e..da4139c 100644 --- a/pvtoolsSrc/pvutils.h +++ b/pvtoolsSrc/pvutils.h @@ -9,25 +9,25 @@ void convertStructureArray(std::string*, epics::pvData::PVStructureArray * pvdat void terseSeparator(char c); void terseArrayCount(bool flag); -std::ostream& terse(std::ostream& o, epics::pvData::PVField::shared_pointer const & pv); -std::ostream& terseUnion(std::ostream& o, epics::pvData::PVUnion::shared_pointer const & pvUnion); -std::ostream& terseStructure(std::ostream& o, epics::pvData::PVStructure::shared_pointer const & pvStructure); -std::ostream& terseScalarArray(std::ostream& o, epics::pvData::PVScalarArray::shared_pointer const & pvArray); -std::ostream& terseStructureArray(std::ostream& o, epics::pvData::PVStructureArray::shared_pointer const & pvArray); -std::ostream& terseUnionArray(std::ostream& o, epics::pvData::PVUnionArray::shared_pointer const & pvArray); +std::ostream& terse(std::ostream& o, epics::pvData::PVField::const_shared_pointer const & pv); +std::ostream& terseUnion(std::ostream& o, epics::pvData::PVUnion::const_shared_pointer const & pvUnion); +std::ostream& terseStructure(std::ostream& o, const epics::pvData::PVStructure::const_shared_pointer &pvStructure); +std::ostream& terseScalarArray(std::ostream& o, epics::pvData::PVScalarArray::const_shared_pointer const & pvArray); +std::ostream& terseStructureArray(std::ostream& o, epics::pvData::PVStructureArray::const_shared_pointer const & pvArray); +std::ostream& terseUnionArray(std::ostream& o, epics::pvData::PVUnionArray::const_shared_pointer const & pvArray); enum EnumMode { AutoEnum, NumberEnum, StringEnum }; void setEnumPrintMode(EnumMode mode); void formatTTypes(bool flag); -bool isTType(epics::pvData::PVStructure::shared_pointer const & pvStructure); -bool formatTType(std::ostream& o, epics::pvData::PVStructure::shared_pointer const & pvStructure); +bool isTType(epics::pvData::PVStructure::const_shared_pointer const & pvStructure); +bool formatTType(std::ostream& o, const epics::pvData::PVStructure::const_shared_pointer &pvStructure); void printUserTag(bool flag); std::ostream& printEnumT(std::ostream& o, epics::pvData::PVStructure const & pvEnumT); -std::ostream& printEnumT(std::ostream& o, epics::pvData::PVStructure::shared_pointer const & pvEnumT); -std::ostream& printTimeT(std::ostream& o, epics::pvData::PVStructure::shared_pointer const & pvTimeT); +std::ostream& printEnumT(std::ostream& o, epics::pvData::PVStructure::const_shared_pointer const & pvEnumT); +std::ostream& printTimeT(std::ostream& o, epics::pvData::PVStructure::const_shared_pointer const & pvTimeT); bool starts_with(const std::string& str, const std::string& part); diff --git a/src/client/client.cpp b/src/client/client.cpp index 8a68530..b83c17d 100644 --- a/src/client/client.cpp +++ b/src/client/client.cpp @@ -190,6 +190,15 @@ ClientProvider::ClientProvider(const std::string& providerName, THROW_EXCEPTION2(std::invalid_argument, providerName); } +ClientProvider::ClientProvider(const std::tr1::shared_ptr& provider) + :impl(new Impl) +{ + impl->provider = provider; + + if(!impl->provider) + THROW_EXCEPTION2(std::invalid_argument, "null ChannelProvider"); +} + ClientProvider::~ClientProvider() {} ClientChannel diff --git a/src/client/clientGet.cpp b/src/client/clientGet.cpp index aaa69fe..25f4d10 100644 --- a/src/client/clientGet.cpp +++ b/src/client/clientGet.cpp @@ -72,7 +72,10 @@ struct GetPutter : public pva::ChannelPutRequester, } virtual std::string getRequesterName() OVERRIDE FINAL - { return "GetPutter"; } + { + Guard G(mutex); + return op ? op->getChannel()->getRequesterName() : ""; + } virtual void channelPutConnect( const epics::pvData::Status& status, diff --git a/src/client/clientMonitor.cpp b/src/client/clientMonitor.cpp index 2d6e4fa..33fe905 100644 --- a/src/client/clientMonitor.cpp +++ b/src/client/clientMonitor.cpp @@ -89,7 +89,10 @@ struct Monitor::Impl : public pva::MonitorRequester } virtual std::string getRequesterName() OVERRIDE FINAL - { return "RPCer"; } + { + Guard G(mutex); + return chan ? chan->getRequesterName() : ""; + } virtual void monitorConnect(pvd::Status const & status, diff --git a/src/client/clientRPC.cpp b/src/client/clientRPC.cpp index 4dff91a..a10ac55 100644 --- a/src/client/clientRPC.cpp +++ b/src/client/clientRPC.cpp @@ -83,7 +83,10 @@ struct RPCer : public pva::ChannelRPCRequester, } virtual std::string getRequesterName() OVERRIDE FINAL - { return "RPCer"; } + { + Guard G(mutex); + return op ? op->getChannel()->getRequesterName() : ""; + } virtual void channelRPCConnect( const epics::pvData::Status& status, diff --git a/src/client/pva/client.h b/src/client/pva/client.h index 32f84b8..ff5df34 100644 --- a/src/client/pva/client.h +++ b/src/client/pva/client.h @@ -365,6 +365,7 @@ public: */ ClientProvider(const std::string& providerName, const std::tr1::shared_ptr& conf = std::tr1::shared_ptr()); + explicit ClientProvider(const std::tr1::shared_ptr& provider); ~ClientProvider(); /** Get a new Channel diff --git a/src/remoteClient/clientContextImpl.cpp b/src/remoteClient/clientContextImpl.cpp index a3c965e..9bf41ed 100644 --- a/src/remoteClient/clientContextImpl.cpp +++ b/src/remoteClient/clientContextImpl.cpp @@ -1935,19 +1935,21 @@ public: m_releasedCount = 0; m_reportQueueStateInProgress = false; - // reuse on reconnect - if (m_lastStructure.get() == 0 || - *(m_lastStructure.get()) != *(structure.get())) { while (!m_monitorQueue.empty()) m_monitorQueue.pop(); + m_freeQueue.clear(); + + m_up2datePVStructure.reset(); + for (int32 i = 0; i < m_queueSize; i++) { PVStructure::shared_pointer pvStructure = getPVDataCreate()->createPVStructure(structure); MonitorElement::shared_pointer monitorElement(new MonitorElement(pvStructure)); m_freeQueue.push_back(monitorElement); } + m_lastStructure = structure; } } @@ -2001,8 +2003,10 @@ public: // deserialize changedBitSet and data, and overrun bit set changedBitSet->deserialize(payloadBuffer, transport.get()); - if (m_up2datePVStructure && m_up2datePVStructure.get() != pvStructure.get()) + if (m_up2datePVStructure && m_up2datePVStructure.get() != pvStructure.get()) { + assert(pvStructure->getStructure().get()==m_up2datePVStructure->getStructure().get()); pvStructure->copyUnchecked(*m_up2datePVStructure, *changedBitSet, true); + } pvStructure->deserialize(payloadBuffer, transport.get(), changedBitSet.get()); overrunBitSet->deserialize(payloadBuffer, transport.get()); @@ -2874,23 +2878,23 @@ public: AbstractClientResponseHandler::handleResponse(responseFrom, transport, version, command, payloadSize, payloadBuffer); transport->ensureData(5); + int32 ioid = payloadBuffer->getInt(); + MessageType type = (MessageType)payloadBuffer->getByte(); - // TODO optimize - ResponseRequest::shared_pointer rr = _context.lock()->getResponseRequest(payloadBuffer->getInt()); - if (rr.get()) + string message = SerializeHelper::deserializeString(payloadBuffer, transport.get()); + + bool shown = false; + ResponseRequest::shared_pointer rr = _context.lock()->getResponseRequest(ioid); + if (rr) { - DataResponse::shared_pointer nrr = dynamic_pointer_cast(rr); - if (nrr.get()) - { - Requester::shared_pointer requester = nrr->getRequester(); - if (requester.get()) { - MessageType type = (MessageType)payloadBuffer->getByte(); - string message = SerializeHelper::deserializeString(payloadBuffer, transport.get()); - requester->message(message, type); - } + Requester::shared_pointer requester = rr->getRequester(); + if (requester) { + requester->message(message, type); + shown = true; } } - + if(!shown) + std::cerr<<"Orphaned server message "<startMessage((int8)18, sizeof(int32)/sizeof(int8) + 1); + control->startMessage((int8)CMD_MESSAGE, sizeof(int32)/sizeof(int8) + 1); buffer->putInt(_ioid); buffer->putByte((int8)_messageType); epics::pvData::SerializeHelper::serializeString(_message, buffer, control); diff --git a/src/server/responseHandlers.cpp b/src/server/responseHandlers.cpp index 8c30a33..4a53222 100644 --- a/src/server/responseHandlers.cpp +++ b/src/server/responseHandlers.cpp @@ -129,6 +129,7 @@ ServerResponseHandler::ServerResponseHandler(ServerContextImpl::shared_pointer c m_handlerTable[CMD_GET_FIELD].reset(new ServerGetFieldHandler(context)); /* 17 - get field response */ m_handlerTable[CMD_MESSAGE] = badResponse; /* 18 - message to Requester */ m_handlerTable[CMD_MULTIPLE_DATA] = badResponse; /* 19 - grouped monitors */ + m_handlerTable[CMD_RPC].reset(new ServerRPCHandler(context)); /* 20 - RPC response */ m_handlerTable[CMD_CANCEL_REQUEST].reset(new ServerCancelRequestHandler(context)); /* 21 - cancel request */ } diff --git a/src/utils/requester.cpp b/src/utils/requester.cpp index 6927e68..bd2920f 100644 --- a/src/utils/requester.cpp +++ b/src/utils/requester.cpp @@ -35,7 +35,7 @@ string getMessageTypeName(MessageType messageType) void Requester::message(std::string const & message,MessageType messageType) { - std::cerr << "[" << getRequesterName() << "] message(" << message << ", " << getMessageTypeName(messageType) << ")\n"; + std::cerr << "[" << getRequesterName() << "] " << getMessageTypeName(messageType) << " : " << message << "\n"; } }}