Skip to content

Conversation

@jsouter
Copy link

@jsouter jsouter commented Apr 28, 2025

Closes #528

Building with WITH_PVXS = YES (also require WITH_PVA = YES for time being) allows us to use pvxs, removing dependency on normativeTypesCPP, pvDataCPP, pvDatabaseCPP, pvAccessCPP.

WIP; currently testing against modified versions of ADSimDetector and pvaDriver, note that packages depending on this using PVXS will also need to be built with WITH_PVXS=YES in their config files. It may be that ultimately it would be better to move all the PVXS logic out of the separate *_pvxs.cpp files, but this was the simplest and most readable way to do it during development.

There are a few TODOs I will address, so making this a draft PR for now.

@jsouter jsouter changed the title Allow PVXS to be used in place of pvAccess libraries Convert NDPluginPva to use PVXS Apr 28, 2025
Copy link
Member

@ericonr ericonr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The first commit breaks the build, because it uses the copied (pvAccess) versions of the files, but links everything against PVXS. Maybe it would be best to do "copy files" -> "convert into PVXS" -> "hook up build system"?

Updating headers before implementation also breaks the build... I think it's easier to understand to have a single commit for implementation and header that can be verified at once, instead of splitting it.

What do you think?

@jsouter
Copy link
Author

jsouter commented Apr 28, 2025

The first commit breaks the build, because it uses the copied (pvAccess) versions of the files, but links everything against PVXS. Maybe it would be best to do "copy files" -> "convert into PVXS" -> "hook up build system"?

Updating headers before implementation also breaks the build... I think it's easier to understand to have a single commit for implementation and header that can be verified at once, instead of splitting it.

What do you think?

Thanks for the comments, I'll try and go through them soon. I think this is a reasonable change, also happy to squash things down more if needed.

@jsouter jsouter force-pushed the pvxs branch 3 times, most recently from f5a9c57 to 834fad7 Compare April 30, 2025 13:51
endif

ifeq ($(WITH_PVA),YES)
ifeq ($(WITH_PVXS),YES)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fyi. PVXS automatically provides some Make variables for testing presence and version.

https://github.com/epics-base/pvxs/blob/master/configure/CONFIG_PVXS_VERSION#L5-L14

eg.

ifdef PVXS_1_3_0

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the review!
This is good to know, though maybe we still want to be explicit about selecting the PVXS backend if both PVA and PVXS are provided, considering that it changes the public signature of the NTNDArrayConverter constructor. What do you think @ericonr?
I suppose with how the build works right now that any other modules using ADCore as a dependency (like pvaDriver) will only be able to see the libraries for one implementation at a time even though it can see both versions of the headers, probably I should fix it so that we build both libraries.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The form I would encourage is:

# Link QSRV
ifdef PVXS_1_3_0 # prefer v2
myioc_DBD += pvxsIoc.dbd
myioc_LIBS += pvxsIoc pvxs
else
ifdef EPICS_QSRV_MAJOR_VERSION # fallback to v1
myioc_LIBS += qsrv
myioc_LIBS += $(EPICS_BASE_PVA_CORE_LIBS)
myioc_DBD += PVAServerRegister.dbd
myioc_DBD += qsrv.dbd
endif
endif

Which detects either PVXS or pva2pva.

Comment on lines 51 to 60
bool NTNDArrayRecord::init ()
{
m_pv = pvxs::server::SharedPV(pvxs::server::SharedPV::buildReadonly());
m_pv.open(m_value);
m_server = pvxs::server::Server::fromEnv();
m_server.addPV(m_name, m_pv);
m_server.start(); // start is not blocking
m_converter.reset(new NTNDArrayConverter(m_value));
return true;
}
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To discuss: do we want to open a server per plugin instance?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should not need to create a Server at all. Instead use pvxs::ioc::server().

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mdavidsaver how does that interact with my comment about pvxsIoc.dbd #532 (comment) ? Is including pvxsIoc.dbd our responsibility, or the final integrator's when building the IOC?

And the documentation for the plugin should therefore state that the IOC needs to be built with QSRV2 and it shouldn't be disabled at runtime, right? Or should there be a fallback where if pvxs::ioc::server() throws, we create our own server?

Right now, wouldn't this throw anyway? The documentation states

This Server instance is created during a registrar function, started by the initHookAfterCaServerRunning phase of iocInit().

But NTNDArrayRecord::init is called by NTNDArrayRecord::create, which is called by the NDPluginPva initializer, which should always happen before iocInit.

Comment on lines 7 to 21
LIBRARY_IOC += ntndArrayConverter
INC += ntndArrayConverterAPI.h
ifeq ($(WITH_PVXS), YES)
INC += ntndArrayConverter.h
LIB_SRCS += ntndArrayConverter_pvxs.cpp
USR_CPPFLAGS += -DHAVE_PVXS
else
INC += ntndArrayConverter.h
LIB_SRCS += ntndArrayConverter.cpp
endif
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Currently this builds only one implementation, should we be building both versions to allow users to switch between pvAccess and pvxs without rebuilding ADCore?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel like that's too much flexibility without clear differences between versions. IMO choosing between one or the other should be left to the EPICS "distributor", to simplify things.

But I am biased, because I didn't even think it was necessary to keep supporting the old PVA libraries, since PVXS can always be installed as a separate module.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, I'd vote for PVXS only unless there is a real need for the old libraries. The network protocol is backwards compatible (AFAIK)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would prefer compatibility with the old libraries to be retained, at least until PVXS is made part of base. We have local areaDetector applications that make use of PVAccess/PVData libraries as well as the pvaDriver and pvaPlugin modules packaged with areaDetector. So we'd need to port our local applications to PVXS in order to update areaDetector, although I suppose using both at the same time is possible. Ideally we would port our local applications, and it doesn't seem difficult to do this. It's just we have at least 60+ IOC applications spread across 30 beamlines, and it would be a major upgrade.

Copy link
Author

@jsouter jsouter May 6, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this may also be the first required use of C++11 in ADCore (if a PVA plugin is needed), before moving my test build to a containerised environment I was seeing linker errors trying to compile this against Diamond's released support modules (asyn and busy iirc) which were compiled using an older version of GCC - I don't think pvAccessCPP et al. require C++11.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree. Since the 2 PVA libraries have different features it makes sense to support both. It also does not require rebuilding ADCore when switching between them.

Yes. For example, APS DAQ system still uses old libraries, and if the support for those is dropped, there would have to be lots of work required on our end if we wanted to upgrade to the new ADCore version.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The old plugin relies on PvDatabase server, which has the ability to distribute different data to different clients (e.g., image1 to client1, image2 to client2, etc.) if clients request that.

Is this a PVXS limitation that's being worked on, or just something nice to have that people will eventually be able to live without?

I could imagine emulating it by having a fanout to multiple NDPluginPVXS instances, but it definitely doesn't have the same ergonomics...

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The PVDatabase server has a pretty convenient plugin framework that allows one to develop different server side plugins, which in turn allows one to control what updates get returned to clients that monitor the given channel. In this particular case, the plugin I mentioned is this one: https://github.com/epics-base/pvDatabaseCPP/blob/master/documentation/dataDistributorPlugin.md

This feature has nothing to do with the PVA vs PVXS protocol itself. Just like the plugin framework in PVDatabase server, something like this could be added to PVXS-based servers. I do not know whether there are plans for doing so or not. I find it very useful for real-time processing of fast detector data, which could not be handled otherwise by a single client. Some examples of possible workflows are here: https://github.com/epics-base/pvaPy/blob/master/documentation/streamingFramework.md.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, so it sounds like the best course for the time being is to provide an NDPluginPVA, NDPluginPVXS and the required NTNDArrayConverter and NTNDArrayConverterPVXS (or some similar name), and maybe we have completely separate header files for each so that we can choose to provide one, both or neither simultaneously based on CONFIG flags.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The old plugin relies on PvDatabase server, which has the ability to distribute different data to different clients ...

The PVXS SharedPV object specifically does not do this. Thus the "shared" part of its name. PVXS also provides a lower level API on which SharedPV is built (also p4p.gw). The spam.cpp test program is one example usage, providing a unique deluge to each subscriber to test per-subscription flow control.

@MarkRivers
Copy link
Member

I think this may also be the first required use of C++11 in ADCore (if a PVA plugin is needed),

NDPluginBasePixel in ADCore requires C++11.

These are all of the modules in my areaDetector tree that have c++ flags:

(base) [epics@corvette areaDetector]$ find . -name Makefile -exec grep -H 'c++' {} \;
./ADPICam/PICamApp/src/Makefile:USR_CXXFLAGS_Linux += -std=c++1y
./ADCore/ADApp/pluginSrc/Makefile:NDPluginBadPixel_CXXFLAGS_Linux += -std=c++11
./ADCore/ADApp/pluginSrc/Makefile:NDPluginBadPixel_CXXFLAGS_Darwin += -std=c++11
./ADCore/ADApp/pluginTests/Makefile:  USR_CXXFLAGS_Linux += -std=c++11
./ADUVC/uvcApp/src/Makefile:USR_CPPFLAGS += -std=c++11
./ADGenICam/GenICamApp/src/Makefile:USR_CXXFLAGS_Linux += -std=c++11
./ADLambda/LambdaApp/src/Makefile:USR_CXXFLAGS += -std=c++17
./ADAravis/aravisApp/src/Makefile:USR_CXXFLAGS_Linux += -std=c++11
./ADPluginBar/barApp/barSrc/Makefile:CODE_CXXFLAGS=-std=c++11
./ADTimePix3/tpx3App/src/Makefile:USR_CPPFLAGS += -std=c++11
./ADSpinnaker/spinnakerApp/src/Makefile:USR_CXXFLAGS_Linux += -std=c++11 -Wno-unknown-pragmas
./ADSpinnaker/spinnakerApp/exampleSrc/Makefile:USR_CXXFLAGS_Linux += -std=c++11 -Wno-unknown-pragmas

@jsouter jsouter force-pushed the pvxs branch 2 times, most recently from 4d3e6af to 3c16751 Compare May 9, 2025 13:09
jsouter

This comment was marked as duplicate.

@jsouter jsouter marked this pull request as ready for review June 11, 2025 10:28
@jsouter
Copy link
Author

jsouter commented Jun 12, 2025

Docs will need updating.
Also: need to make a dev build which contains both the old pva packages and PVXS, and check build/deployments work as expected, especially with regards to the pvxs ioc server.

edit: okay, my container was doing some configure/RULES magic and automatically including and link pvxsIoc without me realising, but it is of course needed to instantiate pvxs::ioc::server, also have to fix a few more name collisions to allow both converters to be compiled simultaneously.

@jsouter jsouter requested a review from MarkRivers July 11, 2025 09:15
@MarkRivers
Copy link
Member

This PR looks Ok to me. Do you want me to merge it?

@jsouter
Copy link
Author

jsouter commented Jul 17, 2025

Just pushed some docs, not sure why that didn't get pushed before.
I think there are a few small changes I need to consider/check
- whether I need to make a separate .template for NDPluginPvxs to get around an asyn port name collision in the case we have a pva and pvxs plugin loaded simultaneously should be fine, plugins would be using different ports

  • Need to check if the links work correctly in the rendered docs
  • Also need to decide if we are happy to include pvxsIoc as a dependency in a support module, it seems like the most straightforward approach but goes against recommendations from the pvxs docs

@jwlodek
Copy link
Member

jwlodek commented Aug 7, 2025

I think you can probably just link to pvxsIOC / include the pvxsIOC.dbd in the commonDriverMakefile without needing do it into the library

@henriquesimoes
Copy link
Member

henriquesimoes commented Oct 2, 2025

@mdavidsaver, @MarkRivers, @sveseli, can you take a look in this final version of the PR? It should be ready for merging from @jsouter view.

@sveseli
Copy link
Contributor

sveseli commented Oct 2, 2025

@mdavidsaver, @MarkRivers, @sveseli, can you take a look in this final version of the PR. I should be ready for merging from @jsouter view.

Looks okay to me.

Copy link
Member

@MarkRivers MarkRivers left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me. I will merge it.

@MarkRivers MarkRivers merged commit a44677e into areaDetector:master Dec 30, 2025
@MarkRivers
Copy link
Member

I merged this PR and built pvxs with no errors. I modified the following files in areaDetector/configure/

CONFIG_SITE.local to have these lines:

# EPICS PVXS can be used for NDPluginPva, and qsrv
WITH_PVXS = YES

RELEASE_LIBS.local to have this line

PVXS=$(SUPPORT)/pvxs

RELEASE_PRODS.local to have this line:

# PXVS is an optional package for NDPluginPVA and pvaServer
PVXS=$(SUPPORT)/pvxs

ADCore builds fine. However, when I build ADSimDetector or any other driver I get these errors when linking the IOC. I have added line breaks to make the output more legible.

/usr/bin/g++ -o simDetectorApp -Wl,-Bstatic -L/home/epics/devel/areaDetector-3-14/ADSimDetector/lib/linux-x86_64 
-L/home/epics/devel/areaDetector-3-14/ADSimDetector/iocs/simDetectorIOC/lib/linux-x86_64 
-L/corvette/home/epics/devel/areaDetector-3-14/ADCore/lib/linux-x86_64 
-L/corvette/home/epics/devel/areaDetector-3-14/ADSupport/lib/linux-x86_64 
-L/corvette/home/epics/devel/asyn-4-45/lib/linux-x86_64 
-L/corvette/home/epics/devel/autosave-5-11/lib/linux-x86_64 
-L/corvette/home/epics/devel/busy-1-7-3/lib/linux-x86_64 
-L/corvette/home/epics/devel/calc-3-7-4/lib/linux-x86_64 
-L/corvette/home/epics/devel/devIocStats-3-1-16/lib/linux-x86_64 
-L/corvette/home/epics/devel/pvxs/lib/linux-x86_64 
-L/corvette/home/epics/devel/seq-2-2-9/lib/linux-x86_64 
-L/corvette/home/epics/devel/sscan-2-11-5/lib/linux-x86_64 
-L/corvette/usr/local/epics-devel/base-7.0.9/lib/linux-x86_64 
-Wl,-rpath,/home/epics/devel/areaDetector-3-14/ADSimDetector/lib/linux-x86_64 
-Wl,-rpath,/home/epics/devel/areaDetector-3-14/ADSimDetector/iocs/simDetectorIOC/lib/linux-x86_64 
-Wl,-rpath,/corvette/home/epics/devel/areaDetector-3-14/ADCore/lib/linux-x86_64 
-Wl,-rpath,/corvette/home/epics/devel/areaDetector-3-14/ADSupport/lib/linux-x86_64 
-Wl,-rpath,/corvette/home/epics/devel/asyn-4-45/lib/linux-x86_64 
-Wl,-rpath,/corvette/home/epics/devel/autosave-5-11/lib/linux-x86_64 
-Wl,-rpath,/corvette/home/epics/devel/busy-1-7-3/lib/linux-x86_64 
-Wl,-rpath,/corvette/home/epics/devel/calc-3-7-4/lib/linux-x86_64 
-Wl,-rpath,/corvette/home/epics/devel/devIocStats-3-1-16/lib/linux-x86_64 
-Wl,-rpath,/corvette/home/epics/devel/pvxs/lib/linux-x86_64 
-Wl,-rpath,/corvette/home/epics/devel/seq-2-2-9/lib/linux-x86_64 
-Wl,-rpath,/corvette/home/epics/devel/sscan-2-11-5/lib/linux-x86_64 
-Wl,-rpath,/corvette/usr/local/epics-devel/base-7.0.9/lib/linux-x86_64          
-rdynamic -m64          simDetectorApp_registerRecordDeviceDriver.o simDetectorAppMain.o   -lsimDetector 
-lNDPlugin -lADBase -lqsrv -lntndArrayConverter -lnt -lpvDatabase -lpvAccessIOC -lpvAccessCA -lpvAccess 
-lpvData -lpvxs -lpvxsIoc -lntndArrayConverterPvxs -lnetCDF -lMagick++ -lcoders -lMagick -ljbig -ljp2 -lbzlib 
-lpng -lwebp -llcms -lttf -lwmf -lfilters -lnanohttp_stream -ltiff -lxml2 -lNeXus -lhdf5 -lhdf5_hl -lbitshuffle -lblosc 
-lszip -lzlib -ljpeg -lasyn -lautosave -lbusy -lcalc -ldevIocStats -lsscan -lseq -lpv -ldbRecStd -ldbCore -lca -lCom 
-Wl,-Bdynamic -lboost_system -lX11 -lXext -levent_core -levent_pthreads -lpthread   -lreadline -lm -lrt -ldl -lgcc
/bin/ld: /corvette/home/epics/devel/pvxs/lib/linux-x86_64/libpvxsIoc.a(pvalink.o): in function `dbpvar':
/home/epics/devel/pvxs/ioc/O.linux-x86_64/../pvalink.cpp:186: multiple definition of `dbpvar'; /corvette/usr/local/epics-devel/base-7.0.9/lib/linux-x86_64/libqsrv.a(pvalink.o):/usr/local/epics-devel/base-7.0.9/modules/pva2pva/pdbApp/O.linux-x86_64/../pvalink.cpp:203: first defined here
/bin/ld: /corvette/home/epics/devel/pvxs/lib/linux-x86_64/libpvxsIoc.a(pvalink_channel.o):/home/epics/devel/pvxs/ioc/O.linux-x86_64/../pvalink_channel.cpp:20: multiple definition of `pvaLinkNWorkers'; /corvette/usr/local/epics-devel/base-7.0.9/lib/linux-x86_64/libqsrv.a(pvalink_channel.o):/usr/local/epics-devel/base-7.0.9/modules/pva2pva/pdbApp/O.linux-x86_64/../pvalink_channel.cpp:8: first defined here
/bin/ld: /corvette/home/epics/devel/pvxs/lib/linux-x86_64/libpvxsIoc.a(pvalink_jlif.o):/home/epics/devel/pvxs/ioc/O.linux-x86_64/../pvalink_jlif.cpp:310: multiple definition of `pvar_jlif_lsetPVA'; /corvette/usr/local/epics-devel/base-7.0.9/lib/linux-x86_64/libqsrv.a(pvalink.o):/usr/local/epics-devel/base-7.0.9/modules/pva2pva/pdbApp/O.linux-x86_64/../pvalink.cpp:349: first defined here
/bin/ld: /corvette/home/epics/devel/areaDetector-3-14/ADSupport/lib/linux-x86_64/libjp2.a(jas_stream.o): in function `jas_stream_tmpfile':
/home/epics/devel/areaDetector-3-14/ADSupport/supportApp/GraphicsMagickSrc/jp2/src/libjasper/O.linux-x86_64/../../../../../../supportApp/GraphicsMagickSrc/jp2/src/libjasper/base/jas_stream.c:368: warning: the use of `tmpnam' is dangerous, better use `mkstemp'
/bin/ld: /corvette/home/epics/devel/pvxs/lib/linux-x86_64/libpvxsIoc.a(iochooks.o): in function `pvxs::ioc::pvxsi()':
/home/epics/devel/pvxs/ioc/O.linux-x86_64/../iochooks.cpp:212: undefined reference to `pvxs::target_information(std::ostream&)'
/bin/ld: /corvette/home/epics/devel/pvxs/lib/linux-x86_64/libpvxsIoc.a(pvalink.o): in function `pvxs::client::Config::build() const':
/home/epics/devel/pvxs/ioc/O.linux-x86_64/../../src/pvxs/client.h:1076: undefined reference to `pvxs::client::Context::Context(pvxs::client::Config const&)'
/bin/ld: /corvette/home/epics/devel/pvxs/lib/linux-x86_64/libpvxsIoc.a(pvalink.o): in function `pvxs::ioc::linkGlobal_t::alloc()':
/home/epics/devel/pvxs/ioc/O.linux-x86_64/../pvalink.cpp:60: undefined reference to `pvxs::client::Context::~Context()'
/bin/ld: /corvette/home/epics/devel/pvxs/lib/linux-x86_64/libpvxsIoc.a(pvalink.o): in function `pvxs::client::Config::build() const':
/home/epics/devel/pvxs/ioc/O.linux-x86_64/../../src/pvxs/client.h:1076: undefined reference to `pvxs::client::Context::Context(pvxs::client::Config const&)'
/bin/ld: /corvette/home/epics/devel/pvxs/lib/linux-x86_64/libpvxsIoc.a(pvalink.o): in function `pvxs::ioc::linkGlobal_t::alloc()':
/home/epics/devel/pvxs/ioc/O.linux-x86_64/../pvalink.cpp:62: undefined reference to `pvxs::client::Context::~Context()'
/bin/ld: /corvette/home/epics/devel/pvxs/lib/linux-x86_64/libpvxsIoc.a(pvalink.o): in function `pvxs::ioc::testqsrvWaitForLinkConnected(link*, bool)':
/home/epics/devel/pvxs/ioc/O.linux-x86_64/../pvalink.cpp:134: undefined reference to `pvxs::client::Context::hurryUp()'
/bin/ld: /corvette/home/epics/devel/pvxs/lib/linux-x86_64/libpvxsIoc.a(pvalink_channel.o): in function `pvxs::ioc::linkGlobal_t::~linkGlobal_t()':
/home/epics/devel/pvxs/ioc/O.linux-x86_64/../pvalink_channel.cpp:51: undefined reference to `pvxs::client::Context::~Context()'
/bin/ld: /corvette/home/epics/devel/pvxs/lib/linux-x86_64/libpvxsIoc.a(pvalink_channel.o): in function `pvxs::client::detail::CommonBuilder<pvxs::client::PutBuilder, pvxs::client::detail::PRBase>::rawRequest(pvxs::Value const&)':
/home/epics/devel/pvxs/ioc/O.linux-x86_64/../../src/pvxs/client.h:688: undefined reference to `pvxs::client::detail::CommonBase::_rawRequest(pvxs::Value const&)'
/bin/ld: /corvette/home/epics/devel/pvxs/lib/linux-x86_64/libpvxsIoc.a(pvalink_channel.o): in function `pvxs::ioc::pvaLinkChannel::put(bool)':
/home/epics/devel/pvxs/ioc/O.linux-x86_64/../pvalink_channel.cpp:278: undefined reference to `pvxs::client::PutBuilder::exec()'
/bin/ld: /corvette/home/epics/devel/pvxs/lib/linux-x86_64/libpvxsIoc.a(pvalink_channel.o): in function `pvxs::client::detail::CommonBuilder<pvxs::client::PutBuilder, pvxs::client::detail::PRBase>::~CommonBuilder()':
/home/epics/devel/pvxs/ioc/O.linux-x86_64/../../src/pvxs/client.h:649: undefined reference to `pvxs::client::detail::PRBase::~PRBase()'
/bin/ld: /corvette/home/epics/devel/pvxs/lib/linux-x86_64/libpvxsIoc.a(pvalink_channel.o): in function `pvxs::client::detail::CommonBuilder<pvxs::client::MonitorBuilder, pvxs::client::detail::CommonBase>::rawRequest(pvxs::Value const&)':
/home/epics/devel/pvxs/ioc/O.linux-x86_64/../../src/pvxs/client.h:688: undefined reference to `pvxs::client::detail::CommonBase::_rawRequest(pvxs::Value const&)'
/bin/ld: /corvette/home/epics/devel/pvxs/lib/linux-x86_64/libpvxsIoc.a(pvalink_channel.o): in function `pvxs::ioc::pvaLinkChannel::open()':
/home/epics/devel/pvxs/ioc/O.linux-x86_64/../pvalink_channel.cpp:123: undefined reference to `pvxs::client::MonitorBuilder::exec()'
/bin/ld: /corvette/home/epics/devel/pvxs/lib/linux-x86_64/libpvxsIoc.a(pvalink_channel.o): in function `pvxs::client::detail::CommonBuilder<pvxs::client::MonitorBuilder, pvxs::client::detail::CommonBase>::~CommonBuilder()':
/home/epics/devel/pvxs/ioc/O.linux-x86_64/../../src/pvxs/client.h:649: undefined reference to `pvxs::client::detail::CommonBase::~CommonBase()'
/bin/ld: /corvette/home/epics/devel/pvxs/lib/linux-x86_64/libpvxsIoc.a(pvalink_channel.o): in function `pvxs::client::detail::CommonBuilder<pvxs::client::PutBuilder, pvxs::client::detail::PRBase>::~CommonBuilder()':
/home/epics/devel/pvxs/ioc/O.linux-x86_64/../../src/pvxs/client.h:649: undefined reference to `pvxs::client::detail::PRBase::~PRBase()'
/bin/ld: /corvette/home/epics/devel/pvxs/lib/linux-x86_64/libpvxsIoc.a(pvalink_channel.o): in function `pvxs::client::detail::CommonBuilder<pvxs::client::MonitorBuilder, pvxs::client::detail::CommonBase>::~CommonBuilder()':
/home/epics/devel/pvxs/ioc/O.linux-x86_64/../../src/pvxs/client.h:649: undefined reference to `pvxs::client::detail::CommonBase::~CommonBase()'
/bin/ld: /corvette/home/epics/devel/pvxs/lib/linux-x86_64/libpvxsIoc.a(pvalink_channel.o): in function `pvxs::ioc::linkGlobal_t::linkGlobal_t()':
/home/epics/devel/pvxs/ioc/O.linux-x86_64/../pvalink_channel.cpp:47: undefined reference to `pvxs::client::Context::~Context()'
/bin/ld: /corvette/home/epics/devel/pvxs/lib/linux-x86_64/libpvxsIoc.a(pvalink_channel.o):(.data.rel.local.DW.ref._ZTIN4pvxs6client10DisconnectE[DW.ref._ZTIN4pvxs6client10DisconnectE]+0x0): undefined reference to `typeinfo for pvxs::client::Disconnect'
collect2: error: ld returned 1 exit status
make[5]: *** [/corvette/usr/local/epics-devel/base-7.0.9/configure/RULES_BUILD:232: simDetectorApp] Error 1
make[5]: Leaving directory '/home/epics/devel/areaDetector-3-14/ADSimDetector/iocs/simDetectorIOC/simDetectorApp/src/O.linux-x86_64'
make[4]: *** [/corvette/usr/local/epics-devel/base-7.0.9/configure/RULES_ARCHS:58: install.linux-x86_64] Error 2
make[4]: Leaving directory '/home/epics/devel/areaDetector-3-14/ADSimDetector/iocs/simDetectorIOC/simDetectorApp/src'
make[3]: *** [/corvette/usr/local/epics-devel/base-7.0.9/configure/RULES_DIRS:85: src.install] Error 2
make[3]: Leaving directory '/home/epics/devel/areaDetector-3-14/ADSimDetector/iocs/simDetectorIOC/simDetectorApp'
make[2]: *** [/corvette/usr/local/epics-devel/base-7.0.9/configure/RULES_DIRS:85: simDetectorApp.install] Error 2
make[2]: Leaving directory '/home/epics/devel/areaDetector-3-14/ADSimDetector/iocs/simDetectorIOC'
make[1]: *** [/corvette/usr/local/epics-devel/base-7.0.9/configure/RULES_DIRS:85: simDetectorIOC.install] Error 2
make[1]: Leaving directory '/home/epics/devel/areaDetector-3-14/ADSimDetector/iocs'
make: *** [/corvette/usr/local/epics-devel/base-7.0.9/configure/RULES_DIRS:85: iocs.install] Error 2

Am I doing something wrong?

@MarkRivers
Copy link
Member

I believe many of the errors are a result of the order of the pvxs and pvxsIoc libraries being in the incorrect order in commonLibraryMakefile. I reversed the order:

diff --git a/ADApp/commonDriverMakefile b/ADApp/commonDriverMakefile
index 3f68af7b..a579265f 100644
--- a/ADApp/commonDriverMakefile
+++ b/ADApp/commonDriverMakefile
@@ -40,8 +40,8 @@ endif
 ifeq ($(WITH_PVXS),YES)
   $(DBD_NAME)_DBD += NDPluginPvxs.dbd
   $(DBD_NAME)_DBD += pvxsIoc.dbd
-  PROD_LIBS += pvxs
   PROD_LIBS += pvxsIoc
+  PROD_LIBS += pvxs
   PROD_LIBS += ntndArrayConverterPvxs
 endif

That eliminates all of the undefined references. However, there is still a problem with multiply defined references to dbpvar, pvaLinkNWorkers, and pvar_jlif_lsetPVA.

/bin/ld: /corvette/home/epics/devel/pvxs/lib/linux-x86_64/libpvxsIoc.a(pvalink.o): in function `dbpvar':
/home/epics/devel/pvxs/ioc/O.linux-x86_64/../pvalink.cpp:186: multiple definition of `dbpvar'; /corvette/usr/local/epics-devel/base-7.0.9/lib/linux-x86_64/libqsrv.a(pvalink.o):/usr/local/epics-devel/base-7.0.9/modules/pva2pva/pdbApp/O.linux-x86_64/../pvalink.cpp:203: first defined here
/bin/ld: /corvette/home/epics/devel/pvxs/lib/linux-x86_64/libpvxsIoc.a(pvalink_channel.o):/home/epics/devel/pvxs/ioc/O.linux-x86_64/../pvalink_channel.cpp:20: multiple definition of `pvaLinkNWorkers'; /corvette/usr/local/epics-devel/base-7.0.9/lib/linux-x86_64/libqsrv.a(pvalink_channel.o):/usr/local/epics-devel/base-7.0.9/modules/pva2pva/pdbApp/O.linux-x86_64/../pvalink_channel.cpp:8: first defined here
/bin/ld: /corvette/home/epics/devel/pvxs/lib/linux-x86_64/libpvxsIoc.a(pvalink_jlif.o):/home/epics/devel/pvxs/ioc/O.linux-x86_64/../pvalink_jlif.cpp:310: multiple definition of `pvar_jlif_lsetPVA'; /corvette/usr/local/epics-devel/base-7.0.9/lib/linux-x86_64/libqsrv.a(pvalink.o):/usr/local/epics-devel/base-7.0.9/modules/pva2pva/pdbApp/O.linux-x86_64/../pvalink.cpp:349: first defined here
collect2: error: ld returned 1 exit status

@jsouter did you test building and running detectors with this PR?

@mdavidsaver
Copy link
Contributor

... multiple definition of `dbpvar'; .../libqsrv.a(pvalink.o):/.../pvalink.cpp:203: first defined here
/bin/ld: /.../libpvxsIoc.a(pvalink_channel.o):/.../pvalink_channel.cpp:20...

These are symbol conflicts between libqsrv and pvxsIoc. I thought I had avoided these, but clearly not. It looks like they got in the PVA link support.

At the moment, it will not be possible to link both QSRV1 and QSRV2 into the same executable.

@mdavidsaver
Copy link
Contributor

At the moment, it will not be possible to link both QSRV1 and QSRV2 into the same executable.

I have renamed the conflicting symbols, and re-verified that QSRV 1 and 2 can reasonably coexist. epics-base/pvxs#147

@MarkRivers
Copy link
Member

@mdavidsaver thanks for that. I pulled the master branch, and the link errors are fixed.

I modified commonPlugins.cmd to add NDPluginPvxs and tried it with the simDetector. This is the output from the startup script when running with gdb:

# Optional: load NDPluginPva and/or NDPluginPvxs plugins
#NDPvaConfigure("PVA1", $(QSIZE), 0, "$(PORT)", 0, $(PREFIX)Pva1:Image, 0, 0, 0)
NDPvxsConfigure("PVA1", 20, 0, "SIM1", 0, 13SIM1:Pva1:Image, 0, 0, 0)
[New Thread 0x7fff66615640 (LWP 1369994)]
[New Thread 0x7fff66514640 (LWP 1369995)]
[New Thread 0x7fff66413640 (LWP 1369996)]
2025/12/31 11:23:47.568 NDPluginDriver::deleteCallbackThreads timeout waiting for plugin thread 0 exit message
[Thread 0x7fff66413640 (LWP 1369996) exited]
[Thread 0x7fff66514640 (LWP 1369995) exited]
c++ error: No Instance

It appears that it fails to create the plugin. It crashes later, but the backtrace is not very useful.

@MarkRivers
Copy link
Member

The failure above goes away if PVA is not included in the build, only PVXS. I am opening 2 new issues.

@mdavidsaver
Copy link
Contributor

The failure above goes away if PVA is not included in the build, only PVXS. I am opening 2 new issues.

A normal situation will be to link in one or the other. Removing the linking errors in about making way for a clearer runtime error/warning.

The No Instance error come from server() in this line if QSRV2 has not started. In this case due to presence of QSRV1.

pvxs::ioc::server().addPV(m_name, m_pv);

The C++ error prefix from the IOCsh mechanics.

@mdavidsaver
Copy link
Contributor

mdavidsaver commented Dec 31, 2025

Looking back to #532 (comment), I recommend using some Makefile ifdef to detect the presence of PVXS and/or pva2pva when linking an executable.

ifdef PVXS_1_3_0
# QSRV2 present
else
# no QSRV2, test for QSRV1
ifdef EPICS_QSRV_MAJOR_VERSION # fallback to v1
# QSRV1 present
endif
endif

@mdavidsaver
Copy link
Contributor

mdavidsaver commented Dec 31, 2025

... when linking an executable.

This might be done in commonDriverMakefile with some logic like:

# detect presence of QSRV1 or 2.  Prefer QSRV2.
ifdef PVXS_1_3_0
HAVE_PVXS = YES
else
ifdef EPICS_QSRV_MAJOR_VERSION
HAVE_PVA2PVA = YES
endif
endif

ifeq ($(HAVE_PVA2PVA)$(WITH_QSRV),YESYES)
# ... existing
endif

# unchanged
ifeq ($(WITH_PVA),YES)
# ... existing
endif

ifeq ($(HAVE_PVXS)$(WITH_PVXS),YESYES)
# ... existing
endif

edit: updated logic to make clear that the conflict is between libpvxsIoc and libqsrv. libpvxs and libpvAccess can coexist without issue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Convert NDPluginPva to use PVXS

9 participants