ioc: revise qsrv 2 prototype

ioc: check for mis-matched onStartSubscription()/onDisableSubscription()

ioc: fix subscription lifetime

ioc: catch exceptions in dbEvent callbacks

ioc: avoid unnecessary virtual

ioc: minor

ioc: fix qsrv -S

ioc: qsrvGroupSourceInit() catch+log

ioc: runOnServer avoid std::function

ioc: cleanup and simplifications.

Avoid some redundant std::map lookups.
Make Group partially const to prevent implicit ctor.

ioc: avoid typedefs only used once

ioc: overhaul Group::show().  shows triggers

ioc: MappingType

ioc: pvxsgl -> pvxgl

ioc: separate group config singleton from server singleton

ioc: remove unnecessary forward declarations

ioc: restructure pvxsInitHook

ioc: qsrv runtime disable by default

ioc: compat w/ older Base

ioc: link pvxsIoc w/ DB libs

ioc: Channel proper detection of invalid PV

ioc: no need to keep vector<dbCommon*> around

ioc: fix initial group update for mappings w/o dbChannel

ioc: redo testing

split out group tests, only run with Base >= 7.0

ioc: minor

ioc: loc_bad_alloc

ioc: avoid symbol/DTYP clash with pva2pva

ioc: test record alias in group json

ioc: test put failure when SPC_NOMOD and DISP=1

ioc: test channel filters

ioc: unnecessary capture

ioc: avoid sharing Value between multiple subscriptions

It is possible to create two subscriptions through the same channel.

ioc: group subscription include queueSize

ioc: eliminate unused atomicMonitor

ioc: consolidate GroupSource::get()

avoid some indirection

ioc: pvRequest override of atomicPutGet

ioc: fix group non-atomic put

ioc: test asTrap hooks

ioc: test putOrder also sets field order

ioc: simplify GroupConfigProcessor::loadConfigFiles()

Also ensure that groupMapMutex is held

ioc: testqgroup cover JSON def.

ioc: dbLoadGroup() use macros

ioc: pvxsl() take integer argument

ioc: display.form and info(Q:form

ioc: "NO_ALARM" -> ""

ioc: use dbServer

at least for informational callbacks.

ioc: consolidate createRequestAndSubscriptionHandlers()

ioc: eliminate ChannelAndLock

properties dbChannel doesn't need a separate DBManyLock

ioc: test that putOrder also controls field order

ioc: MappingType -> MappingInfo

Handle info(Q:time:tag
Add +type:"const"

ioc: cleanup includes

ioc: test dbNotifyCancel()

ioc: inline checkForTrailingCommentsAtEnd()
This commit is contained in:
Michael Davidsaver
2023-02-21 12:10:31 -08:00
parent 832c76d6e4
commit afafa09547
66 changed files with 4109 additions and 3145 deletions
+138 -77
View File
@@ -9,8 +9,11 @@
#include <vector>
#include <string.h>
#include <epicsExport.h>
#include <epicsString.h>
#include <iocsh.h>
#include <initHooks.h>
@@ -21,7 +24,11 @@
#include "groupconfigprocessor.h"
#include "iocshcommand.h"
// must include after log.h has been included to avoid clash with printf macro
#if EPICS_VERSION_INT < VERSION_INT(7, 0, 3, 1)
# define iocshSetError(ret) do { (void)ret; }while(0)
#endif
// include last to avoid clash of #define printf with other headers
#include <epicsStdio.h>
namespace pvxs {
namespace ioc {
@@ -31,10 +38,10 @@ namespace ioc {
*
* @param jsonFileName
*/
void dbLoadGroupCmd(const char* jsonFileName) {
(void)dbLoadGroup(jsonFileName);
auto gp = GroupConfigProcessor();
gp.loadConfigFiles();
static
void dbLoadGroupCmd(const char* jsonFileName, const char *macros) {
iocshSetError(dbLoadGroup(jsonFileName, macros));
GroupConfigProcessor().loadConfigFiles();
}
/**
@@ -44,70 +51,113 @@ void dbLoadGroupCmd(const char* jsonFileName) {
* @param level optional depth to show details for
* @param pattern optionally only show records matching the regex pattern
*/
static
void pvxsgl(int level, const char* pattern) {
runOnPvxsServer(([level, &pattern](IOCServer* pPvxsServer) {
try {
// Default pattern to match everything
if (!pattern) {
pattern = "";
}
try {
// Default pattern to match everything
if (!pattern) {
pattern = "";
}
{
epicsGuard<epicsMutex> G(pPvxsServer->groupMapMutex);
{
auto& config(IOCGroupConfig::instance());
epicsGuard<epicsMutex> G(config.groupMapMutex);
// For each group
for (auto& mapEntry: pPvxsServer->groupMap) {
auto& groupName = mapEntry.first;
auto& group = mapEntry.second;
// if no pattern specified or the pattern matches
if (!pattern[0] || !!epicsStrGlobMatch(groupName.c_str(), pattern)) {
// Print the group name
printf("%s\n", groupName.c_str());
// print sub-levels if required
if (level > 0) {
group.show(level);
}
// For each group
for (auto& mapEntry: config.groupMap) {
auto& groupName = mapEntry.first;
auto& group = mapEntry.second;
// if no pattern specified or the pattern matches
if (!pattern[0] || !!epicsStrGlobMatch(groupName.c_str(), pattern)) {
// Print the group name
printf("%s\n", groupName.c_str());
// print sub-levels if required
if (level > 0) {
group.show(level);
}
}
}
} catch (std::exception& e) {
fprintf(stderr, "%s\n", e.what());
}
}));
} catch (std::exception& e) {
fprintf(stderr, "%s\n", e.what());
}
}
/**
* Load JSON group definition file.
* This function does not actually parse the given file, but adds it to the list of files to be loaded,
* at the appropriate time in the startup process.
*
* @param jsonFilename the json file containing the group definitions. If filename is a dash or a dash then star, the list of
* files is cleared. If it starts with a dash followed by a filename then file is removed from the list. Otherwise
* the filename is added to the list of files to be loaded.
* @return 0 for success, 1 for failure
*/
long dbLoadGroup(const char* jsonFilename) {
static
const auto dbLoadGroupMsg =
"dbLoadGroup(\"file.json\")\n"
"dbLoadGroup(\"file.json\", \"MAC=value,...\")\n"
"\n"
"Load additional DB group definitions from file.\n"
"\n"
"dbLoadGroup(\"-*\")\n"
"dbLoadGroup(\"-file.json\")\n"
"dbLoadGroup(\"-file.json\", \"MAC=value,...\")\n"
"\n"
"Remove all, or one, previously added group definitions.\n"
;
long dbLoadGroup(const char* jsonFilename, const char* macros) {
try {
if (!jsonFilename || !jsonFilename[0]) {
printf("dbLoadGroup(\"file.json\")\n"
"Load additional DB group definitions from file.\n");
fprintf(stderr, "Missing filename\n");
fprintf(stderr, "%s\n"
"Error: Missing required JSON filename\n", dbLoadGroupMsg);
return 1;
}
if(!macros)
macros = "";
runOnPvxsServer([&jsonFilename](IOCServer* pPvxsServer) {
if (jsonFilename[0] == '-') {
jsonFilename++;
if (jsonFilename[0] == '*' && jsonFilename[1] == '\0') {
pPvxsServer->groupConfigFiles.clear();
} else {
pPvxsServer->groupConfigFiles.remove(jsonFilename);
}
} else {
pPvxsServer->groupConfigFiles.remove(jsonFilename);
pPvxsServer->groupConfigFiles.emplace_back(jsonFilename);
bool remove = jsonFilename[0] == '-';
if(remove)
jsonFilename++;
auto& config(IOCGroupConfig::instance());
auto& gCF = config.groupConfigFiles;
if(strcmp(jsonFilename, "*")==0) {
gCF.clear();
return 0;
}
decltype(IOCGroupConfig::JFile::jf) jfile;
decltype(IOCGroupConfig::JFile::handle) macs;
if(!remove) {
jfile.reset(new std::ifstream(jsonFilename));
if (!jfile->is_open()) {
fprintf(stderr, "Error opening \"%s\"\n", jsonFilename);
return 1;
}
});
if(macros[0]!='\0') {
MAC_HANDLE* mac;
const char * env_pair[] = {"", "environ", NULL, NULL};
if(macCreateHandle(&mac, env_pair))
throw std::bad_alloc();
macs.reset(mac);
char **pairs = nullptr;
auto noinstall = macParseDefns(mac, macros, &pairs)<0 || macInstallMacros(mac, pairs)<0;
free(pairs);
if(noinstall) {
fprintf(stderr, "Error Invalid macros for \"%s\", \"%s\"\n",
jsonFilename, macros);
return 1;
}
}
}
for(auto next(gCF.begin()); next != gCF.end();)
{
auto it(next++);
if(it->fname==jsonFilename && it->macros==macros)
config.groupConfigFiles.erase(it);
}
if(!remove) {
gCF.emplace_back(std::move(jfile), jsonFilename, macros, std::move(macs));
}
return 0;
} catch (std::exception& e) {
fprintf(stderr, "Error: %s\n", e.what());
@@ -129,25 +179,37 @@ using namespace pvxs;
* @param theInitHookState the initHook state - we only want to trigger on the initHookAfterIocBuilt state - ignore all others
*/
void qsrvGroupSourceInit(initHookState theInitHookState) {
if (theInitHookState == initHookAfterInitDatabase) {
GroupConfigProcessor processor;
// Parse all info(Q:Group... records to configure groups
processor.loadConfigFromDb();
try {
if(!IOCSource::enabled())
return;
if (theInitHookState == initHookAfterInitDatabase) {
GroupConfigProcessor processor;
epicsGuard<epicsMutex> G(processor.config.groupMapMutex);
// Load group configuration files
processor.loadConfigFiles();
// Parse all info(Q:Group... records to configure groups
processor.loadConfigFromDb();
// Configure groups
processor.defineGroups();
// Load group configuration files
processor.loadConfigFiles();
// Resolve triggers
processor.resolveTriggerReferences();
// checks on groupConfigMap
processor.validateGroups();
// Create Server Groups
processor.createGroups();
} else if (theInitHookState == initHookAfterIocBuilt) {
// Load group configuration from parsed groups in iocServer
pvxs::ioc::iocServer().addSource("qsrvGroup", std::make_shared<pvxs::ioc::GroupSource>(), 1);
// Configure groups
processor.defineGroups();
// Resolve triggers
processor.resolveTriggerReferences();
// Create Server Groups
processor.createGroups();
} else if (theInitHookState == initHookAfterIocBuilt) {
// Load group configuration from parsed groups in iocServer
pvxs::ioc::server().addSource("qsrvGroup", std::make_shared<pvxs::ioc::GroupSource>(), 1);
}
} catch(std::exception& e) {
fprintf(stderr, "ERROR: Unhandled exception in %s(%d): %s\n",
__func__, theInitHookState, e.what());
}
}
@@ -164,16 +226,15 @@ void qsrvGroupSourceInit(initHookState theInitHookState) {
*/
void pvxsGroupSourceRegistrar() {
// Register commands to be available in the IOC shell
IOCShCommand<int, const char*>("pvxsgl", "[level, [pattern]]", "Group Sources list.\n"
"List record/field names.\n"
"If `level` is set then show only down to that level.\n"
"If `pattern` is set then show records that match the pattern.")
IOCShCommand<int, const char*>("pvxgl", "[level, [pattern]]",
"Group Sources list.\n"
"List record/field names.\n"
"If `level` is set then show only down to that level.\n"
"If `pattern` is set then show records that match the pattern.")
.implementation<&pvxsgl>();
IOCShCommand<const char*>("dbLoadGroup", "jsonDefinitionFile", "Load Group Record Definition from given file.\n"
"'-' or '-*' to remove previous files.\n"
"'-<jsonDefinitionFile>' to remove the file from the list.\n"
"otherwise add the file to the list of files to load.\n")
IOCShCommand<const char*, const char*>("dbLoadGroup",
"JSON file", "macros", dbLoadGroupMsg)
.implementation<&dbLoadGroupCmd>();
initHookRegister(&qsrvGroupSourceInit);