This commit is contained in:
parent
8ae588ed40
commit
d82e77382d
|
@ -37,7 +37,7 @@ namespace fsm = boost::statechart;
|
||||||
|
|
||||||
#ifdef MD_MAIN
|
#ifdef MD_MAIN
|
||||||
|
|
||||||
mdGTEEngine *engineFactory;
|
mdDeviceFabrik *engineFactory;
|
||||||
mdLogger *theseLogs;
|
mdLogger *theseLogs;
|
||||||
masterDaemonConfig *thisConfig;
|
masterDaemonConfig *thisConfig;
|
||||||
|
|
||||||
|
@ -46,7 +46,7 @@ namespace fsm = boost::statechart;
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
|
||||||
extern mdGTEEngine *engineFactory;
|
extern mdDeviceFabrik *engineFactory;
|
||||||
extern mdLogger *theseLogs;
|
extern mdLogger *theseLogs;
|
||||||
extern masterDaemonConfig *thisConfig;
|
extern masterDaemonConfig *thisConfig;
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,86 @@
|
||||||
|
#ifndef MASTER_DAEMON
|
||||||
|
#define MASTER_DAEMON
|
||||||
|
|
||||||
|
#include <msgpack.hpp>
|
||||||
|
|
||||||
|
/*! \brief masterDaemon
|
||||||
|
* server core.
|
||||||
|
*
|
||||||
|
* Contains the data layer and the XMLRPC API.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifdef MD_CORE
|
||||||
|
|
||||||
|
class masterDaemon *thisService;
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
extern class masterDaemon *thisService;
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
using boost::asio::ip::udp;
|
||||||
|
|
||||||
|
|
||||||
|
class masterDaemon : public mdProcess,
|
||||||
|
public Listener<mdAttention>,
|
||||||
|
public Listener<mdCDPulse>,
|
||||||
|
public Listener<mdClientBirth>,
|
||||||
|
public Listener<mdClientDeath>,
|
||||||
|
public Listener<mdIncoming>,
|
||||||
|
public Listener<mdResponse>,
|
||||||
|
public Listener<mdTelemetryFrame>,
|
||||||
|
public Listener<mdDeviceCommand> {
|
||||||
|
public:
|
||||||
|
|
||||||
|
bool shuttingDown;
|
||||||
|
|
||||||
|
boost::asio::io_service io_;
|
||||||
|
|
||||||
|
int arCycles,
|
||||||
|
dataClients[MAX_DATACLIENTS],
|
||||||
|
instruments[MAX_INSTRUMENTS],
|
||||||
|
nClievers,
|
||||||
|
sentCommands;
|
||||||
|
|
||||||
|
masterDaemonConfig *cfg;
|
||||||
|
mdDGChannel *bg,*fg;
|
||||||
|
|
||||||
|
std::string clievers[MAX_CLIEVER];
|
||||||
|
|
||||||
|
masterDaemon();
|
||||||
|
~masterDaemon() {}
|
||||||
|
|
||||||
|
masterDaemon(masterDaemonConfig *cmdCfg) { int i;
|
||||||
|
thisService = this;
|
||||||
|
cfg = cmdCfg;
|
||||||
|
nClievers = 0;
|
||||||
|
shuttingDown = false;
|
||||||
|
memset(dataClients,0,sizeof(dataClients));
|
||||||
|
memset(instruments,0,sizeof(instruments));
|
||||||
|
}
|
||||||
|
|
||||||
|
int getDeviceHandle(int deviceMajor,std::string &deviceMinor) {};
|
||||||
|
int initBaseAPI(void);
|
||||||
|
int initDataLayer(void);
|
||||||
|
int releaseDevice(int handle) {return( -1);}
|
||||||
|
int validateHandleForCmds(int handle) {return(-1);}
|
||||||
|
void dispatch(mdWQitem*);
|
||||||
|
void dispatch(const mdIncoming&);
|
||||||
|
void listen();
|
||||||
|
xmlrpc_c::value* fetchCommands(std::string subSystem) {};
|
||||||
|
|
||||||
|
virtual void processEvent(const mdAttention &ev);
|
||||||
|
virtual void processEvent(const mdCDPulse &ev);
|
||||||
|
virtual void processEvent(const mdClientBirth &ev);
|
||||||
|
virtual void processEvent(const mdClientDeath &ev);
|
||||||
|
virtual void processEvent(const mdIncoming &ev);
|
||||||
|
virtual void processEvent(const mdResponse &ev);
|
||||||
|
virtual void processEvent(const mdTelemetryFrame &ev);
|
||||||
|
virtual void processEvent(const mdDeviceCommand &ev);
|
||||||
|
|
||||||
|
void run();
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,90 @@
|
||||||
|
|
||||||
|
#include <boost/asio.hpp>
|
||||||
|
|
||||||
|
/*! \brief mdDevice
|
||||||
|
* General abstraction of all MD clients
|
||||||
|
*
|
||||||
|
* For historical reasons all clients are considered to be
|
||||||
|
* devices, though mdclient and US clients have a non-device
|
||||||
|
* client type. The central device of the system has zero
|
||||||
|
* device type.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
using boost::asio::ip::udp;
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
class mdDevice {
|
||||||
|
public:
|
||||||
|
|
||||||
|
bool isSingleton;
|
||||||
|
int clieverGroup, // masterDaemonConfig.thisMachineContext
|
||||||
|
handle,mdStdDevIdx;
|
||||||
|
md_device type;
|
||||||
|
mdState state;
|
||||||
|
|
||||||
|
udp::endpoint ip;
|
||||||
|
|
||||||
|
// Only Machine and Instrument use below
|
||||||
|
InstructionSet cmds;
|
||||||
|
|
||||||
|
// Some parameters initially here are now all uniformly ODEs
|
||||||
|
// defined in the COOL scripts.
|
||||||
|
|
||||||
|
~mdDevice() {}
|
||||||
|
mdDevice(md_device t) : type(t) {clieverGroup = handle = mdStdDevIdx = -1;}
|
||||||
|
|
||||||
|
void lxi_control(T* device,std::string scpiText);
|
||||||
|
T* registeR(md_device t);
|
||||||
|
|
||||||
|
xmlrpc_c::value* fetchCommands(std::string subSystem);
|
||||||
|
void registerCmd(const char *cmdName,const mdIncoming &mdI);
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
class mdClientServer;
|
||||||
|
class mdClientServer : public mdDevice<mdClientServer> {
|
||||||
|
public:
|
||||||
|
|
||||||
|
mdClientServer() : mdDevice<mdClientServer>( MDDEV_CD ) {};
|
||||||
|
mdClientServer *validateClient(int handle, mdResponse &r);
|
||||||
|
};
|
||||||
|
|
||||||
|
class mdMachine;
|
||||||
|
class mdMachine : public mdDevice<mdMachine> {
|
||||||
|
public:
|
||||||
|
|
||||||
|
mdMachine() : mdDevice<mdMachine>( MACHINE )
|
||||||
|
{ cmds["RST"] = new mdCommand(MD_SCPI,std::string("RST")); }
|
||||||
|
mdMachine *validateClient(int handle, const mdClientBirth &c, mdResponse &r);
|
||||||
|
void registerCmd(const char *cmdName,const mdIncoming &mdI);
|
||||||
|
};
|
||||||
|
|
||||||
|
class mdInstrument;
|
||||||
|
class mdInstrument : public mdDevice<mdInstrument> {
|
||||||
|
public:
|
||||||
|
|
||||||
|
mdInstrument() : mdDevice<mdInstrument>( MDDEV_INSTRUMENT )
|
||||||
|
{ cmds["RST"] = new mdCommand(MD_SCPI,std::string("RST")); }
|
||||||
|
mdInstrument *validateClient(int handle, const mdClientBirth &c, mdResponse &r);
|
||||||
|
void registerCmd(const char *cmdName,const mdIncoming &mdI);
|
||||||
|
};
|
||||||
|
|
||||||
|
class mdDataClient;
|
||||||
|
class mdDataClient : public mdDevice<mdDataClient> {
|
||||||
|
public:
|
||||||
|
|
||||||
|
mdDataClient() : mdDevice<mdDataClient>( MDDEV_DATACLIENT ) {};
|
||||||
|
mdDataClient *validateClient(int handle);
|
||||||
|
};
|
||||||
|
|
||||||
|
class masterDaemon;
|
||||||
|
class mdDeviceFabrik : public mdDevice<masterDaemon>
|
||||||
|
{public:
|
||||||
|
|
||||||
|
mdDeviceFabrik() : mdDevice<masterDaemon>( MDDEV_MD ) {}
|
||||||
|
void newFromHeartbeat(const mdClientBirth &itsAWhat);
|
||||||
|
std::string newFromAPI(md_device type,std::string signature);
|
||||||
|
|
||||||
|
};
|
|
@ -70,9 +70,9 @@ using boost::asio::ip::udp;
|
||||||
#define MD_EPOCH date()
|
#define MD_EPOCH date()
|
||||||
#define MD_HEARTBEAT 1 // Network peer heartbeat in seconds.
|
#define MD_HEARTBEAT 1 // Network peer heartbeat in seconds.
|
||||||
#define MD_MAX_DATAGRAM (32*1024) // half of the IPV4 max
|
#define MD_MAX_DATAGRAM (32*1024) // half of the IPV4 max
|
||||||
#define AUSREGCLIEVER_APP "Generic"
|
#define CLIEVER_APP "AusReg Cliever"
|
||||||
#define MD_COMPONENT "Master Daemon" // Cliever Component
|
#define MD_COMPONENT "Master Daemon" // Cliever Component
|
||||||
#define MD_NAME DACLIPS_APP " " MD_COMPONENT
|
#define MD_NAME CLIEVER_APP " " MD_COMPONENT
|
||||||
#define MD_VERSION " 1.0 " // Version
|
#define MD_VERSION " 1.0 " // Version
|
||||||
#define MD_REFRESH 10 // default milliseconds between telemetry frame updates
|
#define MD_REFRESH 10 // default milliseconds between telemetry frame updates
|
||||||
#define MD_TYPE "AUSREGCLIEVER" // i.e. what a MACHINE is, Change per your Cliever derivation
|
#define MD_TYPE "AUSREGCLIEVER" // i.e. what a MACHINE is, Change per your Cliever derivation
|
||||||
|
|
|
@ -9,6 +9,7 @@ class mdAttention: public TimeStampedEvent<>, public PolymorphEvent {
|
||||||
virtual void send() const { sendTypedEvent(*this); }
|
virtual void send() const { sendTypedEvent(*this); }
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class mdClientBirth: public TimeStampedEvent<>, public PolymorphEvent {
|
class mdClientBirth: public TimeStampedEvent<>, public PolymorphEvent {
|
||||||
public:
|
public:
|
||||||
bool dgDetermined;
|
bool dgDetermined;
|
||||||
|
|
|
@ -66,7 +66,7 @@ void md() {
|
||||||
theseLogs->logN(0,"AusReg Cliever <- MasterDaemon.");
|
theseLogs->logN(0,"AusReg Cliever <- MasterDaemon.");
|
||||||
}
|
}
|
||||||
|
|
||||||
boost::thread foreground(runMasterDaemon);
|
boost::thread foreground(runMasterDaemon);`
|
||||||
boost::thread background(runClientLayer);
|
boost::thread background(runClientLayer);
|
||||||
|
|
||||||
if (!foreground.joinable()) {
|
if (!foreground.joinable()) {
|
||||||
|
@ -108,8 +108,8 @@ main(int const argc,
|
||||||
thisConfig = new masterDaemonConfig();
|
thisConfig = new masterDaemonConfig();
|
||||||
thisConfig->shellProcess = getpid();
|
thisConfig->shellProcess = getpid();
|
||||||
|
|
||||||
mdOrKb = (strcspn(argv[0],"./") == strlen(argv[0])) ? argv[0] : strrchr(argv[0],'/') + 1;
|
mdOrAC = (strcspn(argv[0],"./") == strlen(argv[0])) ? argv[0] : strrchr(argv[0],'/') + 1;
|
||||||
mdHasKb = strstr(argv[0],"clips") ? true : false;
|
mdHasEPPTk = strstr(argv[0],"epp") ? true : false;
|
||||||
|
|
||||||
if (argc < 3 || argc > 6) usage();
|
if (argc < 3 || argc > 6) usage();
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,327 @@
|
||||||
|
#define MD_CORE
|
||||||
|
#include "auc-md.h"
|
||||||
|
#include "masterDaemon.h"
|
||||||
|
#include "coreapi.h"
|
||||||
|
#include "../server/Listener.cpp"
|
||||||
|
#include "../server/EventSender2.cpp"
|
||||||
|
|
||||||
|
void attention();
|
||||||
|
void arCallback(const boost::system::error_code& error);
|
||||||
|
void mdWQ();
|
||||||
|
|
||||||
|
void masterDaemon::dispatch(mdWQitem *next) {
|
||||||
|
|
||||||
|
bool success;
|
||||||
|
boost::system::error_code ec;
|
||||||
|
const char *failure;
|
||||||
|
int sentBytes,step=1;
|
||||||
|
mdResponse *ackOrNak = (mdResponse *)next->what;
|
||||||
|
|
||||||
|
switch(next->kind) {
|
||||||
|
case DV_MDQUERY:
|
||||||
|
failure = (const char *)&ackOrNak->reply.dg.payLoad[0];
|
||||||
|
success = ackOrNak->reply.dg.hdr.dgType.value == 1;
|
||||||
|
goto commonReply;
|
||||||
|
case MD_NEWBORN:
|
||||||
|
ackOrNak->reply.dg.hdr.msgType = MDDG_NEWBORN;
|
||||||
|
failure = "Stillbirth";
|
||||||
|
success = ackOrNak->mdStdDevIdx >= 0;
|
||||||
|
commonReply:
|
||||||
|
assert(cb.find(ackOrNak->mdStdDevIdx) != cb.end());
|
||||||
|
ackOrNak->reply.dg.hdr.clientType = MDDEV_MD;
|
||||||
|
if (success) {
|
||||||
|
if (!cb[ackOrNak->mdStdDevIdx]->connection.open) {
|
||||||
|
ackOrNak->bus->connect_to(ackOrNak->ip,ec,step,ackOrNak->mdStdDevIdx);
|
||||||
|
if (cb[ackOrNak->mdStdDevIdx]->connection.open) {
|
||||||
|
if ((sentBytes=ackOrNak->bus->send(ackOrNak->reply,ec)) != sizeof(mdDGReply))
|
||||||
|
theseLogs->logNdebug(1,2,"incomplete blocking send: %d: %s",sentBytes,ec.message().c_str());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
theseLogs->logNdebug(1,2,"Couldn't get back channel to client: %s (in step %d).",ec.message().c_str(),step);
|
||||||
|
}
|
||||||
|
else if ((sentBytes=ackOrNak->bus->send(ackOrNak->reply,ec)) != sizeof(mdDGReply))
|
||||||
|
theseLogs->logNdebug(1,2,"incomplete blocking send: %d: %s",sentBytes,ec.message().c_str());
|
||||||
|
|
||||||
|
} else {
|
||||||
|
theseLogs->logNdebug(1,0,failure);
|
||||||
|
}
|
||||||
|
delete ackOrNak;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
delete next;
|
||||||
|
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* Prenatal heartbeats and commands are not queued.
|
||||||
|
* Everything else is.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void masterDaemon::dispatch(const mdIncoming &what) {
|
||||||
|
|
||||||
|
bool isObservation;
|
||||||
|
const char *name,*xStr;
|
||||||
|
int about=what.dg.hdr.handle;
|
||||||
|
md_device thisKind;
|
||||||
|
mdInstrument *d1;
|
||||||
|
mdMachine *d2;
|
||||||
|
|
||||||
|
map<int,mdLiveClient*>::iterator iter = thisConfig->allClients.find(about);
|
||||||
|
|
||||||
|
switch(what.dg.hdr.msgType) {
|
||||||
|
case MDDG_CDRESET:
|
||||||
|
theseLogs->logN(0,"Shutdown request received from a Cliever");
|
||||||
|
thisConfig->halt = true;
|
||||||
|
break;
|
||||||
|
case MDDG_MDQUERY:
|
||||||
|
if( iter == thisConfig->allClients.end() )
|
||||||
|
theseLogs->logN(1,"Query for device whose handle (%d) has disappeared, ignored.", about );
|
||||||
|
else {
|
||||||
|
thisKind = thisConfig->allClients[about]->devType;
|
||||||
|
name = &what.dg.payLoad[0];
|
||||||
|
xStr = &what.dg.payLoad[what.dg.hdr.primeOffset];
|
||||||
|
switch(what.dg.hdr.dgSubType) {
|
||||||
|
case MDDG_REGSCPI:
|
||||||
|
theseLogs->logNdebug(NORMAL_DEBUG,4,"Src SCPI: '%s' from type: %d ('%s'), handle %d.", name, what.dg.hdr.clientType,xStr,about);
|
||||||
|
if (thisKind == MACHINE) theMachine->registerCmd(name,what);
|
||||||
|
else thisConfig->instruments[about]->registerCmd(name,what);
|
||||||
|
break;
|
||||||
|
case MDDG_REGODE:
|
||||||
|
theseLogs->logNdebug(NORMAL_DEBUG,4,"Src ODE: '%s' from type: %d ('%s'), handle %d.", name, what.dg.hdr.clientType,xStr,about);
|
||||||
|
goto regName;
|
||||||
|
case MDDG_REGOBS:
|
||||||
|
theseLogs->logNdebug(NORMAL_DEBUG,4,"Src Obs: '%s' from type: %d ('%s'), handle %d.", name, what.dg.hdr.clientType,xStr,about);
|
||||||
|
regName:
|
||||||
|
if (thisKind == MACHINE) theMachine->state.registerData(name,what);
|
||||||
|
else thisConfig->instruments[about]->state.registerData(name,what);
|
||||||
|
break;
|
||||||
|
}}
|
||||||
|
break;
|
||||||
|
case MDDG_HEARTBEAT:
|
||||||
|
if (!what.dg.hdr.sourceHandle) {
|
||||||
|
if (what.dg.hdr.clientType < MDDEV_CD || what.dg.hdr.clientType > MDDEV_DATACLIENT) {
|
||||||
|
theseLogs->logN(1,"Heartbeat from unknown client type: %d, ignored.", what.dg.hdr.clientType);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
theseLogs->logNdebug(NORMAL_DEBUG*4,1,"Heartbeat from new %s ...",clientTypes[what.dg.hdr.clientType]);
|
||||||
|
if (what.dg.hdr.primeOffset >= 5) {
|
||||||
|
mdClientBirth itsAWhat;
|
||||||
|
name = (char *)(&what.dg.payLoad[what.dg.hdr.primeOffset]);
|
||||||
|
theseLogs->logNdebug(NORMAL_DEBUG*4,1," ... its telemetry port is %s.",what.dg.payLoad);
|
||||||
|
theseLogs->logNdebug(NORMAL_DEBUG*4,1," ... '%s' will be its _deviceName.",name);
|
||||||
|
itsAWhat.dg = what.dg;
|
||||||
|
itsAWhat.ip = what.ip;
|
||||||
|
itsAWhat.ip.port((unsigned short)atoi(what.dg.payLoad));
|
||||||
|
itsAWhat.dgDetermined = true;
|
||||||
|
itsAWhat.send();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
theseLogs->logN(1,"Heartbeat didn't appear to say what port to use, ignored.");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
theseLogs->logNdebug(MAX_DEBUG,2,"Heartbeat from client with handle: %d.",what.dg.hdr.sourceHandle);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
int masterDaemon::initBaseAPI(void) {
|
||||||
|
|
||||||
|
int rc=OK;
|
||||||
|
|
||||||
|
try {
|
||||||
|
|
||||||
|
theseLogs->logN(0,"Create Generic Core API");
|
||||||
|
|
||||||
|
xmlrpc_c::methodPtr const registerDeviceP(new registerDevice(thisService->cfg));
|
||||||
|
xmlrpc_c::methodPtr const getMDversionP(new getMDversion);
|
||||||
|
xmlrpc_c::methodPtr const getP(new getter);
|
||||||
|
xmlrpc_c::methodPtr const setP(new setter);
|
||||||
|
xmlrpc_c::methodPtr const getCmdListP(new cmdListFetch);
|
||||||
|
xmlrpc_c::methodPtr const getCmdP(new cmd);
|
||||||
|
xmlrpc_c::methodPtr const createP(new create);
|
||||||
|
|
||||||
|
thisConfig->api_registry.addMethod("device.registeR", registerDeviceP );
|
||||||
|
thisConfig->api_registry.addMethod("state.getMDversion", getMDversionP );
|
||||||
|
thisConfig->api_registry.addMethod("state.create", createP);
|
||||||
|
thisConfig->api_registry.addMethod("state.get", getP );
|
||||||
|
thisConfig->api_registry.addMethod("state.set", setP );
|
||||||
|
thisConfig->api_registry.addMethod("behavior.getCommandList", getCmdListP );
|
||||||
|
thisConfig->api_registry.addMethod("behavior.command", getCmdP );
|
||||||
|
|
||||||
|
}
|
||||||
|
catch(...)
|
||||||
|
{ rc = NOT_OK; }
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
}
|
||||||
|
void masterDaemon::listen() {
|
||||||
|
|
||||||
|
EventSender<mdAttention>::add(*this);
|
||||||
|
assert(EventSender<mdAttention>::getNumListeners() == 1);
|
||||||
|
EventSender<mdCDPulse>::add(*this);
|
||||||
|
assert(EventSender<mdCDPulse>::getNumListeners() == 1);
|
||||||
|
EventSender<mdClientBirth>::add(*this);
|
||||||
|
assert(EventSender<mdClientBirth>::getNumListeners() == 1);
|
||||||
|
EventSender<mdClientDeath>::add(*this);
|
||||||
|
assert(EventSender<mdClientDeath>::getNumListeners() == 1);
|
||||||
|
EventSender<mdDeviceCommand>::add(*this);
|
||||||
|
assert(EventSender<mdDeviceCommand>::getNumListeners() == 1);
|
||||||
|
EventSender<mdIncoming>::add(*this);
|
||||||
|
assert(EventSender<mdIncoming>::getNumListeners() == 1);
|
||||||
|
EventSender<mdResponse>::add(*this);
|
||||||
|
assert(EventSender<mdResponse>::getNumListeners() == 1);
|
||||||
|
EventSender<mdTelemetryFrame>::add(*this);
|
||||||
|
assert(EventSender<mdTelemetryFrame>::getNumListeners() == 1);
|
||||||
|
|
||||||
|
boost::thread mdAr(attention);
|
||||||
|
|
||||||
|
}
|
||||||
|
void masterDaemon::processEvent( const mdAttention &thisAR )
|
||||||
|
{
|
||||||
|
assert(EventSender<mdAttention>::isSending());
|
||||||
|
}
|
||||||
|
void masterDaemon::processEvent( const mdCDPulse &thisPulse )
|
||||||
|
{
|
||||||
|
assert(EventSender<mdCDPulse>::isSending());
|
||||||
|
}
|
||||||
|
void masterDaemon::processEvent( const mdClientBirth &thisWhat )
|
||||||
|
{
|
||||||
|
assert(EventSender<mdClientBirth>::isSending());
|
||||||
|
if (thisWhat.dgDetermined) {
|
||||||
|
deviceFactory->newFromHeartbeat(thisWhat);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
deviceFactory->newFromAPI(
|
||||||
|
thisWhat.clientType,thisWhat.signature);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void masterDaemon::processEvent( const mdClientDeath &thisWas )
|
||||||
|
{
|
||||||
|
assert(EventSender<mdClientDeath>::isSending());
|
||||||
|
}
|
||||||
|
void masterDaemon::processEvent( const mdDeviceCommand &thisCmd )
|
||||||
|
{
|
||||||
|
assert(EventSender<mdDeviceCommand>::isSending());
|
||||||
|
}
|
||||||
|
void masterDaemon::processEvent( const mdIncoming &thisDatagram )
|
||||||
|
{
|
||||||
|
assert(EventSender<mdIncoming>::isSending());
|
||||||
|
thisService->dispatch(thisDatagram);
|
||||||
|
}
|
||||||
|
void masterDaemon::processEvent( const mdResponse &thisReply )
|
||||||
|
{
|
||||||
|
const void *queued = &thisReply;
|
||||||
|
|
||||||
|
assert(EventSender<mdResponse>::isSending());
|
||||||
|
queue(new mdWQitem( queued, thisReply.dCat, 0 ));
|
||||||
|
|
||||||
|
}
|
||||||
|
void masterDaemon::processEvent( const mdTelemetryFrame &thisFrame )
|
||||||
|
{
|
||||||
|
assert(EventSender<mdTelemetryFrame>::isSending());
|
||||||
|
}
|
||||||
|
void masterDaemon::run() {
|
||||||
|
|
||||||
|
deviceFactory = new mdDeviceFabrik();
|
||||||
|
fg = new mdDGChannel( thisService->io_, 0 );
|
||||||
|
|
||||||
|
if (initBaseAPI()) return;
|
||||||
|
listen();
|
||||||
|
boost::thread work(mdWQ);
|
||||||
|
assert(work.joinable());
|
||||||
|
theseLogs->logNdebug(MAX_DEBUG,0,"Master Daemon worker started, foreground async i/o service joins MD worker.");
|
||||||
|
io_.run();
|
||||||
|
work.join();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void mdDGChannel::handle_receive_from(const boost::system::error_code& error,
|
||||||
|
size_t bytes_recvd)
|
||||||
|
{
|
||||||
|
const char *c1;
|
||||||
|
|
||||||
|
if (!error && bytes_recvd > 0)
|
||||||
|
{
|
||||||
|
mdIncoming incoming(thisService->bg);
|
||||||
|
|
||||||
|
incoming.ip = thisService->bg->p_endpoint_;
|
||||||
|
c1 = thisService->bg->p_endpoint_.address().to_string().c_str();
|
||||||
|
|
||||||
|
if (incoming.dg.hdr.clientType > 0 && incoming.dg.hdr.clientType < N_MDDEV_TYPES)
|
||||||
|
{ theseLogs->logNdebug(MAX_DEBUG,3,"msgtype %d received from %s (a '%s').",incoming.dg.hdr.msgType,c1,clientTypes[incoming.dg.hdr.clientType]);
|
||||||
|
incoming.send();
|
||||||
|
} else
|
||||||
|
theseLogs->logNdebug(1,2,"msgtype %d received from unknown MD client type at %s, ignored.",incoming.dg.hdr.msgType,c1);
|
||||||
|
}
|
||||||
|
passive_.async_receive_from(
|
||||||
|
boost::asio::buffer(data_, MD_MAX_DATAGRAM), p_endpoint_,
|
||||||
|
boost::bind(&mdDGChannel::handle_receive_from, this,
|
||||||
|
boost::asio::placeholders::error,
|
||||||
|
boost::asio::placeholders::bytes_transferred));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Callbacks, thread runners, and other ordinary functions.
|
||||||
|
|
||||||
|
void attention() {
|
||||||
|
|
||||||
|
bool announced = false;
|
||||||
|
|
||||||
|
// The housekeeping routine is the only cyclic event in MD
|
||||||
|
boost::asio::deadline_timer t0(thisService->io_, boost::posix_time::seconds(MD_HAUSHALT));
|
||||||
|
while (!thisService->shuttingDown)
|
||||||
|
{t0.async_wait(arCallback);
|
||||||
|
sleep(2*MD_HAUSHALT);
|
||||||
|
if (!announced) {announced = true;
|
||||||
|
theseLogs->logNdebug(MAX_DEBUG,0,"First invocation of housekeeping.");}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
void arCallback(const boost::system::error_code& error) {
|
||||||
|
|
||||||
|
mdAttention housekeep( thisService->arCycles++ );
|
||||||
|
housekeep.send();
|
||||||
|
|
||||||
|
}
|
||||||
|
void runMasterDaemon() {
|
||||||
|
|
||||||
|
cb[0] = new mdCB();
|
||||||
|
thisService = new masterDaemon( thisConfig );
|
||||||
|
thisService->run();
|
||||||
|
|
||||||
|
}
|
||||||
|
void mdWQ() {
|
||||||
|
|
||||||
|
while(!thisConfig->shutdown) {
|
||||||
|
if (!thisConfig->halt && thisService->q.size())
|
||||||
|
{ thisService->dispatch(thisService->q.top()); thisService->q.pop(); }
|
||||||
|
else
|
||||||
|
boost::this_thread::yield();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
void runDataLayer() {
|
||||||
|
|
||||||
|
boost::asio::io_service io_;
|
||||||
|
|
||||||
|
theseLogs->logN(1,"Background dgram service thread starting on port %d.",thisConfig->telemetryPort);
|
||||||
|
|
||||||
|
try {
|
||||||
|
|
||||||
|
thisService->bg = new mdDGChannel(io_, thisConfig->telemetryPort);
|
||||||
|
io_.run();
|
||||||
|
|
||||||
|
}
|
||||||
|
catch (std::exception& e)
|
||||||
|
{
|
||||||
|
theseLogs->logN(1,"Fatal error on main bus background: %s .",e.what());
|
||||||
|
}
|
||||||
|
catch (...)
|
||||||
|
{
|
||||||
|
theseLogs->logN(0,"Unknown failure in background datalayer.");
|
||||||
|
}
|
||||||
|
|
||||||
|
theseLogs->logNdebug(1,0,"mainbus background thread exited.");
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,346 @@
|
||||||
|
#include "cliever-md.h"
|
||||||
|
#include "masterDaemon.h"
|
||||||
|
#include "../server/Listener.cpp"
|
||||||
|
#include "../server/EventSender2.cpp"
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
/*! \brief Client object implementatios
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
int getHandle() {
|
||||||
|
|
||||||
|
map<int,mdLiveClient*>::iterator it;
|
||||||
|
|
||||||
|
bool collision = thisConfig->allClients.size() > 0 ? false: true, found;
|
||||||
|
int value; srand ( time(NULL) );
|
||||||
|
|
||||||
|
value = (rand() % (MAX_DEVICE * 10)) + 1;
|
||||||
|
|
||||||
|
while(collision) {
|
||||||
|
for(found = false, it = thisConfig->allClients.begin();
|
||||||
|
it != thisConfig->allClients.end() && !found;
|
||||||
|
found = (it->first == value ? true : false), it++);
|
||||||
|
if (!found) collision = false;
|
||||||
|
else value = (rand() % (MAX_DEVICE * 10)) + 1;
|
||||||
|
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
|
||||||
|
}
|
||||||
|
template<class T> T* mdDevice<T>::registeR(md_device t) {
|
||||||
|
|
||||||
|
T *value=NULL;
|
||||||
|
int h = getHandle();
|
||||||
|
|
||||||
|
if (value=mdDevice<T>::validateClient(h)) {
|
||||||
|
theseLogs->logN(2,"Handle %d assigned to new client of type: %s",clientTypes[t]);
|
||||||
|
} else {
|
||||||
|
theseLogs->logN(2,"Validation failed for client type: %s",clientTypes[t]);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
value->handle = h;
|
||||||
|
value->create();
|
||||||
|
thisConfig->allClients[h] = value; // validateClient has already added to group
|
||||||
|
return value ;
|
||||||
|
|
||||||
|
}
|
||||||
|
template<class T>
|
||||||
|
void mdDevice<T>::lxi_control(T *device, std::string fullText) {
|
||||||
|
|
||||||
|
T *target = device;
|
||||||
|
char *ip,*port,*command,*timeout,*argv[5];
|
||||||
|
|
||||||
|
argv[1] = ip = (char *)malloc(32);
|
||||||
|
argv[2] = port = (char *)malloc(16);
|
||||||
|
argv[3] = timeout = (char *)malloc(32);
|
||||||
|
argv[4] = command = (char *)malloc(1024);
|
||||||
|
|
||||||
|
sprintf(ip,"--ip %s",target->ip.c_str());
|
||||||
|
sprintf(port,"--port %s",target->port.c_str());
|
||||||
|
sprintf(timeout,"--timeout %s",target->timeout.c_str());
|
||||||
|
sprintf(command,"--scpi %s",fullText.c_str());
|
||||||
|
|
||||||
|
lxi_control(5,argv);
|
||||||
|
|
||||||
|
free(ip);
|
||||||
|
free(port);
|
||||||
|
free(timeout);
|
||||||
|
free(command);
|
||||||
|
|
||||||
|
}
|
||||||
|
mdClientServer* mdClientServer::validateClient(int handle, mdResponse &r) {
|
||||||
|
|
||||||
|
bool isNew=true;
|
||||||
|
int i,m=-1,n=-1;
|
||||||
|
mdClientServer *value=NULL;
|
||||||
|
|
||||||
|
if (!thisConfig->nClievers) {m =0; isNew = true;}
|
||||||
|
else for(i=0;i<MAX_CLIEVER && isNew;i++)
|
||||||
|
{if (m < 0 && thisConfig->clievers[i].empty()) m = i;
|
||||||
|
if (ip.address().to_string() == thisConfig->clievers[i]) isNew = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isNew && thisConfig->nClievers < MAX_CLIEVER)
|
||||||
|
{ for (n=i=0;i<thisConfig->nClievers && n < 0;i++)
|
||||||
|
if (!thisConfig->cliever[i]) n = i;
|
||||||
|
thisConfig->nClievers++;
|
||||||
|
thisConfig->cliever[n] = value = this;
|
||||||
|
mdStdDevIdx = n + 1;
|
||||||
|
ip = r.ip;
|
||||||
|
thisConfig->clievers[n] = ip.address().to_string();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
theseLogs->logN(1,"Either a Cliever already active at %s or limit number (%d) reached.",ip.address().to_string().c_str(),MAX_CLIEVER);
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
|
||||||
|
}
|
||||||
|
mdMachine* mdMachine::validateClient(int handle, const mdClientBirth &c, mdResponse &r) {
|
||||||
|
|
||||||
|
char *cp;
|
||||||
|
mdMachine *value=NULL;
|
||||||
|
|
||||||
|
if (!theMachine) {
|
||||||
|
if (c.dg.hdr.dgType.clieverGroup) {
|
||||||
|
theseLogs->logN(1,"Machine specified non-zero cliever group(%d) in GDOLMS 1.x, rejected.",c.dg.hdr.dgType.clieverGroup);
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
if (!thisConfig->cliever[c.dg.hdr.dgType.clieverGroup]) {
|
||||||
|
theseLogs->logN(1,"The cliever for this device group (%d) is not online, machine birth rejected.",c.dg.hdr.dgType.clieverGroup);
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
strcpy(r.reply.dg.payLoad,thisConfig->clievers[c.dg.hdr.dgType.clieverGroup].c_str());
|
||||||
|
cp = &r.reply.dg.payLoad[0] + strlen(r.reply.dg.payLoad) + 1;
|
||||||
|
*((unsigned short *)cp) = thisConfig->cliever[c.dg.hdr.dgType.clieverGroup]->ip.port();
|
||||||
|
theMachine = value = this;
|
||||||
|
mdStdDevIdx = MAX_CLIEVER + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
done:
|
||||||
|
return value;
|
||||||
|
|
||||||
|
}
|
||||||
|
mdInstrument* mdInstrument::validateClient(int handle, const mdClientBirth &c, mdResponse &r) {
|
||||||
|
|
||||||
|
mdInstrument *value=NULL;
|
||||||
|
|
||||||
|
if (thisConfig->instruments.size() < MAX_INSTRUMENTS) {
|
||||||
|
thisConfig->instruments[handle] = value = this;
|
||||||
|
// mdStdDevIdx =
|
||||||
|
}
|
||||||
|
else theseLogs->logN(1,"Too many instruments, configured limit is: %d.",MAX_INSTRUMENTS);
|
||||||
|
|
||||||
|
return value;
|
||||||
|
|
||||||
|
}
|
||||||
|
mdDataClient* mdDataClient::validateClient(int handle) {
|
||||||
|
|
||||||
|
mdDataClient *value=NULL;
|
||||||
|
|
||||||
|
if (thisConfig->clients.size() < MAX_DATACLIENTS) {
|
||||||
|
thisConfig->clients[handle] = value = this;
|
||||||
|
}
|
||||||
|
else theseLogs->logN(1,"Too many non-device clients, configured limit is: %d.",MAX_DATACLIENTS);
|
||||||
|
|
||||||
|
return value;
|
||||||
|
|
||||||
|
}
|
||||||
|
std::string mdDeviceFabrik::newFromAPI(md_device type,std::string thisSpecialOne) {
|
||||||
|
|
||||||
|
}
|
||||||
|
void mdDeviceFabrik::newFromHeartbeat(const mdClientBirth &thisOne) {
|
||||||
|
|
||||||
|
const char *kind,*outcome;
|
||||||
|
void *resultat;
|
||||||
|
|
||||||
|
int i,mdStdDevIdx;
|
||||||
|
md_device thisKind;
|
||||||
|
mdCB *newControlBlock;
|
||||||
|
mdLiveClient *newAllMap;
|
||||||
|
mdClientServer *newCliever;
|
||||||
|
mdMachine *newMachine;
|
||||||
|
mdInstrument *newInstrument;
|
||||||
|
mdResponse *result = new mdResponse(thisService->bg,thisOne.ip);
|
||||||
|
|
||||||
|
int maybe=getHandle();
|
||||||
|
|
||||||
|
result->dCat = MD_NEWBORN;
|
||||||
|
result->reply.dg.hdr = thisOne.dg.hdr;
|
||||||
|
result->reply.dg.hdr.dgType.isAckNak = true;
|
||||||
|
result->reply.dg.hdr.dgType.value = true;
|
||||||
|
result->ip = thisOne.ip;
|
||||||
|
|
||||||
|
switch(thisOne.dg.hdr.clientType) {
|
||||||
|
case MDDEV_CD:
|
||||||
|
thisKind = MDDEV_CD;
|
||||||
|
kind = "cliever";
|
||||||
|
newCliever = new mdClientServer();
|
||||||
|
if (resultat = newCliever = newCliever->validateClient( maybe, *result )) {
|
||||||
|
newCliever->ip = thisOne.ip;
|
||||||
|
mdStdDevIdx = newCliever->mdStdDevIdx;
|
||||||
|
}
|
||||||
|
else delete newCliever;
|
||||||
|
break;
|
||||||
|
case MACHINE:
|
||||||
|
thisKind = MACHINE;
|
||||||
|
kind = "machine";
|
||||||
|
newMachine = new mdMachine();
|
||||||
|
if (resultat = newMachine = newMachine->validateClient( maybe, thisOne, *result )) {
|
||||||
|
theMachine = newMachine;
|
||||||
|
newMachine->ip = thisOne.ip;
|
||||||
|
mdStdDevIdx = MAX_CLIEVER + thisOne.dg.hdr.dgType.clieverGroup;
|
||||||
|
}
|
||||||
|
else delete newMachine;
|
||||||
|
break;
|
||||||
|
case MDDEV_INSTRUMENT:
|
||||||
|
thisKind = MDDEV_INSTRUMENT;
|
||||||
|
kind = "instrument";
|
||||||
|
newInstrument = new mdInstrument();
|
||||||
|
if (!(resultat = newInstrument = newInstrument->validateClient( maybe, thisOne, *result )))
|
||||||
|
delete newInstrument;
|
||||||
|
else {newInstrument->ip = thisOne.ip;
|
||||||
|
for (i=0;i<MAX_INSTRUMENTS && thisService->instruments[i];i++);
|
||||||
|
thisService->instruments[i] = maybe;
|
||||||
|
mdStdDevIdx = 2+i;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
outcome = resultat ? "succeeded" : "failed";
|
||||||
|
theseLogs->logN(2,"The %s instantiation request %s.",kind,outcome);
|
||||||
|
|
||||||
|
if (!resultat) { result->reply.dg.hdr.dgType.value = false;
|
||||||
|
result->mdStdDevIdx = MDDEV_MD;
|
||||||
|
}
|
||||||
|
else { newAllMap = new mdLiveClient();
|
||||||
|
newAllMap->devType = thisKind;
|
||||||
|
newAllMap->mdStdDevIdx = mdStdDevIdx;
|
||||||
|
thisConfig->allClients[maybe] = newAllMap;
|
||||||
|
result->reply.dg.hdr.sinkHandle = maybe;
|
||||||
|
result->mdStdDevIdx = mdStdDevIdx;
|
||||||
|
cb[mdStdDevIdx] = newControlBlock = new mdCB;
|
||||||
|
newControlBlock->handle = maybe;
|
||||||
|
}
|
||||||
|
|
||||||
|
result->send();
|
||||||
|
|
||||||
|
}
|
||||||
|
void mdMachine::registerCmd(const char *cmdName,const mdIncoming &thisOne) {
|
||||||
|
|
||||||
|
const char *msg;
|
||||||
|
char *name;
|
||||||
|
int value = OK;
|
||||||
|
std::string arg = std::string(cmdName);
|
||||||
|
std::map<int,mdLiveClient*>::iterator iter = thisConfig->allClients.find(thisOne.dg.hdr.handle);
|
||||||
|
mdResponse *result = new mdResponse(thisService->bg,thisOne.ip);
|
||||||
|
|
||||||
|
result->reply.dg.hdr = thisOne.dg.hdr;
|
||||||
|
result->dCat = DV_MDQUERY;
|
||||||
|
|
||||||
|
if( iter == thisConfig->allClients.end() ) {
|
||||||
|
theseLogs->logN(1,"Cmd reg for device whose handle (%d) absent, ignored.", thisOne.dg.hdr.handle );
|
||||||
|
value = MDERR_NOTREADY;
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
result->mdStdDevIdx = iter->second->mdStdDevIdx;
|
||||||
|
|
||||||
|
if (cmds.empty()) {
|
||||||
|
theseLogs->logN(1,"attempt to register '%s' but device not ready to accept command registration.",cmdName);
|
||||||
|
value = MDERR_NOTREADY;
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( cmds.find(arg) == cmds.end() ) {
|
||||||
|
theseLogs->logN(1,"attempt to register '%s' whose rules has not yet been defined.",cmdName);
|
||||||
|
value = MDERR_MISSING;
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Currently presumes SCPI.
|
||||||
|
|
||||||
|
if (cmds[arg]->getHandler())
|
||||||
|
{value = MDERR_CONFLICT; goto done;}
|
||||||
|
else{
|
||||||
|
cmds[arg]->setHandler(cmds[arg]);
|
||||||
|
result->reply.dg.hdr.dgSubType = MDDG_REGSCPI;
|
||||||
|
}
|
||||||
|
|
||||||
|
done:
|
||||||
|
|
||||||
|
if (value == OK) {
|
||||||
|
msg = cmdName;
|
||||||
|
result->reply.dg.hdr.dgType.value = 1;
|
||||||
|
}
|
||||||
|
else msg = thisConfig->err[value];
|
||||||
|
|
||||||
|
result->reply.dg.hdr.msgType = MDDG_MDQUERY;
|
||||||
|
name = (char *)(&result->reply.dg.payLoad[0] + result->reply.dg.hdr.primeOffset);
|
||||||
|
|
||||||
|
strcpy(name,msg);
|
||||||
|
result->reply.dg.hdr.payloadSize = result->reply.dg.hdr.primeOffset + strlen(name) + 1;
|
||||||
|
result->send();
|
||||||
|
|
||||||
|
}
|
||||||
|
//
|
||||||
|
// TODO: figure out how to avoid this duplication in gcc and msvc 10.
|
||||||
|
//
|
||||||
|
void mdInstrument::registerCmd(const char *cmdName,const mdIncoming &thisOne) {
|
||||||
|
|
||||||
|
const char *msg;
|
||||||
|
char *name;
|
||||||
|
int value = OK;
|
||||||
|
std::string arg = std::string(cmdName);
|
||||||
|
std::map<int,mdLiveClient*>::iterator iter = thisConfig->allClients.find(thisOne.dg.hdr.handle);
|
||||||
|
mdResponse *result = new mdResponse(thisService->bg,thisOne.ip);
|
||||||
|
|
||||||
|
result->reply.dg.hdr = thisOne.dg.hdr;
|
||||||
|
result->dCat = DV_MDQUERY;
|
||||||
|
|
||||||
|
if( iter == thisConfig->allClients.end() ) {
|
||||||
|
theseLogs->logN(1,"Cmd reg for device whose handle (%d) absent, ignored.", thisOne.dg.hdr.handle );
|
||||||
|
value = MDERR_NOTREADY;
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
result->mdStdDevIdx = iter->second->mdStdDevIdx;
|
||||||
|
|
||||||
|
if (cmds.empty()) {
|
||||||
|
theseLogs->logN(1,"attempt to register '%s' but device not ready to accept command registration.",cmdName);
|
||||||
|
value = MDERR_NOTREADY;
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( cmds.find(arg) == cmds.end() ) {
|
||||||
|
theseLogs->logN(1,"attempt to register '%s' which has no rules basis.",cmdName);
|
||||||
|
value = MDERR_MISSING;
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Currently presumes SCPI.
|
||||||
|
|
||||||
|
if (cmds[arg]->getHandler())
|
||||||
|
{value = MDERR_CONFLICT; goto done;}
|
||||||
|
else{
|
||||||
|
cmds[arg]->setHandler(cmds[arg]);
|
||||||
|
result->reply.dg.hdr.dgSubType = MDDG_REGSCPI;
|
||||||
|
}
|
||||||
|
|
||||||
|
done:
|
||||||
|
|
||||||
|
if (value == OK) {
|
||||||
|
msg = cmdName;
|
||||||
|
result->reply.dg.hdr.dgType.value = 1;
|
||||||
|
}
|
||||||
|
else msg = thisConfig->err[value];
|
||||||
|
|
||||||
|
result->reply.dg.hdr.msgType = MDDG_MDQUERY;
|
||||||
|
name = (char *)(&result->reply.dg.payLoad[0] + result->reply.dg.hdr.primeOffset);
|
||||||
|
|
||||||
|
strcpy(name,msg);
|
||||||
|
result->reply.dg.hdr.payloadSize = result->reply.dg.hdr.primeOffset + strlen(name) + 1;
|
||||||
|
result->send();
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue