diff --git a/Build/gcc/Makefile b/Build/gcc/Makefile index 622ad45d..a107be96 100644 --- a/Build/gcc/Makefile +++ b/Build/gcc/Makefile @@ -91,7 +91,11 @@ CXXFLAGS = $(DEPENDENCY_FLAGS) $(INCLUDE_FLAGS) \ -std=c++11 -Wall -fPIC -pthread -pedantic # Debug target logic. +ifdef ORCHESTRATOR_DEBUG +CXXFLAGS += -g3 -DORCHESTRATOR_DEBUG=1 +else debug: CXXFLAGS += -g3 -DORCHESTRATOR_DEBUG=1 +endif debug: all # Handy aliases for building executables, scripts, and libraries. Order doesn't @@ -104,20 +108,24 @@ ROOT_EXECUTABLE = $(EXECUTABLE_DIR)/root DUMMY_EXECUTABLE = $(EXECUTABLE_DIR)/dummy LOGSERVER_EXECUTABLE = $(EXECUTABLE_DIR)/logserver LOGSERVER_MESSAGE_FILE = $(EXECUTABLE_DIR)/OrchestratorMessages.txt +NAMESERVER_EXECUTABLE = $(EXECUTABLE_DIR)/nameserver RTCL_EXECUTABLE = $(EXECUTABLE_DIR)/rtcl INJECTOR_EXECUTABLE = $(EXECUTABLE_DIR)/injector MOTHERSHIP_EXECUTABLE = $(EXECUTABLE_DIR)/mothership +MOTHERSHIP_DUMMY_EXECUTABLE = $(EXECUTABLE_DIR)/mothershipdummy -all: orchestrate launcher root dummy logserver rtcl injector mothership \ - application_staging_environment +all: orchestrate launcher root dummy logserver nameserver rtcl injector \ + mothership mothershipdummy application_staging_environment launcher: $(LAUNCHER_EXECUTABLE) orchestrate: $(ORCHESTRATE_SCRIPT) root: $(ROOT_EXECUTABLE) dummy: $(DUMMY_EXECUTABLE) logserver: $(LOGSERVER_EXECUTABLE) $(LOGSERVER_MESSAGE_FILE) +nameserver: $(NAMESERVER_EXECUTABLE) rtcl: $(RTCL_EXECUTABLE) injector: $(INJECTOR_EXECUTABLE) mothership: $(MOTHERSHIP_EXECUTABLE) +mothershipdummy: $(MOTHERSHIP_DUMMY_EXECUTABLE) # Prerequisites (objects) for building executables are defined here: include Makefile.executable_prerequisites @@ -133,7 +141,8 @@ tests: $(ALL_TESTS) $(TEST_SCRIPT) # Makefile.executable_prerequisites for the prerequisites of these executables # (obviously...) $(LAUNCHER_EXECUTABLE) $(ROOT_EXECUTABLE) $(DUMMY_EXECUTABLE) \ -$(LOGSERVER_EXECUTABLE) $(RTCL_EXECUTABLE) $(INJECTOR_EXECUTABLE): +$(LOGSERVER_EXECUTABLE) $(NAMESERVER_EXECUTABLE) $(RTCL_EXECUTABLE) \ +$(INJECTOR_EXECUTABLE) $(MOTHERSHIP_DUMMY_EXECUTABLE): @$(shell $(MKDIR) $(EXECUTABLE_DIR)) $(CXX) -pthread -Wl,-rpath-link=$(QT_LIB_DIR) \ -L$(MPICH_LIB_DIR) -L$(QT_LIB_DIR) -L/usr/lib \ @@ -282,8 +291,8 @@ clean: application_staging_environment_teardown .PRECIOUS: $(DEPENDENCY_DIR)/%.d # Non-builtin targets that do not explicitly represent files that are created. -.PHONY: all clean debug tests orchestrate root dummy logserver rtcl injector \ - mothership application_staging_environment \ +.PHONY: all clean debug tests orchestrate root dummy logserver nameserver rtcl injector \ + mothership mothershipdummy application_staging_environment \ application_staging_environment_teardown # The "all" target should be the default. diff --git a/Build/gcc/Makefile.executable_prerequisites b/Build/gcc/Makefile.executable_prerequisites old mode 100644 new mode 100755 index 8fa80e05..6dc7cd3e --- a/Build/gcc/Makefile.executable_prerequisites +++ b/Build/gcc/Makefile.executable_prerequisites @@ -79,6 +79,15 @@ HARDWARE_FILE_READER_SOURCES = $(SOURCE_DIR)/OrchBase/HardwareFileReader/Hardwar $(GENERICS_DIR)/uif.cpp \ $(HARDWARE_DEPLOYER_SOURCES) +# Sources used by executables that provide POETS name resolution. +NAME_SERVICES_SOURCES = $(SOURCE_DIR)/NameServer/ABDefs.cpp \ + $(SOURCE_DIR)/NameServer/ABRecord.cpp \ + $(SOURCE_DIR)/NameServer/ABTask.cpp \ + $(SOURCE_DIR)/NameServer/AddressBook.cpp \ + $(SOURCE_DIR)/NameServer/SBase.cpp \ + $(SOURCE_DIR)/OrchBase/CMsg_p.cpp \ + $(SOURCE_DIR)/OrchBase/P_addr.cpp + # The orchestrate execution script uses a template. ORCHESTRATE_TEMPLATE = ./Resources/orchestrate_template.sh @@ -125,7 +134,11 @@ ROOT_SOURCES = $(SOURCE_DIR)/Root/RootMain.cpp \ $(GENERICS_DIR)/filename.cpp \ $(GENERICS_DIR)/lex.cpp \ $(GENERICS_DIR)/rand.cpp \ - $(SOURCE_DIR)/NameServer/Ns_el.cpp + $(SOURCE_DIR)/Common/Debug.cpp \ + $(SOURCE_DIR)/NameServer/ABDefs.cpp \ + $(SOURCE_DIR)/NameServer/ABRecord.cpp \ + $(SOURCE_DIR)/NameServer/ABTask.cpp \ + $(SOURCE_DIR)/NameServer/AddressBook.cpp ROOT_SOURCES += $(TRULY_COMMON_SOURCES) ROOT_SOURCES += $(HARDWARE_FILE_READER_SOURCES) @@ -178,6 +191,26 @@ LOGSERVER_SOURCES += $(TRULY_COMMON_SOURCES) # from which the Orchestrator is started. LOGSERVER_MESSAGE_FILE_ORIGIN = $(SOURCE_DIR)/OrchestratorMessages.txt +# The nameserver component consists of: +# +# - The "main" file (/Source/NameServer/NameServerMain.cpp) and its derived +# class in /Source/NameServer. +# +# - The name services sources in /Source/NameServer. +# +# - The truly common sources. +# +# - The hardware and software address sources. +# +NAMESERVER_SOURCES = $(SOURCE_DIR)/NameServer/NameServerMain.cpp \ + $(SOURCE_DIR)/NameServer/NameServer.cpp \ + $(SOURCE_DIR)/Common/Debug.cpp + +NAMESERVER_SOURCES += $(NAME_SERVICES_SOURCES) +NAMESERVER_SOURCES += $(TRULY_COMMON_SOURCES) +#NAMESERVER_SOURCES += $(SOFTWARE_ADDRESS_SOURCES) +#NAMESERVER_SOURCES += $(HARDWARE_ADDRESS_SOURCES) + # The rtcl (real-time clock) component consists of: # # - The "main" file (/Source/RTCL/RTCL.cpp) and its dependencies in @@ -220,6 +253,8 @@ INJECTOR_SOURCES += $(TRULY_COMMON_SOURCES) # # - An interface source file from the softswitch, in /Source/Softswitch/src. # +# - The name services sources. +# # - The truly common sources. # # - Some "generics" (/Generics) sources. @@ -229,25 +264,40 @@ INJECTOR_SOURCES += $(TRULY_COMMON_SOURCES) # Hostlink sources are to be compiled separately. MOTHERSHIP_SOURCES = $(SOURCE_DIR)/Mothership/MothershipMain.cpp \ $(SOURCE_DIR)/Mothership/TaskInfo.cpp \ - $(SOURCE_DIR)/Mothership/TMoth.cpp \ + $(SOURCE_DIR)/Mothership/Mothership.cpp \ $(SOURCE_DIR)/Softswitch/src/poets_msg.cpp \ $(GENERICS_DIR)/dumpchan.cpp \ $(GENERICS_DIR)/rand.cpp \ + $(SOURCE_DIR)/Common/Debug.cpp \ $(SOURCE_DIR)/OrchBase/Bin.cpp \ $(SOURCE_DIR)/OrchBase/build_defs.cpp \ $(SOURCE_DIR)/OrchBase/CFrag.cpp \ - $(SOURCE_DIR)/OrchBase/CMsg_p.cpp \ $(SOURCE_DIR)/OrchBase/D_graph.cpp \ - $(SOURCE_DIR)/OrchBase/P_addr.cpp \ $(SOURCE_DIR)/OrchBase/P_device.cpp \ $(SOURCE_DIR)/OrchBase/P_message.cpp \ $(SOURCE_DIR)/OrchBase/P_pin.cpp \ $(SOURCE_DIR)/OrchBase/P_task.cpp \ - $(SOURCE_DIR)/Common/Debug.cpp \ $(HARDWARE_DEPLOYER_SOURCES) +MOTHERSHIP_SOURCES += $(NAME_SERVICES_SOURCES) MOTHERSHIP_SOURCES += $(TRULY_COMMON_SOURCES) +# A dummy mothership is an executable to provide mothership-like functionality +# when there is no connected box with a HostLink interface. It consists of: +# +# - The "main" file (/Source/Mothership/MothershipDummyMain.cpp) and its +# dependencies in /Source/Mothership +# +# - The name services sources. +# +# - The truly common sources. +# +MOTHERSHIP_DUMMY_SOURCES = $(SOURCE_DIR)/Mothership/MothershipDummyMain.cpp \ + $(SOURCE_DIR)/Mothership/MothershipDummy.cpp + +MOTHERSHIP_DUMMY_SOURCES += $(NAME_SERVICES_SOURCES) +MOTHERSHIP_DUMMY_SOURCES += $(TRULY_COMMON_SOURCES) + # Convert these lists of sources into a list of objects to define a dependency # system for the linker. @@ -279,7 +329,7 @@ $(1)_OBJECTS := $(call orch_sources_to_objects, $($(1)_SOURCES)) endef $(foreach object_set,\ - LAUNCHER ROOT INJECTOR DUMMY LOGSERVER RTCL MOTHERSHIP,\ + LAUNCHER ROOT INJECTOR DUMMY LOGSERVER NAMESERVER RTCL MOTHERSHIP MOTHERSHIP_DUMMY,\ $(eval $(call OBJECT_TEMPLATE,$(object_set)))) # Define executable prerequisites. RULE_TEMPLATE defines the substitutions made @@ -292,5 +342,5 @@ $($(1)_EXECUTABLE): $($(1)_OBJECTS) endef $(foreach executable_name,\ - LAUNCHER ROOT DUMMY LOGSERVER RTCL INJECTOR MOTHERSHIP,\ + LAUNCHER ROOT DUMMY LOGSERVER NAMESERVER RTCL INJECTOR MOTHERSHIP MOTHERSHIP_DUMMY,\ $(eval $(call RULE_TEMPLATE,$(executable_name)))) diff --git a/OrchestratorFrontEnd.pro b/OrchestratorFrontEnd.pro index 133bf129..7b9ee83b 100644 --- a/OrchestratorFrontEnd.pro +++ b/OrchestratorFrontEnd.pro @@ -88,7 +88,6 @@ SOURCES += \ ./Generics/uif.cpp \ Source/Common/Pglobals.cpp \ Source/OrchBase/P_super.cpp \ - Source/NameServer/Ns_el.cpp \ Source/OrchBase/P_owner.cpp \ Source/OrchBase/build_defs.cpp \ Source/OrchBase/CMsg_p.cpp @@ -181,7 +180,6 @@ HEADERS += \ ./Source/OrchBase/build_defs.h \ Source/OrchBase/P_super.h \ Source/Injector/Injector.h \ - Source/NameServer/Ns_el.h \ Source/OrchBase/P_owner.h \ Generics/dumpchan.h \ Source/OrchBase/CMsg_p.h \ diff --git a/Source/Common/CommonBase.h b/Source/Common/CommonBase.h index e1222545..b51cf4ef 100644 --- a/Source/Common/CommonBase.h +++ b/Source/Common/CommonBase.h @@ -5,6 +5,7 @@ #include #include using namespace std; +#include "Debug.h" #include "PMsg_p.hpp" #include "ProcMap.h" #include "pthread.h" diff --git a/Source/Common/Decode.cpp b/Source/Common/Decode.cpp index bc0cab0e..835f8606 100644 --- a/Source/Common/Decode.cpp +++ b/Source/Common/Decode.cpp @@ -11,6 +11,10 @@ DON'T PUT ANY #include IN THIS FILE It's virtual here because this makes it an invalid translation unit, so if you try to compile it, the compiler will squeak. + +A similar decode exists in SDecode.cpp, intended specifically for classes that +derive from SBase. Do not use this file (Decode.cpp) if you are deriving from +SBase. Similarly, do not use SDecode.cpp if you derive directly from CommonBase. */ //============================================================================== diff --git a/Source/Common/PMsg_p.cpp b/Source/Common/PMsg_p.cpp index d7b4ca19..19c4f438 100644 --- a/Source/Common/PMsg_p.cpp +++ b/Source/Common/PMsg_p.cpp @@ -12,6 +12,7 @@ and when I come across a need. PMsg_p::PMsg_p(MPI_Comm c):Msg_p(){comm=c;} PMsg_p::PMsg_p(byte * pb,int l,MPI_Comm c):Msg_p(pb,l){comm=c;} +PMsg_p::PMsg_p(Msg_p & m, MPI_Comm c):Msg_p(m){comm=c;} PMsg_p::PMsg_p(PMsg_p & r):Msg_p(r){comm=r.comm;} PMsg_p::~PMsg_p(void){} diff --git a/Source/Common/PMsg_p.hpp b/Source/Common/PMsg_p.hpp index a1aa431c..8a271706 100644 --- a/Source/Common/PMsg_p.hpp +++ b/Source/Common/PMsg_p.hpp @@ -10,6 +10,7 @@ class PMsg_p : public Msg_p { public : PMsg_p(MPI_Comm c=MPI_COMM_NULL); PMsg_p(byte * pb,int l,MPI_Comm c=MPI_COMM_NULL); + PMsg_p(Msg_p & m, MPI_Comm c=MPI_COMM_NULL); PMsg_p(PMsg_p & r); virtual ~ PMsg_p(void); void Bcast(); diff --git a/Source/Common/Pglobals.cpp b/Source/Common/Pglobals.cpp index 10ecef0d..7761461d 100644 --- a/Source/Common/Pglobals.cpp +++ b/Source/Common/Pglobals.cpp @@ -15,6 +15,7 @@ const byte Q::INJCT; const byte Q::NAME; const byte Q::SUPR; const byte Q::TINS; +const byte Q::CANDC; // Level 1 subkeys const byte Q::PING; const byte Q::POST; @@ -28,10 +29,16 @@ const byte Q::RUN; const byte Q::LOAD; const byte Q::STOP; const byte Q::TOPO; -const byte Q::DIST; -const byte Q::TDIR; const byte Q::SHOW; const byte Q::ACPT; +// Nameserver additions ---------------------------------------------------- +const byte Q::SEND; +const byte Q::QRY; +const byte Q::RPLY; +const byte Q::DATA; +const byte Q::CFG; +const byte Q::CMDC; +const byte Q::DUMP; // temporary use: for MPI testing ------------------------------------------ const byte Q::M0; const byte Q::M1; @@ -41,7 +48,41 @@ const byte Q::MN; const byte Q::REQ; const byte Q::ACK; const byte Q::FWD; +// Nameserver additions --------------------------------------------------- +const byte Q::TASK; +const byte Q::DEVT; +const byte Q::DEVI; +const byte Q::SUPV; +const byte Q::EXTN; +const byte Q::LIST; +const byte Q::BLD; +const byte Q::INTG; +const byte Q::STATE; +// moved from L1 to L2 for Nameserver -------------------------------------- +const byte Q::DIST; +const byte Q::RECL; +const byte Q::TDIR; +const byte Q::DEL; +const byte Q::MONI; +const byte Q::LOGN; +const byte Q::DEVE; // Level 3 subkeys +const byte Q::FALSE; +const byte Q::TRUE; +const byte Q::OFF; +const byte Q::ON; +const byte Q::NF; +const byte Q::TNF; +const byte Q::NM; +const byte Q::ID; +const byte Q::ALL; +const byte Q::NGRP; +const byte Q::IGRP; +const byte Q::NSUP; +const byte Q::ISUP; +const byte Q::IN; +const byte Q::OUT; +const byte Q::ATR; // Not a value const byte Q::NAV; diff --git a/Source/Common/Pglobals.h b/Source/Common/Pglobals.h index a99e1cbc..d62fc70f 100644 --- a/Source/Common/Pglobals.h +++ b/Source/Common/Pglobals.h @@ -127,22 +127,63 @@ static const byte RUN = 0x48; static const byte LOAD = 0x49; static const byte STOP = 0x4a; static const byte TOPO = 0x4b; -static const byte DIST = 0x4c; -static const byte RECL = 0x4d; -static const byte TDIR = 0x4e; -static const byte SHOW = 0x4f; -static const byte ACPT = 0x50; +static const byte SHOW = 0x4c; +static const byte ACPT = 0x4d; +// Nameserver additions ---------------------------------------------------- +static const byte SEND = 0x4e; +static const byte QRY = 0x4f; +static const byte RPLY = 0x50; +static const byte DATA = 0x51; +static const byte CFG = 0x52; +static const byte CMDC = 0x53; +static const byte DUMP = 0x54; // temporary use: for MPI testing ------------------------------------------ static const byte M0 = 0x60; static const byte M1 = 0x61; static const byte MN = 0x62; -//-------------------------------------------------------------------------- +// _________________________________________________________________________ // Level 2 subkeys static const byte REQ = 0x80; static const byte ACK = 0x81; static const byte FWD = 0x82; +// Nameserver additions --------------------------------------------------- +static const byte TASK = 0x83; +static const byte DEVT = 0x84; +static const byte DEVI = 0x85; +static const byte SUPV = 0x86; +static const byte EXTN = 0x87; +static const byte ATTR = 0x88; +static const byte LIST = 0x89; +static const byte BLD = 0x8a; +static const byte INTG = 0x8b; +// moved from L1 to L2 for Nameserver -------------------------------------- +static const byte DIST = 0x8c; +static const byte RECL = 0x8d; +static const byte TDIR = 0x8e; +static const byte STATE = 0x8f; +static const byte DEL = 0x90; +static const byte MONI = 0x91; +static const byte LOGN = 0x92; +static const byte DEVE = 0x93; +// _________________________________________________________________________ // Level 3 subkeys - +static const byte FALSE = 0xc0; +static const byte TRUE = 0xc1; +static const byte OFF = 0xc2; +static const byte ON = 0xc3; +static const byte NF = 0xc4; +static const byte TNF = 0xc5; +static const byte NM = 0xc6; +static const byte ID = 0xc7; +static const byte ALL = 0xc8; +static const byte NGRP = 0xc9; +static const byte IGRP = 0xca; +static const byte NSUP = 0xcb; +static const byte ISUP = 0xcc; +static const byte IN = 0xcd; +static const byte OUT = 0xce; +static const byte ATR = 0xcf; + // Not a value static const byte NAV = 0xff; static const int NAP = -1; @@ -156,9 +197,10 @@ static const byte ROOT = 0x00; #define csLOGSERVERproc "LogServer:CommonBase" #define csRTCLproc "RTCL:CommonBase" #define csINJECTORproc "Injector:CommonBase" -#define csNAMESERVERproc "NameServer:CommonBase" +#define csNAMESERVERproc "NameServer:SBase:CommonBase" #define csMONITORproc "Monitor:CommonBase" -#define csMOTHERSHIPproc "TMoth:CommonBase" +#define csMOTHERSHIPproc "Mothership:SBase:CommonBase" +//#define csMOTHERSHIPproc "TMoth:CommonBase" #define csMPITESTproc "MPITest:CommonBase" // tag defined as a directive because MPI libraries are c-based, have no concept diff --git a/Source/Common/SDecode.cpp b/Source/Common/SDecode.cpp new file mode 100644 index 00000000..4d0a0e15 --- /dev/null +++ b/Source/Common/SDecode.cpp @@ -0,0 +1,58 @@ + +/* The reason for the rather bizarre way of defining this method - via the +#include in the .h file - is because it's part of several derived classes, and +FnMapx and this are different in each derived class, and I can't find a way +of doing this without breaking syntax. Rather, I can and have, and this is it, +but I really, really don't like it. + +Perfection is the enemy of progress, and all that. This works. + +DON'T PUT ANY #include IN THIS FILE + +It's virtual here because this makes it an invalid translation unit, so if you +try to compile it, the compiler will squeak. + +This particular version - SDecode, is intended for use in classes that +derive from SBase. It's duplicated from Decode.cpp because (1) you can't use +a dynamic_cast to check whether the class is derived from SBase without including +all the SBase code anyway in classes that have nothing to do with it; (2) we +don't want to use an #ifdef directive as an ugly 'solution' for this. +*/ + +//============================================================================== + +virtual unsigned Decode(PMsg_p * pPkt, unsigned cIdx) +// Routine to decode - that is, send to the correct handler - incoming messages +// Look at the message key, and use the map to call the appropriate (derived +// class) member. If it's not in the derived class map, try the base class map. +// If it's not there either, it gets junked. +{ +// printf("%s::Decode ",Sderived.c_str()); + // Handler in the derived class? +if (FnMapx[cIdx]->find(pPkt->Key())!=FnMapx[cIdx]->end()) { + // printf(".. derived, key 0x%x\n", pPkt->Key()); fflush(stdout); + return (this->*(*FnMapx[cIdx])[pPkt->Key()])(pPkt,cIdx); +} + // No. In SBase? +if (SBase::FnMapx[cIdx]->find(pPkt->Key())!=SBase::FnMapx[cIdx]->end()) { + // printf(".. sbase, key 0x%x\n", pPkt->Key()); fflush(stdout); + return (this->*(*SBase::FnMapx[cIdx])[pPkt->Key()])(pPkt,cIdx); +} + // Nope. Base class? +if (CommonBase::FnMapx[cIdx]->find(pPkt->Key())!=CommonBase::FnMapx[cIdx]->end()) { + // printf(".. base, key 0x%x\n", pPkt->Key()); fflush(stdout); + return (this->*(*CommonBase::FnMapx[cIdx])[pPkt->Key()])(pPkt,cIdx); +} +// printf(".. dropped, key 0x%x\n", pPkt->Key()); fflush(stdout); + // Nope. Kick. + // Pull out the unknown key and post what + // little we know to the LogServer +Post(101,Sderived,int2str(pPkt->Src()),pPmap[cIdx]->vPmap[pPkt->Src()].P_class,int2str(pPkt->Tgt()), + pPmap[cIdx]->vPmap[pPkt->Tgt()].P_class,hex2str(pPkt->Key())); +return 0; // Return "keep going" value +} + +//============================================================================== + + + diff --git a/Source/Mothership/Mothership.cpp b/Source/Mothership/Mothership.cpp new file mode 100644 index 00000000..9a17d47d --- /dev/null +++ b/Source/Mothership/Mothership.cpp @@ -0,0 +1,1417 @@ +//------------------------------------------------------------------------------ + +#include "Mothership.h" +#include +#include +#include +#include "Pglobals.h" +#include "CMsg_p.h" +#include "flat.h" +#include "string.h" +#include "limits.h" + +const int Mothership::NumBoards; + +//============================================================================== + +Mothership::Mothership(int argc,char * argv[],string d) : + SBase(argc,argv,d,string(__FILE__)), HostLink() +{ + + +FnMapx.push_back(new FnMap_t); // create a new event map in the derived class + +// Load the incoming event map +(*FnMapx[0])[PMsg_p::KEY(Q::EXIT )] = &Mothership::OnExit; // overloads CommonBase +(*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::CFG,Q::DIST )] = &Mothership::OnCfg; +(*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::CFG,Q::TDIR )] = &Mothership::OnCfg; +(*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::CFG,Q::BLD )] = &Mothership::OnCfg; +(*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::CFG,Q::RECL )] = &Mothership::OnCfg; +(*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::CFG,Q::DEL )] = &Mothership::OnCfg; +(*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::CFG,Q::STATE )] = &Mothership::OnCfg; +(*FnMapx[0])[PMsg_p::KEY(Q::CMND,Q::LOAD )] = &Mothership::OnCmnd; +(*FnMapx[0])[PMsg_p::KEY(Q::CMND,Q::RUN )] = &Mothership::OnCmnd; +(*FnMapx[0])[PMsg_p::KEY(Q::CMND,Q::STOP )] = &Mothership::OnCmnd; +(*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::DUMP,Q::TASK,Q::ALL )] = &Mothership::OnDump; +(*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::DUMP,Q::TASK,Q::NM )] = &Mothership::OnDump; +(*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::DUMP,Q::LIST )] = &Mothership::OnDump; +(*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::SEND,Q::DEVI,Q::NM )] = &Mothership::OnSend; +(*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::SEND,Q::DEVI,Q::ID )] = &Mothership::OnSend; +(*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::SEND,Q::DEVI,Q::ALL )] = &Mothership::OnSend; +(*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::SEND,Q::DEVI,Q::NGRP)] = &Mothership::OnSend; +(*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::SEND,Q::DEVI,Q::IGRP)] = &Mothership::OnSend; +(*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::SEND,Q::DEVI,Q::NSUP)] = &Mothership::OnSend; +(*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::SEND,Q::DEVI,Q::ISUP)] = &Mothership::OnSend; +(*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::SEND,Q::SUPV )] = &Mothership::OnSend; +(*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::SEND,Q::EXTN )] = &Mothership::OnSend; +(*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::SEND,Q::DEVT,Q::NM )] = &Mothership::OnSend; +(*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::SEND,Q::DEVT,Q::IN )] = &Mothership::OnSend; +(*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::SEND,Q::DEVT,Q::OUT )] = &Mothership::OnSend; +(*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::SEND,Q::ATTR )] = &Mothership::OnSend; +(*FnMapx[0])[PMsg_p::KEY(Q::SUPR )] = &Mothership::OnSuper; +(*FnMapx[0])[PMsg_p::KEY(Q::SYST,Q::HARD )] = &Mothership::OnSyst; +(*FnMapx[0])[PMsg_p::KEY(Q::SYST,Q::KILL )] = &Mothership::OnSyst; +(*FnMapx[0])[PMsg_p::KEY(Q::SYST,Q::SHOW )] = &Mothership::OnSyst; +(*FnMapx[0])[PMsg_p::KEY(Q::SYST,Q::TOPO )] = &Mothership::OnSyst; +(*FnMapx[0])[PMsg_p::KEY(Q::TINS )] = &Mothership::OnTinsel; + +// mothership's address in POETS space is the host bridge ID: 10 | BoxXCoordinate << +// number of bits in the Tinsel address space. The version below only deals with +// left-hand-side boxes. Later this should be fixed. +PAddress = 1 << (1+TinselMeshYBits+TinselMeshXBits+TinselLogCoresPerBoard+TinselLogThreadsPerCore); + +twig_running = false; +ForwardMsgs = false; // don't forward tinsel traffic yet + +MPISpinner(); // Spin on *all* messages; exit on DIE +DebugPrint("Exiting Mothership. Closedown flags: AcceptConns: %s, " + "ForwardMsgs: %s\n", AcceptConns ? "true" : "false", + ForwardMsgs ? "true" : "false"); +if (twig_running) StopTwig(); // wait for the twig thread, if it's still somehow running. +printf("********* Mothership rank %d on the way out\n",Urank); fflush(stdout); +} + +//------------------------------------------------------------------------------ + +Mothership::~Mothership() +// Destructor gets rid of response pins added by the Twig, tasks, and +// additional event maps for comms other than MPI_COMM_WORLD +{ +DebugPrint("********* Mothership rank %d destructor\n",Urank); fflush(stdout); +WALKMAP(uint32_t,PinBuf_t*,TwigMap,D) // get rid of Twig pins +{ + if (D->second) + { + WALKMAP(uint16_t,char*,(*(D->second)),P) + delete[] P->second; + delete D->second; + } +} +WALKMAP(string,TaskInfo_t*,TaskMap,T) // and deployed tasks + delete T->second; + +WALKVECTOR(FnMap_t*,FnMapx,F) + delete *F; // get rid of derived class function tables +DebugPrint("********* Mothership rank %d destructor finished\n",Urank); +fflush(stdout); +} + +//------------------------------------------------------------------------------ + +unsigned Mothership::Boot(string task) +/* Boot handles 2 functions: starting the cores and then dealing with a software + barrier at startup. For the moment, the Tinsel barrier is not thread-safe, + so this has to be executed at the level of the main mothership process. + (We might prefer [PIP?] a thread-safe start process that can take a vector of + cores and boot them all in parallel) +*/ +{ + DebugPrint("Entering boot stage\n"); + if (TaskMap.find(task) == TaskMap.end()) // no such task + { + Post(107,task); + return 1; + } + DebugPrint("Task %s being booted\n", task.c_str()); + switch(TaskMap[task]->status) + { + case TaskInfo_t::TASK_BOOT: // already being booted. This should not be run again. + { + Post(513, task, TaskInfo_t::Task_Status.find(TaskMap[task]->status)->second); + TaskMap[task]->status = TaskInfo_t::TASK_ERR; + return 2; + } + DebugPrint("Task is ready to be booted\n"); + case TaskInfo_t::TASK_RDY: + { + uint32_t mX, mY, core, thread; + // create a response bitmap to receive the various startup barrier messages. + // do this before running 'go' so that we can receive the responses in one block. + map*> t_start_bitmap; + vector taskCores = TaskMap[task]->CoresForTask(); + P_addr coreAddress; + WALKVECTOR(P_core*,taskCores,C) + { + unsigned numThreads = (*C)->P_threadm.size(); + if (numThreads) + { + (*C)->get_hardware_address()->populate_a_software_address(&coreAddress); + // the bitmap is a vector of unsigneds, each bit representing a + // thread to be started. We can set up all threads in the bitmap + // below the last modulo-32 group by simply initialising a vector of + // FFFFFFFF's + t_start_bitmap[Mothership::GetHWAddr(coreAddress)] = + new vector(numThreads/(8*sizeof(unsigned)),UINT_MAX); + unsigned remainder = numThreads%(8*sizeof(unsigned)); + if (remainder) + t_start_bitmap[Mothership::GetHWAddr(coreAddress)]->push_back + (UINT_MAX >> ((8*sizeof(unsigned))-remainder)); + } + } + DebugPrint("Task start bitmaps created for %d cores\n", t_start_bitmap.size()); + // actually boot the cores + WALKVECTOR(P_core*,taskCores,C) + { + (*C)->get_hardware_address()->populate_a_software_address(&coreAddress); + fromAddr(Mothership::GetHWAddr(coreAddress),&mX,&mY,&core,&thread); + DebugPrint("Booting %d threads on threadID 0x%X at x:%d y:%d c:%d\n", + (*C)->P_threadm.size(),Mothership::GetHWAddr(coreAddress), + mX,mY,core); + DebugPrint("* startOne(meshX=%u, meshY=%u, coreId=%u, numThreads=%lu)\n", + mX, mY, core, (*C)->P_threadm.size()); + startOne(mX,mY,core,(*C)->P_threadm.size()); + DebugPrint("%d threads started on threadID 0x%X at x:%d y:%d c:%d\n", + (*C)->P_threadm.size(),Mothership::GetHWAddr(coreAddress), + mX,mY,core); + DebugPrint("Triggering %d threads on core %d at x:%d y:%d c:%d\n", + (*C)->P_threadm.size(),Mothership::GetHWAddr(coreAddress), + mX,mY,core); + DebugPrint("* goOne(meshX=%u, meshY=%u, coreId=%u)\n", mX, mY, core); + goOne(mX,mY,core); + } + // per Matt Naylor comment safer to start all cores then issue the go command to all cores separately. + // WALKVECTOR(P_core*,taskCores,C) + // { + // (*C)->get_hardware_address()->populate_a_software_address(&coreAddress); + // DebugPrint("Triggering %d threads on core %d at x:%d y:%d c:%d\n",(*C)->P_threadm.size(),Mothership::GetHWAddr(coreAddress),mX,mY,core); + // goOne(mX,mY,core); + // } + // DebugPrint("%d cores booted\n", taskCores.size()); + + // deal with the barrier. Keep receiving messages until the bitmap is clear. + P_Sup_Msg_t barrier_msg; + while (!t_start_bitmap.empty()) + { + recvMsg(&barrier_msg, p_sup_hdr_size()); // blocking receive message call + DebugPrint("Received a message from a core during application barrier\n"); + if ((barrier_msg.header.sourceDeviceAddr & P_SUP_MASK) && + (barrier_msg.header.command == P_PKT_MSGTYP_BARRIER)) + { + DebugPrint("Barrier message from thread ID 0x%X\n", + barrier_msg.header.sourceDeviceAddr ^ P_SUP_MASK); + fromAddr(((barrier_msg.header.sourceDeviceAddr ^ P_SUP_MASK) >> P_THREAD_OS), + &mX,&mY,&core,&thread); + unsigned hw_core = toAddr(mX,mY,core,0); + DebugPrint("Received a barrier acknowledge from core %#X\n", hw_core); + // valid core? + if (t_start_bitmap.find(hw_core) != t_start_bitmap.end()) + { + DebugPrint("Thread %d on core %d responding\n", thread, core); + DebugPrint("Core bitmap for thread %d before acknowledge: %#X\n", + thread, + (*t_start_bitmap[hw_core])[thread/(8*sizeof(unsigned))]); + // clear the bitmap bit + (*t_start_bitmap[hw_core])[thread/(8*sizeof(unsigned))] &= + (~(1 << (thread%(8*sizeof(unsigned))))); + DebugPrint("Core bitmap for thread %d after acknowledge: %#X\n", + thread, + (*t_start_bitmap[hw_core])[thread/(8*sizeof(unsigned))]); + vector::iterator S; + DebugPrint("Core bitmap currently has %d elements\n", + t_start_bitmap.size()); + // any elements of the vector still have uncleared bits? + for (S = t_start_bitmap[hw_core]->begin(); + S != t_start_bitmap[hw_core]->end(); S++) if (*S) break; + DebugPrint("Core bitmap for core %d has %d subelements\n", + hw_core, t_start_bitmap[hw_core]->size()); + // all clear. This core is good to go. Remove it from the + // bitmap vector. + if (S == t_start_bitmap[hw_core]->end()) + { + DebugPrint("Removing core bitmap for core %d\n", + thread, t_start_bitmap[hw_core]->size()); + t_start_bitmap[hw_core]->clear(); + delete t_start_bitmap[hw_core]; + t_start_bitmap.erase(hw_core); + } + } + } + } + // Through the barrier. + DebugPrint("%d cores passed Mothership barrier and awaiting start signal \n", taskCores.size()); + // MPI_Barrier(Comms[0]); // barrier on the mothercore side (temporarily removed until we have multi-mothership systems) + void* args = this; + // at this point, load the Supervisor for the task. + SuperHandle = dlopen((TaskMap[task]->BinPath+"/libSupervisor.so").c_str(), + RTLD_NOW); + if (!SuperHandle) Post(532,(TaskMap[task]->BinPath+"/libSupervisor.so"), + int2str(Urank),string(dlerror())); + else + { + Post (540,int2str(Urank)); + //int (*SupervisorInit)(CommonBase*) = reinterpret_cast(dlsym(SuperHandle, "SupervisorInit")); + // set up initialiser function + int (*SupervisorInit)() = reinterpret_cast(dlsym(SuperHandle, + "SupervisorInit")); + string badFunc(""); + //if (!SupervisorInit || (*SupervisorInit)(this)) badFunc = "SupervisorInit"; + // run the initialiser + if (!SupervisorInit || (*SupervisorInit)()) badFunc = "SupervisorInit"; + // and set up the main Supervisor function + else if ((SupervisorCall = reinterpret_cast + (dlsym(SuperHandle, "SupervisorCall"))) == NULL) + badFunc = "SupervisorCall"; + if (badFunc.size()) Post(533,badFunc,int2str(Urank),string(dlerror())); + } + // create a thread (later will be a process) to deal with packets from + // tinsel cores. Have to do it here, after all the initial setup is complete, + // otherwise setup barrier communications may fail. + ForwardMsgs = true; // set forwarding on so thread doesn't immediately exit + if (pthread_create(&Twig_thread,NULL,Twig,args)) + { + twig_running = false; + ForwardMsgs = false; + Post(531, int2str(Urank)); + } + else twig_running =true; + Post(561, task.c_str()); + TaskMap[task]->status = TaskInfo_t::TASK_BARR; // now at barrier on the tinsel side. + return 0; + } + case TaskInfo_t::TASK_RUN: + case TaskInfo_t::TASK_STOP: + case TaskInfo_t::TASK_END: + break; + default: + TaskMap[task]->status = TaskInfo_t::TASK_ERR; + } + Post(511,task,"booted",TaskInfo_t::Task_Status.find(TaskMap[task]->status)->second); + return 3; +} + +//------------------------------------------------------------------------------ + +unsigned Mothership::CmLoad(string task) +/* Load a task to the system. The task needs to have been previously deployed. + This function responds to the task /init command (or equivalent). It farms + off loading of individual boards to the LoadBoard function. LoadBoard CAN be + threaded, but current Tinsel implementations don't support it, so for the + moment LoadBoard works (slowly!) one board at a time. +*/ +{ + DebugPrint("%d tasks deployed to Mothership\n", TaskMap.size()); + if (TaskMap.find(task) == TaskMap.end()) // task deployed? + { + Post(515, task, int2str(Urank)); // No. Abandon. + return 0; + } + DebugPrint("Task %s found\n", task.c_str()); + WALKMAP(string,TaskInfo_t*,TaskMap,T) + { + // only one task can be active at a time (for the moment. Later we may + // want more sophisticated task mapping that allows multiple active tasks + // on non-overlapping board sets). Look for other active tasks. + if ((T->first != task) && (T->second->status & (TaskInfo_t::TASK_BOOT | + TaskInfo_t::TASK_RDY | + TaskInfo_t::TASK_BARR | + TaskInfo_t::TASK_RUN | + TaskInfo_t::TASK_STOP | + TaskInfo_t::TASK_ERR))) + { + // found another task seemingly active. What state is it in? + map::const_iterator othStatus = + TaskInfo_t::Task_Status.find(T->second->status); + if (othStatus == TaskInfo_t::Task_Status.end()) + T->second->status = TaskInfo_t::TASK_ERR; // status corrupted. + Post(508,T->first); + return 2; + // other task was in a sane but active state. Abandon. + Post(509,task,T->first,othStatus->second); + return 0; + } + } + DebugPrint("Task status confirmed as TASK_LOAD\n"); + // Only this task is active. But since it's already active it + // doesn't need to be loaded. + if (!((TaskMap[task]->status == TaskInfo_t::TASK_IDLE) || + (TaskMap[task]->status == TaskInfo_t::TASK_END))) + { + Post(511,task,"loaded to hardware", + TaskInfo_t::Task_Status.find(TaskMap[task]->status)->second); + return 0; + } + // otherwise we are good to go. Task goes to boot state. + TaskMap[task]->status = TaskInfo_t::TASK_BOOT; + DebugPrint("Task status is TASK_BOOT\n"); + // Now load the boards. For the moment one at a time, later + // this will be threaded. + long coresThisTask = 0; + long coresLoaded = 0; + long boardsLoaded = 0; + unsigned totalBoards = TaskMap[task]->BoardsForTask().size(); + unsigned totalCores = TaskMap[task]->CoresForTask().size(); + DebugPrint("Task will use %ld boards.\n", + totalBoards); + // for each board mapped to the task, + WALKVECTOR(P_board*,TaskMap[task]->BoardsForTask(),B) + { + // some output is useful here to keep the operator aware that + // we're doing something + Post(563, long2str(coresLoaded), + uint2str(totalCores), + uint2str(boardsLoaded), + uint2str(totalBoards)); + DebugPrint("Board \"%s\" will use %ld mailboxes.\n", + (*B)->Name().c_str(), (*B)->G.SizeNodes()); + WALKPDIGRAPHNODES(AddressComponent, P_mailbox*, + unsigned, P_link*, + unsigned, P_port*, (*B)->G, MB) + { + DebugPrint("Mailbox \"%s\" will use %ld cores\n", + (*B)->G.NodeData(MB)->Name().c_str(), + (*B)->G.NodeData(MB)->P_corem.size()); + // less work to compute as we go along than to call CoresForTask.size() + // MLV: Not so sure in a post-mailbox world any more... + coresThisTask += (*B)->G.NodeData(MB)->P_corem.size(); + } + coresLoaded += LoadBoard(*B); // load the board (unthreaded model) + ++boardsLoaded; + } + DebugPrint("All boards finished. %ld cores were in the hardware model for " + "this task. %ld cores have been loaded.\n", + coresThisTask, coresLoaded); + // abandoning the boot if load failed + if (coresLoaded < coresThisTask) + { + TaskMap[task]->status = TaskInfo_t::TASK_ERR; + Post(518,task,int2str(coresLoaded),int2str(coresThisTask)); + return 1; + } + TaskMap[task]->status = TaskInfo_t::TASK_RDY; + DebugPrint("Task status is TASK_RDY\n"); + Post(560,task); + // boot the board (which has to be done from the main thread as it is not + // thread-safe) + return Boot(task); +} + +//------------------------------------------------------------------------------ + +unsigned Mothership::CmRun(string task) +// Run a specified task. This passes the tinsel-side barrier to start +// application execution. Invoked using task /run or equivalent. +{ + if (TaskMap.find(task) == TaskMap.end()) // task exists? + { + Post(107, task); + return 0; + } + switch(TaskMap[task]->status) // and is it ready to run? + { + case TaskInfo_t::TASK_BOOT: // these states mean it's still booting. + case TaskInfo_t::TASK_RDY: // i.e. not ready. + { + Post(513, task, TaskInfo_t::Task_Status.find(TaskMap[task]->status)->second); + TaskMap[task]->status = TaskInfo_t::TASK_ERR; + return 1; + } + case TaskInfo_t::TASK_STOP: + Post(512, task,"run","TASK_STOP"); + TaskMap[task]->status = TaskInfo_t::TASK_ERR; + return 2; + case TaskInfo_t::TASK_END: + case TaskInfo_t::TASK_IDLE: // active or not deployed + case TaskInfo_t::TASK_RUN: + Post(511, task,"run",TaskInfo_t::Task_Status.find(TaskMap[task]->status)->second); + return 0; + case TaskInfo_t::TASK_BARR: + { + DebugPrint("Task %s entering tinsel barrier\n",task.c_str()); + P_Msg_Hdr_t barrier_msg; + barrier_msg.messageLenBytes = p_hdr_size(); // barrier is only a header. No payload. + barrier_msg.destEdgeIndex = 0; // no edge index necessary. + barrier_msg.destPin = P_SUP_PIN_INIT; // it goes to the system __init__ pin + barrier_msg.messageTag = P_MSG_TAG_INIT; // and is of message type __init__. + DebugPrint("Building thread list for task %s\n",task.c_str()); + // build a list of the threads in this task (that should be released + // from barrier) + vector threadsToRelease; + P_addr threadAddress; + WALKVECTOR(P_thread*,TaskMap[task]->ThreadsForTask(),R) + { + (*R)->get_hardware_address()->populate_a_software_address(&threadAddress); + threadsToRelease.push_back(Mothership::GetHWAddr(threadAddress)); + } + DebugPrint("Issuing barrier release to %d threads in task %s, using " + "message address 0x%X\n", threadsToRelease.size(), + task.c_str(), DEST_BROADCAST); + // and then issue the barrier release to the threads. + WALKVECTOR(unsigned,threadsToRelease,R) + { + // send to every device on the thread with a supervisor message + barrier_msg.destDeviceAddr = DEST_BROADCAST; + DebugPrint("Attempting to send barrier release message to the thread " + "with hardware address %u.\n", *R); + // this is the 'magic invocation' to release the Tinsel barrier + send(*R,(p_hdr_size()/(4 << TinselLogWordsPerFlit) + + (p_hdr_size()%(4 << TinselLogWordsPerFlit) ? 1 : 0)), + &barrier_msg, true); + } + DebugPrint("Tinsel threads now on their own for task %s\n",task.c_str()); + TaskMap[task]->status = TaskInfo_t::TASK_RUN; + return 0; + } + default: + TaskMap[task]->status = TaskInfo_t::TASK_ERR; + } + Post(511,task,"run",TaskInfo_t::Task_Status.find(TaskMap[task]->status)->second); + return 2; +} + +//------------------------------------------------------------------------------ + +unsigned Mothership::CmStop(string task) +// Handle a task /stop command (which ends the simulation). Even if the task +// has internal termination detection, task /stop should always be issued to +// shut down the task cleanly (close Supervisors, clear memory, etc.) +{ + if (TaskMap.find(task) == TaskMap.end()) // no such task. + { + Post(107, task); + return 0; + } + switch(TaskMap[task]->status) // is task in a state ready to be stopped? + { + case TaskInfo_t::TASK_IDLE: + case TaskInfo_t::TASK_END: + Post(511, task,"stopped", + TaskInfo_t::Task_Status.find(TaskMap[task]->status)->second); + return 0; + case TaskInfo_t::TASK_BOOT: + case TaskInfo_t::TASK_RDY: + case TaskInfo_t::TASK_STOP: + { + Post(813, task, "stopped", + TaskInfo_t::Task_Status.find(TaskMap[task]->status)->second); + return 1; + } + case TaskInfo_t::TASK_BARR: + { + // if we are at the barrier the simplest approach to + // stop cleanly is simply to start and immediately stop. + // thus as long as starting doesn't error, fall through + // to the running condition immediately below. + if (CmRun(task)) + { + TaskMap[task]->status = TaskInfo_t::TASK_ERR; + break; + } + } + case TaskInfo_t::TASK_RUN: + { + TaskMap[task]->status = TaskInfo_t::TASK_STOP; + // set up for shutdown by creating a global stop message + P_Msg_Hdr_t stop_msg; + stop_msg.destEdgeIndex = 0; // ignore edge index. Unused. + stop_msg.destPin = P_SUP_PIN_SYS_SHORT; // goes to the system pin + stop_msg.messageTag = P_MSG_TAG_STOP; // with a stop message type + DebugPrint("Stopping task %s\n",task.c_str()); + // go through each thread of the task, + //vector threads_for_task = TaskMap[task]->ThreadsForTask(); + P_addr threadAddress; + WALKVECTOR(P_thread*, TaskMap[task]->ThreadsForTask(), R) + { + (*R)->get_hardware_address()->populate_a_software_address(&threadAddress); + uint32_t destDevAddr = Mothership::GetHWAddr(threadAddress); + DebugPrint("Stopping thread %d in task %s\n", destDevAddr, task.c_str()); + stop_msg.destDeviceAddr = DEST_BROADCAST; // issue the stop message to all devices + // then issue the stop packet. Packet size 'future proofed' by computing + // it rather than relying on a static constant (this should compile down + // to a constant anyway) + send(destDevAddr,(p_hdr_size()/(4 << TinselLogWordsPerFlit) + + p_hdr_size()%(4 << TinselLogWordsPerFlit) ? 1 : 0), + &stop_msg, true); + } + TaskMap[task]->status = TaskInfo_t::TASK_END; + // check to see if there are any other active tasks + WALKMAP(string, TaskInfo_t*, TaskMap, tsk) + if ((tsk->second->status == TaskInfo_t::TASK_BARR) || + (tsk->second->status == TaskInfo_t::TASK_RUN)) return 0; + // if not, shut down the Twig thread. For the moment, this also closes + // supervisors. When we have multiple Supervisors running, it is probably + // going to be better to shut each one down here. + if (twig_running) StopTwig(); + return 0; + } + // any unexpected states generate an error. + default: + TaskMap[task]->status = TaskInfo_t::TASK_ERR; + } + Post(813,task,"stopped", + TaskInfo_t::Task_Status.find(TaskMap[task]->status)->second); + return 2; +} + +//------------------------------------------------------------------------------ + +unsigned Mothership::ConfigDistribute(PMsg_p* msg, unsigned comm) +/* receive a broadcast core info block from the NameServer. This responds to + the task /deploy command. Deploying a task has 2 effects: 1) it populates + nameserver entries on this Mothership for the task; 2) it sets up a + TaskInfo_t structure which contains basic info about the task such as what + state it's in, where its files are, and what hardware resources it 'owns'. +*/ +{ + CMsg_p task_info(*msg); // a deploy message has a special message type + string TaskName = msg->Zname(0); // first static string is the name + map::iterator T; + if ((T=TaskMap.find(TaskName)) == TaskMap.end()) // task already exists? + { + DebugPrint("Inserting new task %s from NameDist\n", TaskName.c_str()); + TaskMap[TaskName] = new TaskInfo_t(TaskName); + } + DebugPrint("Task %s will have binaries in directory %s\n", + msg->Zname(0).c_str(),msg->Zname(1).c_str()); + // most of the deploy information is the core mappings + vector> cores; + task_info.Get(cores); + DebugPrint("Task %s has %d cores\n",TaskName.c_str(),cores.size()); + // set up the cores + for (vector>::iterator core = cores.begin(); core != cores.end(); core++) + TaskMap[TaskName]->insertCore(core->first, core->second); + DebugPrint("%d cores inserted into TaskInfo structure for %s\n",cores.size(),TaskName.c_str()); + unsigned err = AddressBook::SUCCESS; + // populate the nameserver entries + if ((err = SBase::ConfigDistribute(msg, comm)) != AddressBook::SUCCESS) + return err; + DebugPrint("About to set binary directory %s for task %s\n", + msg->Zname(1).c_str(),msg->Zname(0).c_str()); + // set the directory for core binaries + return ConfigDir(msg, comm); +} + +//------------------------------------------------------------------------------ + +unsigned Mothership::ConfigRecall(PMsg_p* msg, unsigned comm) +// remove (recall) a core info block from the Mothership. This frees up all +// resources owned by a given task to be reallocatable to another task. +{ + string TaskName = msg->Zname(0); + map::iterator T; + vector taskNames; // list of tasks to recall + if (TaskName.empty()) // no task name => recall all tasks + { + WALKMAP(string, TaskInfo_t*, TaskMap, task) + taskNames.push_back(task->first); + } + else // otherwise remove the named task + { + if ((T=TaskMap.find(TaskName)) == TaskMap.end()) // task exists? + { + Post(607, TaskName); // No. Nothing to do in this Mothership. + return SBase::ConfigRecall(msg, comm); // but remove from base. + } + taskNames.push_back(TaskName); + } + WALKVECTOR(string, taskNames, tName) // recall all tasks in the list + { + switch (TaskMap[*tName]->status) + { + case TaskInfo_t::TASK_IDLE: + case TaskInfo_t::TASK_BOOT: + case TaskInfo_t::TASK_STOP: + case TaskInfo_t::TASK_END: + break; + case TaskInfo_t::TASK_RDY: + { + // A task that is ready to run should not be recalled + // but stopped first. + Post(813, *tName, "recalled", "TASK_RDY"); + return AddressBook::ERR_INVALID_TASK; + } + case TaskInfo_t::TASK_BARR: + case TaskInfo_t::TASK_RUN: + CmStop(*tName); // stop any running tasks before recalling them. + break; + default: + { + // Task in an unknown state - something very wrong has happened! + TaskMap[*tName]->status = TaskInfo_t::TASK_ERR; + Post(812, *tName); + return AddressBook::ERR_INVALID_TASK; + } + } // ends switch (TaskMap[*tName]->status) + system((string("rm -r -f ")+TaskMap[*tName]->BinPath).c_str()); + delete TaskMap[*tName]; // get rid of its TaskInfo object + TaskMap.erase(*tName); // and then remove it from the task map + } + return SBase::ConfigRecall(msg, comm); // remove from base class +} + +//------------------------------------------------------------------------------ + +unsigned Mothership::ConfigDir(PMsg_p *msg, unsigned comm) +// set the path where binaries for a given task are to be found. This can be +// issued as a separate command: task /xpath, although usually task /deploy +// sets this up. +{ + string task = msg->Zname(0); // task name is in the first static field + string dir = msg->Zname(1); // and the binary path is in the second + DebugPrint("Name config dir command received: task %s, directory %s\n", + task.c_str(), dir.c_str()); + map::iterator T; + if ((T=TaskMap.find(task)) == TaskMap.end()) // task already exists? + { + // if not set it up. + DebugPrint("Inserting new task %s from NameTdir\n", task.c_str()); + TaskMap[task] = new TaskInfo_t(task); + } + TaskMap[task]->BinPath = dir; + return SBase::ConfigDir(msg, comm); // inform SBase about the path too. +} + +//------------------------------------------------------------------------------ + +unsigned Mothership::ConfigState(PMsg_p *msg, unsigned comm) +{ + /* Overloaded function to deal with a command Q::NAME,Q::CFG,Q::STATE. + A Mothership should never receive a state-configuration name message. It + should only send them, or update the state internally and call the SBase + method from the internal update. + */ + // get the name to distinguish the type of error + string taskName = msg->Zname(0); + if (taskName.empty()) + Post(710, "ConfigState", int2str(Urank)); // no task name + else if (TaskMap.find(taskName) == TaskMap.end()) + Post(715, taskName, int2str(Urank)); // task not loaded to this Mothership + // find the task and warn the user that they are attempting something illegal + else + { + map::const_iterator st = + TaskInfo_t::Task_Status.find(TaskMap[taskName]->status); + if (st == TaskInfo_t::Task_Status.end()) + { + Post(550, int2str(Urank), msg->Zname(1), "UNKNOWN"); + return AddressBook::ERR_INVALID_STATE; + } + else Post(550, int2str(Urank), msg->Zname(1), st->second); + } + return AddressBook::ERR_NONFATAL; // whatever the case we ignore the message +} + +//------------------------------------------------------------------------------ + +unsigned Mothership::Connect(string svc) +{ + /* Connect deals with what happens when a new MPI universe successfully + connects. This can happen, e.g. if a group of Motherships sit independent + of the Root and its associated processes (perhaps physically separated as + well), and the rest of the Orchestrator connects with the (already running) + Motherships which have set themselves up in their own universe and + published a service name by which they may be accessed. Essentially, we + do everything in the constructor related to universe setup. + */ + unsigned connErr = MPI_SUCCESS; + // set up the connection in the base class + if ((connErr = SBase::Connect(svc)) != MPI_SUCCESS) return connErr; + FnMapx.push_back(new FnMap_t); // add another function table in the derived class + int fIdx=FnMapx.size()-1; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::EXIT )] = &Mothership::OnExit; // overloads CommonBase + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::CFG,Q::DIST )] = &Mothership::OnCfg; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::CFG,Q::TDIR )] = &Mothership::OnCfg; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::CFG,Q::BLD )] = &Mothership::OnCfg; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::CFG,Q::RECL )] = &Mothership::OnCfg; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::CFG,Q::DEL )] = &Mothership::OnCfg; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::CFG,Q::STATE )] = &Mothership::OnCfg; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::CMND,Q::LOAD )] = &Mothership::OnCmnd; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::CMND,Q::RUN )] = &Mothership::OnCmnd; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::CMND,Q::STOP )] = &Mothership::OnCmnd; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::DUMP,Q::TASK,Q::ALL )] = &Mothership::OnDump; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::DUMP,Q::TASK,Q::NM )] = &Mothership::OnDump; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::DUMP,Q::LIST )] = &Mothership::OnDump; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::SEND,Q::DEVI,Q::NM )] = &Mothership::OnSend; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::SEND,Q::DEVI,Q::ID )] = &Mothership::OnSend; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::SEND,Q::DEVI,Q::ALL )] = &Mothership::OnSend; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::SEND,Q::DEVI,Q::NGRP)] = &Mothership::OnSend; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::SEND,Q::DEVI,Q::IGRP)] = &Mothership::OnSend; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::SEND,Q::DEVI,Q::NSUP)] = &Mothership::OnSend; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::SEND,Q::DEVI,Q::ISUP)] = &Mothership::OnSend; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::SEND,Q::SUPV )] = &Mothership::OnSend; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::SEND,Q::EXTN )] = &Mothership::OnSend; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::SEND,Q::DEVT,Q::NM )] = &Mothership::OnSend; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::SEND,Q::DEVT,Q::IN )] = &Mothership::OnSend; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::SEND,Q::DEVT,Q::OUT )] = &Mothership::OnSend; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::SEND,Q::ATTR )] = &Mothership::OnSend; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::SUPR )] = &Mothership::OnSuper; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::SYST,Q::HARD )] = &Mothership::OnSyst; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::SYST,Q::KILL )] = &Mothership::OnSyst; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::SYST,Q::SHOW )] = &Mothership::OnSyst; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::SYST,Q::TOPO )] = &Mothership::OnSyst; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::TINS )] = &Mothership::OnTinsel; + return MPI_SUCCESS; +} + + +//------------------------------------------------------------------------------ + +void Mothership::Dump(FILE * fp, string task) +{ +// Comprehensive dump. Includes both all the task information and the NameServer +// data. +fprintf(fp,"Mothership dump+++++++++++++++++++++++++++++++++++\n"); +fprintf(fp,"Event handler table:\n"); +unsigned cIdx = 0; +WALKVECTOR(FnMap_t*,FnMapx,F) +{ +fprintf(fp,"Function table for comm %d:\n", cIdx++); +fprintf(fp,"Key Method\n"); +WALKMAP(unsigned,pMeth,(**F),i) + fprintf(fp,"%#010x %018lx\n",(*i).first,(uint64_t)&(*i).second); //0x%010x +} +fprintf(fp,"Loaded tasks:\n"); +WALKMAP(string,TaskInfo_t*,TaskMap,TaskI) +{ + fprintf(fp,"Task %s in state %s:\n",TaskI->second->TaskName.c_str(), + TaskInfo_t::Task_Status.find(TaskI->second->status)->second.c_str()); + fprintf(fp,"Reading from binary path %s\n",TaskI->second->BinPath.c_str()); + fprintf(fp,"Task map dump:\n"); + TaskI->second->VirtualBox->Dump(fp); + fprintf(fp,".......................................\n"); +} +if (task.empty()) +{ + fprintf(fp,"SBase summary dump___________________________\n"); + SBase::Dump(fp); + fprintf(fp,"SBase summary dump---------------------------\n"); +} +else if (task == "*") +{ + vector tasks; + unsigned err = AddressBook::SUCCESS; + if ((err = ListTask(tasks))) // get all the tasks + { + Post(710, "Dump", int2str(Urank)); // error: no tasks were found + if (fp != stdout) fclose(fp); + return; // so we don't need to do anything + } + fprintf(fp,"SBase dump of all tasks______________________\n"); + WALKVECTOR(string, tasks, task) SBase::Dump(fp, *task); // dump it all out + fprintf(fp,"SBase dump of all tasks----------------------\n"); +} +else +{ + fprintf(fp,"SBase dump___________________________________\n"); + SBase::Dump(fp,task); + fprintf(fp,"SBase dump-----------------------------------\n"); +} + +fprintf(fp,"Mothership dump-----------------------------------\n"); +fflush(fp); +CommonBase::Dump(fp); +} + +//------------------------------------------------------------------------------ + +// Name Dump responses (from -n and -s switches) + +unsigned Mothership::DumpAll(PMsg_p * msg, unsigned comm) +{ + /* Second static name string is the file name to dump to. The first static + string should be empty. We use the second one so that the first field is + used in a consistent way - for a task name. Filename must be treated as an + absolute literal, because tasks could in + general have different paths, therefore there is no way to infer what + additional path string might be appended to the file name given. + */ + string filename = msg->Zname(1); + FILE* dumpFile; + if (filename.empty()) dumpFile = stdout; // empty filename => dump to console + else dumpFile = fopen(filename.c_str(), "a"); + Dump(dumpFile, "*"); + if (dumpFile != stdout) fclose(dumpFile); + return SUCCESS; // all tasks dump can't fail badly +} + +unsigned Mothership::DumpSummary(PMsg_p * msg, unsigned comm) +{ + // route through base to arrive (virtually) back at the Dump command + // above + return SBase::DumpSummary(msg, comm); +} + +unsigned Mothership::DumpTask(PMsg_p * msg, unsigned comm) +{ + return SBase::DumpTask(msg, comm); +} + +//------------------------------------------------------------------------------ + +long Mothership::LoadBoard(P_board* board) +{ + /* This is the unthreaded version of LoadBoard, the function that does + the data transfer of binaries from files to SDRAM. Currently boards are + loaded 1 core at a time because of hardware limitations. LoadBoard has + 2 separate binaries, a data binary and an executable image, loaded into + different regions as specified in the linker maps generated by the + softswitch builder. Each thread has its own private data space in SDRAM; + each core has a private instruction SRAM, although depending upon + configuration 2 cores - an even and odd-numbered pair, may share + instruction memory (and have to have the same image) + */ + long coresLoaded = 0; + string task; + + // Find the task to load onto this board. + WALKMAP(string, TaskInfo_t*, TaskMap, K) + { + if (K->second->status == TaskInfo_t::TASK_BOOT) task = K->first; + DebugPrint("Found task to load %s\n", task.c_str()); + break; + } + if (!task.empty()) // anything to do? + { + TaskInfo_t* task_map = TaskMap[task]; + P_addr coreAddress, mailboxAddress; + uint32_t mX, mY, core, thread; // Intermediates for HostLink-side + // address components. + DebugPrint("Loading task %s\n", task.c_str()); + // go through all mapped mailboxes, + WALKPDIGRAPHNODES(AddressComponent, P_mailbox*, + unsigned, P_link*, + unsigned, P_port*, board->G, MB) + { + // some output is useful here to keep the operator aware that + // we're doing something. + MB->second.data->get_hardware_address()-> + populate_a_software_address(&mailboxAddress); + Post(562, long2str(coresLoaded), + uint2str(mailboxAddress.A_box), + uint2str(mailboxAddress.A_board)); + // and each mapped core on a mapped mailbox + WALKMAP(AddressComponent, P_core*, + board->G.NodeData(MB)->P_corem, C) + { + // Grab this core's code and data file + string code_f(task_map->BinPath + "/softswitch_code_" + + int2str(task_map->getCore(C->second)) + ".v"); + string data_f(task_map->BinPath + "/softswitch_data_" + + int2str(task_map->getCore(C->second)) + ".v"); + C->second->instructionBinary = new Bin(fopen(code_f.c_str(), + "r")); + C->second->dataBinary = new Bin(fopen(data_f.c_str(), "r")); + + // Populate the P_addr coreAddress from the core's hardware + // address object. Use coreAddress to define mX, mY, core, and + // thread, for compatbilitity with HostLink's loading methods. + C->second->get_hardware_address()-> + populate_a_software_address(&coreAddress); + DebugPrint("Loading core with virtual address Bx:%d, Bd:%d, " + "Mb: %d, Cr:%d\n", + coreAddress.A_box, coreAddress.A_board, + coreAddress.A_mailbox, coreAddress.A_core); + fromAddr(Mothership::GetHWAddr(coreAddress), &mX, &mY, &core, + &thread); + DebugPrint("Loading hardware thread 0x%X at x:%d y:%d c:%d\n", + Mothership::GetHWAddr(coreAddress), mX, mY, core); + + // Load instruction memory, then data memory. + DebugPrint("* loadInstrsOntoCore(codeFilename=%s, meshX=%u, " + "meshY=%u, coreId=%u)\n", + code_f.c_str(), mX, mY, core); + loadInstrsOntoCore(code_f.c_str(), mX, mY, core); + DebugPrint("* loadDataViaCore(dataFilename=%s, meshX=%u, " + "meshY=%u, coreId=%u)\n", + data_f.c_str(), mX, mY, core); + + loadDataViaCore(data_f.c_str(), mX, mY, core); + ++coresLoaded; + } + } + } + DebugPrint("Boot process for board %s finished %ld cores loaded\n", + board->Name().c_str(), coresLoaded); + return coresLoaded; +} + + +//------------------------------------------------------------------------------ + +unsigned Mothership::OnCfg(PMsg_p *msg, unsigned comm) +{ + // top-level instruction dispatch for Q::NAME, Q::CFG command subset + switch(msg->L(2)) + { + case Q::DIST: // DIST and TDIR both set the binary directory; + return ConfigDistribute(msg, comm); // DIST also loads the core map + case Q::TDIR: + return ConfigDir(msg, comm); + case Q::BLD: + return SBase::ConfigBuild(msg, comm); // BLD rebuilts SBase maps + case Q::RECL: + return ConfigRecall(msg, comm); + case Q::DEL: + return SBase::ConfigDelete(msg, comm); + case Q::STATE: // STATE on a mothership will be ignored + return ConfigState(msg, comm); // but issue a warning + default: + Post(700,uint2str(msg->Key()),int2str(Urank)); + return 0; + } +} + +//------------------------------------------------------------------------------ + +unsigned Mothership::OnCmnd(PMsg_p * Z, unsigned cIdx) +// Handler for a task command sent (probably) from the user. These handle the +// basic load, start, and stop operations. +{ + // get the task that the command is going to operate on + string task; + Z->Get(0,task); + unsigned key = Z->Key(); + if (key == PMsg_p::KEY(Q::CMND,Q::LOAD )) + return CmLoad(task); + if (key == PMsg_p::KEY(Q::CMND,Q::RUN )) + return CmRun(task); + if (key == PMsg_p::KEY(Q::CMND,Q::STOP )) + return CmStop(task); + else + Post(510,"Control",int2str(Urank)); + return 0; +} + +//------------------------------------------------------------------------------ + +unsigned Mothership::OnDump(PMsg_p *msg, unsigned comm) +// handlers for various forms of dump +{ + if (msg->L(2) == Q::LIST) return DumpSummary(msg, comm); + if (msg->L(2) != Q::TASK) + { + Post(700,uint2str(msg->Key()),int2str(Urank)); + return 0; + } + switch(msg->L(3)) + { + case Q::ALL: + return DumpAll(msg, comm); + case Q::NM: + return DumpTask(msg, comm); + default: + Post(700,uint2str(msg->Key()),int2str(Urank)); + return 0; + } +} + +//------------------------------------------------------------------------------ + +unsigned Mothership::OnExit(PMsg_p * Z, unsigned cIdx) +// This is what happens when a user command to stop happens +{ + // We are going away. Shut down any active tasks. + WALKMAP(string, TaskInfo_t*, TaskMap, tsk) + { + if ((tsk->second->status == TaskInfo_t::TASK_BOOT) || + (tsk->second->status == TaskInfo_t::TASK_END)) continue; + // automatically run tasks at the barrier and immediately stop them; + // this is the most expedient way of stopping tasks that have never + // actually been started. + if (tsk->second->status == TaskInfo_t::TASK_BARR) CmRun(tsk->first); + if (tsk->second->status == TaskInfo_t::TASK_RUN) CmStop(tsk->first); + } + // stop accepting Tinsel messages + if (twig_running) StopTwig(); + return CommonBase::OnExit(Z,cIdx); // exit through CommonBase handler +} + +//------------------------------------------------------------------------------ + +void Mothership::OnIdle() +// idle processing deals with forwarding packets from the managed Tinsel cores. +{ + // queues may be changing but we can deal with a static snapshot of the + // actual queue because OnIdle will execute periodically. + WALKMAP(uint32_t,deque*,TwigExtMap,D) + { + int NameSrvComm = RootCIdx(); + // int NameSrvComm = NameSCIdx(); + PMsg_p W(Comms[NameSrvComm]); // return packets to be routed via the NameServer's comm + W.Key(Q::TINS); // it'll be a Tinsel packet + W.Tgt(pPmap[NameSrvComm]->U.Root); // temporary: dump external packets to root + //W.Tgt(pPmap[NameSrvComm]->U.NameServer); // directed to the NameServer (or UserIO, when we have it) + W.Src(Urank); // coming from us + /* well, this is awkward: the PMsg_p type has a Put method for vectors + of objects, which is what we want. Our packet should have a vector of + P_Msg_t's. But as things stand, the messages are trapped in a deque + (because we want our twig process to be able to append to the vector of + things to send). Which means copying them out into a vector. Again, + this would be fine if we could copy them directly into a vector in the + PMsg_p, but the interface doesn't allow it - it expects to copy from + vector to vector. So we seem to be stuck with this odd bucket brigade + approach. NOT the most efficient way to move messages. + */ + vector packet; + while (D->second->size()) + { + packet.push_back(D->second->front()); + D->second->pop_front(); + } + W.Put(0,&packet); // stuff the Tinsel messages into the packet + W.Send(); // and away it goes. + } +} + +//------------------------------------------------------------------------------ + + +// OnSend is stubbed for the moment. Will routing like this: though the base +// class, invoke the overloaded virtual functions in the derived Mothership +// class? To be tested when the time comes. +unsigned Mothership::OnSend(PMsg_p *msg, unsigned comm) +{ + return SBase::OnSend(msg, comm); +} + +//------------------------------------------------------------------------------ + +unsigned Mothership::OnSuper(PMsg_p * Z, unsigned cIdx) +// Main hook for user-defined supervisor commands +{ +// set up a return message if we are going to send some reply back +PMsg_p W(Comms[cIdx]); +W.Key(Q::SUPR); +W.Src(Z->Tgt()); +int superReturn = 0; + // Execute. Send a reply if one is expected +if ((superReturn = (*SupervisorCall)(Z,&W)) > 0) +{ + // either to Tinsels, + if (!cIdx && (Z->Tgt() == Urank) && (Z->Src() == Urank)) OnTinsel(&W, 0); + else W.Send(Z->Src()); // or to some external or internal process. +} +if (superReturn < 0) Post(530, int2str(Urank)); +return 0; +} + +//------------------------------------------------------------------------------ + +unsigned Mothership::OnSyst(PMsg_p * Z, unsigned cIdx) +// Handler for system commands sent by the user or operator +{ +unsigned key = Z->Key(); +if (key == PMsg_p::KEY(Q::SYST,Q::HARD )) +{ +vector args; +Z->Get(0,args); +if (SystHW(args)) // A system hardware command executes an external process. +{ + string cmd; + WALKVECTOR(string,args,arg) + { + cmd+=(*arg); + cmd+=(' '); + } + Post(520,int2str(Urank),cmd); + return 0; +} +return 0; +} +if (key == PMsg_p::KEY(Q::SYST,Q::KILL )) +return SystKill(); // Kill brutally shuts us down by exiting immediately +if (key == PMsg_p::KEY(Q::SYST,Q::SHOW )) +return SystShow(); +if (key == PMsg_p::KEY(Q::SYST,Q::TOPO )) +return SystTopo(); +else +Post(524,int2str(Z->Key())); +return 0; +} + +//------------------------------------------------------------------------------ + +unsigned Mothership::OnTinsel(PMsg_p * Z, unsigned cIdx) +// Handler for direct packets to be injected into the network from an external +// source +{ +vector msgs; // messages are packed in Tinsel message format +Z->Get(0, msgs); // We assume they're directly placed in the message +WALKVECTOR(P_Msg_t, msgs, msg) // and they're sent blindly +{ + uint32_t Len = static_cast(msg->header.messageLenBytes); + uint32_t FlitLen = Len >> TinselLogBytesPerFlit; + if (Len << (32-TinselLogBytesPerFlit)) ++FlitLen; + // if we have to we can run OnIdle to empty receive buffers + send(msg->header.destDeviceAddr, FlitLen, &(*msg), true); +} +return 0; +} + +//------------------------------------------------------------------------------ + +unsigned Mothership::OnTinselOut(P_Sup_Msg_t * packet) +/* Deals with what happens when a Tinsel message is received. Generally we + repack the message for delivery to the Supervisor handler and deal with + it there. The Supervisor can do one of 2 things: A) process it itself, + possibly generating another message; B) immediately export it over MPI to + the user Executive or other external process. +*/ +{ +DebugPrint("Processing a command message 0x%x from Tinsel device %d\n", + packet->header.command, packet->header.sourceDeviceAddr); +// handle the kill request from a tinsel core, which generally means an assert failed. +if ((packet->header.command == P_SUP_MSG_KILL)) return SystKill(); +// output messages can simply be posted to the LogServer as an informational message. +if ((packet->header.command == P_SUP_MSG_LOG)) +{ + DebugPrint("Received a handler_log message from device %d\n", + packet->header.sourceDeviceAddr, p_sup_msg_size()); + // Just output the string (this will involve some rubbish at the end where + // arguments would be; to be fixed later). Note that uint8_t*'s have to be + // reinterpret_casted to char*s. + unsigned msg_len = ((packet->header.cmdLenBytes%p_sup_msg_size()) && + (packet->header.seq == packet->header.cmdLenBytes/p_sup_msg_size())) ? + packet->header.cmdLenBytes%p_sup_msg_size() : p_sup_msg_size()-p_sup_hdr_size(); + msg_len -= p_sup_hdr_size(); + Post(601, int2str(packet->header.sourceDeviceAddr), + int2str(packet->header.seq), + string(reinterpret_cast(packet->data), msg_len)); + return 0; +} +DebugPrint("Message from device %d is a Supervisor call. Redirecting\n", + packet->header.sourceDeviceAddr); +PMsg_p W(Comms[0]); // Create a new packet on the local comm +W.Key(Q::SUPR); // it'll be a Supervisor packet +W.Src(Urank); // coming from the us +W.Tgt(Urank); // and directed at us +unsigned last_index = packet->header.cmdLenBytes/p_sup_msg_size() + (packet->header.cmdLenBytes%p_sup_msg_size() ? 1 : 0); +vector pkt_v(packet,&packet[last_index]); // maybe slightly more efficient using the constructor +W.Put(0,&pkt_v); // stuff the Tinsel message into the packet +return OnSuper(&W, 0); +// W.Send(); // away it goes. +// return 0; +} + +//------------------------------------------------------------------------------ + +// Mothership sends. These forward packed groups of messages into the Tinsel +// network. Stubbed for the moment until we have external devices +// to test the send into a Mothership. + +unsigned Mothership::SendAttr(PMsg_p *msg, unsigned comm) +{ + return SBase::SendAttr(msg, comm); +} + +unsigned Mothership::SendDevIAll(PMsg_p *msg, unsigned comm) +{ + return SBase::SendDevIAll(msg, comm); +} + +unsigned Mothership::SendDevIByName(PMsg_p *msg, unsigned comm) +{ + return SBase::SendDevIByName(msg, comm); +} + +unsigned Mothership::SendDevIByID(PMsg_p *msg, unsigned comm) +{ + return SBase::SendDevIByID(msg, comm); +} + +unsigned Mothership::SendDevT(PMsg_p *msg, unsigned comm) +{ + return SBase::SendDevT(msg, comm); +} + +unsigned Mothership::SendExtn(PMsg_p *msg, unsigned comm) +{ + return SBase::SendExtn(msg, comm); +} + +unsigned Mothership::SendSupv(PMsg_p *msg, unsigned comm) +{ + return SBase::SendSupv(msg, comm); +} + +//------------------------------------------------------------------------------ + +void Mothership::StopTwig() +// Cleanly shut down (or try to) the Twig process. This occurs whenever we +// end a task in preparation for shutting down, or exiting. +{ +if (!twig_running) return; +ForwardMsgs = false; // notify the Twig to shut down +pthread_join(Twig_thread,NULL); // wait for it to do so +if (SuperHandle) // then unload its Supervisor +{ + // clean up memory first + int (*SupervisorExit)() = reinterpret_cast(dlsym(SuperHandle, "SupervisorExit")); + if (!SupervisorExit || (*SupervisorExit)() || dlclose(SuperHandle)) + { + Post(534,int2str(Urank)); + SuperHandle = 0; // even if we errored invalidate the Supervisor + } + SupervisorCall = 0; +} +twig_running = false; +} + +//------------------------------------------------------------------------------ + +unsigned Mothership::SystHW(const vector& args) +// Execute some command directly on the Mothership itself. Unlike a Supervisor +// message, which triggers the Supervisor function but doesn't change the +// functionality of the Mothership, this can directly interact with the +// running Mothership to do certain tasks +{ +stringstream cmd(ios_base::out | ios_base::ate); +WALKCVECTOR(string, args, arg) + cmd << *arg; +return system(cmd.str().c_str()); +} + +//------------------------------------------------------------------------------ + +unsigned Mothership::SystKill() +// Kill some process in the local MPI universe. We had better be sure there is +// no traffic destined for this process after such a brutal command! +{ +return 1; +} + +//------------------------------------------------------------------------------ + +unsigned Mothership::SystShow() +// Report the list of processes (active Motherships) on this comm +{ +return 0; +} + +//------------------------------------------------------------------------------ + +unsigned Mothership::SystTopo() +// Report this Mothership's local topology information (boards/cores/connections) +{ +return 0; +} + +//------------------------------------------------------------------------------ + +void* Mothership::Twig(void* par) +// the Twig processing deals with receiving packets from the managed Tinsel cores. +// Ideally, Twig, OnIdle, and OnTinselOut would reside in a separate *process*. +// For the moment we run Twig in a separate *thread* because to run a separate +// process would require identifying the matching Branch's rank, which could be +// done, using a variety of methods, but none of them trivial, and so that's +// 'for later'. +{ + Mothership* parent = static_cast(par); + char* recv_buf = new char[p_msg_size()]; // buffer for one packet at a time + void* p_recv_buf = static_cast(recv_buf); + FILE* OutFile; + char Line[4*P_MSG_MAX_SIZE]; + fpos_t readPos; + fpos_t writePos; + if ( (OutFile = fopen("./DebugOutput.txt", "a+")) ) + { + fsetpos(OutFile, &readPos); + fsetpos(OutFile, &writePos); + } + while (parent->ForwardMsgs) // until told otherwise, + { + // receive all available traffic. Should this be done or only one packet + // and then try again for MPI? We don't expect MPI traffic to be intensive + // and tinsel messsages might be time-critical. + while (parent->canRecv()) + { + DebugPrint("Message received from a Device\n"); + // this is the receive proper + // get the first word, which will be a device address + parent->recv(recv_buf); + uint32_t* device = static_cast(p_recv_buf); + if (!(*device & P_SUP_MASK)) // bound for an external? + { + P_Msg_Hdr_t* m_hdr = static_cast(p_recv_buf); + DebugPrint("Message is bound for external device %d\n", + m_hdr->destDeviceAddr); + // has a message from this particular device been seen before? + // if not set up a new buffer for the device + if (parent->TwigExtMap[m_hdr->destDeviceAddr] == 0) + parent->TwigExtMap[m_hdr->destDeviceAddr] = new deque; + parent->TwigExtMap[m_hdr->destDeviceAddr]->push_back(*(static_cast(p_recv_buf))); + } + else + // message to be consumed on this Mothership, either a system + // message or a Supervisor message + { + P_Sup_Hdr_t* s_hdr = static_cast(p_recv_buf); + s_hdr->sourceDeviceAddr ^= P_SUP_MASK; + + // debug message/watchdog: announces that this thread is + // still operational. + if (s_hdr->command == P_PKT_MSGTYP_ALIVE) + { + DebugPrint("Thread %d is still alive\n", s_hdr->sourceDeviceAddr >> P_THREAD_OS); + } + else + { +//------------------------------------------------------------------------------ + // other messages are routed to the supervisor + DebugPrint("Message is a Supervisor request from device %d\n", + s_hdr->sourceDeviceAddr); + // new device talking? + if (parent->TwigMap[s_hdr->sourceDeviceAddr] == 0) + { + // supervisors have their own internal device buffers + // (pins) for each device + DebugPrint("New device %d reporting to Supervisor\n", + s_hdr->sourceDeviceAddr); + parent->TwigMap[s_hdr->sourceDeviceAddr] = new PinBuf_t; + } + // inactive pin for the device? + if ((*(parent->TwigMap[s_hdr->sourceDeviceAddr]))[s_hdr->destPin] == 0) + { + DebugPrint("New pin %d for device %d reporting to Supervisor\n", + s_hdr->destPin, s_hdr->sourceDeviceAddr); + (*(parent->TwigMap[s_hdr->sourceDeviceAddr]))[s_hdr->destPin] = + new char[MAX_P_SUP_MSG_BYTES](); + } + // convert the buffer into a supervisor message + P_Sup_Msg_t* recvdMsg = + static_cast(static_cast + ((*(parent->TwigMap[s_hdr->sourceDeviceAddr]))[s_hdr->destPin])); + // stuff message into the persistent buffer + memcpy(recvdMsg+s_hdr->seq,recv_buf,p_sup_msg_size()); + if (super_buf_recvd(recvdMsg)) + { + DebugPrint("Entire Supervisor message received of length %d\n", s_hdr->cmdLenBytes); + if (parent->OnTinselOut(recvdMsg)) + parent->Post(530, int2str(parent->Urank)); + super_buf_clr(recvdMsg); + } + } + } + } + // Capture anything happening on the DebugLink - which is text output + // directed at a file. + bool updated = false; + if (OutFile) + { + fsetpos(OutFile, &writePos); + while (parent->pollStdOut(OutFile)) updated = true; + if (updated) + { + DebugPrint("Received a debug output message\n"); + } + fgetpos(OutFile, &writePos); + } + // or possibly only dumped to the local console + else while (parent->pollStdOut()); + // output the debug output buffer, which has to be done immediately + // because we are in a separate thread + if (OutFile && updated) + { + fflush(OutFile); + fsetpos(OutFile, &readPos); + while (!feof(OutFile)) + parent->Post(600, string(fgets(Line, 4*P_MSG_MAX_SIZE, OutFile))); + fgetpos(OutFile, &readPos); + } + } + DebugPrint("Exiting Twig thread\n"); + delete[] recv_buf; + pthread_exit(par); + return par; +} + +//============================================================================== diff --git a/Source/Mothership/Mothership.h b/Source/Mothership/Mothership.h new file mode 100644 index 00000000..29e70efc --- /dev/null +++ b/Source/Mothership/Mothership.h @@ -0,0 +1,136 @@ +#ifndef __MOTHERSHIP__H +#define __MOTHERSHIP__H + +#include +#include "SBase.h" +#include "Debug.h" +#include "PMsg_p.hpp" +#include "Cli.h" +#include "HostLink.h" +#include "pthread.h" +#include "TaskInfo.h" +#include "poets_msg.h" +#include "P_addr.h" + +//============================================================================== +/* A Mothership is a process resident on a Box - a PC directly connected to the + tinsel network using HostLink. Motherships combine several pieces of + functionality, deriving from 2 classes. + From SBase, Motherships handle local name services and receive a subset of + the name server data related to the devices mapped to hardware resources + directly managed by this Mothership. SBase itself inherits from CommonBase + to implement all the Orchestrator MPI infrastructure. SBase and local + overloads of SBase functions also handle the deploying of tasks to + Motherships. A Deploy command sets up a TaskMap entry - a TaskInfo_t object + which contains the pertinent information related to the state, hardware + resources, and mappings of this task. + From HostLink, Motherships support communications between Tinsels and + the rest of the MPI universe, along with the low-level configuration and + command routines. The inheritance relation emphasises that a Mothership *is* + a HostLink: without this interface it is meaningless, whilst a Mothership + on a given Box is by definition the one and only HostLink on the Box. + A separate thread: Twig, extends the HostLink functionality onto the MPI + network, providing communications *from* tinsels *to* MPI and also via a + private channel to Supervisors. A Supervisor is a bit of application-specific + functionality provided in the XML definition and loaded as a dynamic load + library, for operations like data exfiltration, device configuration and + control, etc. In the absence of an application-defined Supervisor, each + application uses the 'Default Supervisor' which blindly routes packets from + Tinsels to the UserIO external interface process. Non-default Supervisors + inherit all the default Supervisor functionality, so external routing + remains unaffected and is handled through Twig. + A final group of commands and functions not inherited from elsewhere are the + Q::CMND family (through OnCmnd) which handle basic control operations for + tasks: load, start, stop; and the Q::SYST family (through OnSyst) which + handle less-common, operator-level commands and queries aimed at providing + a remote management interface for Motherships themselves (OnSyst may be + expanded in future). + */ +//------------------------------------------------------------------------------ +class Mothership : public SBase, public HostLink +{ + +public: + Mothership(int,char **,string); +virtual ~ Mothership(); + +unsigned Connect(string=""); + +// somewhat bodgey function to return the hardware address from the composite; needed because of +// the actual hardware mappings; this is the Orchestrator-side equivalent of toAddr. +static inline unsigned GetHWAddr(P_addr& VAddr) {return (VAddr.A_box << P_BOX_HWOS) | + (VAddr.A_board << P_BOARD_HWOS) | + (VAddr.A_mailbox << P_MAILBOX_HWOS) | + (VAddr.A_core << P_CORE_HWOS) | + (VAddr.A_thread << P_THREAD_HWOS);}; +// static void* LoadBoard(void*); // threaded version of bootloader +static void* Twig (void*); // thread to handle Tinsel messages + +private: +unsigned Boot (string); +unsigned CmLoad (string); +unsigned CmRun (string); +unsigned CmStop (string); +unsigned ConfigDir (PMsg_p *, unsigned); +unsigned ConfigDistribute(PMsg_p *, unsigned); +unsigned ConfigRecall (PMsg_p *, unsigned); +unsigned ConfigState (PMsg_p *, unsigned); +#include "SDecode.cpp" +inline virtual string Dname(){ return typeid(*this).name(); } +void Dump (FILE * = stdout, string = ""); +unsigned DumpAll (PMsg_p *, unsigned); +unsigned DumpSummary (PMsg_p *, unsigned); +unsigned DumpTask (PMsg_p *, unsigned); +long LoadBoard (P_board*); // unthreaded version of bootloader +unsigned OnCfg (PMsg_p *,unsigned); +unsigned OnCmnd (PMsg_p *,unsigned); +unsigned OnDump (PMsg_p *,unsigned); +unsigned OnExit (PMsg_p *,unsigned); +void OnIdle(); +unsigned OnSend (PMsg_p *, unsigned); +unsigned OnSuper (PMsg_p *,unsigned); +unsigned OnSyst (PMsg_p *,unsigned); +unsigned OnTinsel (PMsg_p*, unsigned); +unsigned OnTinselOut (P_Sup_Msg_t *); +void StopTwig(); +// forwarded messages containing POETS packets. The Mothership +// should unpack the packets contained and inject them using +// the HostLink. (This could be placed in a Branch thread). +unsigned SendAttr (PMsg_p *, unsigned); +unsigned SendDevIAll (PMsg_p *, unsigned); +unsigned SendDevIByName (PMsg_p *, unsigned); +unsigned SendDevIByID (PMsg_p *, unsigned); +unsigned SendDevT (PMsg_p *, unsigned); +unsigned SendExtn (PMsg_p *, unsigned); +unsigned SendSupv (PMsg_p *, unsigned); +unsigned SystHW (const vector&); +unsigned SystKill(); +unsigned SystShow(); +unsigned SystTopo(); + +void* SuperHandle; // dynamically loadable supervisor +int (*SupervisorCall)(PMsg_p*, PMsg_p*); // entry point for the Supervisor + +public: +unsigned PAddress; // address of this mothership in POETS-space +bool ForwardMsgs; + +typedef unsigned (Mothership::*pMeth)(PMsg_p *,unsigned); +typedef map FnMap_t; +typedef map PinBuf_t; // type to hold buffers for messages received from devices + +map*> TwigExtMap; // dynamic queues for messages bound for external devices +map TwigMap; // dynamic buffer state for each device from which the Mothership is receiving +map TaskMap; // which tasks are mapped to the machine +vector> BootMap; // which booter is starting which board +vector FnMapx; +pthread_t Twig_thread; // thread for processing tinsel connections +bool twig_running; // flag to show twig thread is active + +static const int NumBoards = NUM_BOARDS_PER_BOX; + +}; + +//============================================================================== + +#endif \ No newline at end of file diff --git a/Source/Mothership/MothershipDummy.cpp b/Source/Mothership/MothershipDummy.cpp new file mode 100644 index 00000000..a4ceed57 --- /dev/null +++ b/Source/Mothership/MothershipDummy.cpp @@ -0,0 +1,846 @@ +#include "MothershipDummy.h" +#include "CMsg_p.h" +#include "Pglobals.h" +#include +#include + + +MothershipDummy::MothershipDummy(int argc,char * argv[], string d) : + SBase(argc,argv,d,string(__FILE__)) +{ + FnMapx.push_back(new FnMap_t); // create a default function table + // Load the message map + + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::CFG,Q::DIST )] = &MothershipDummy::OnCfg; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::CFG,Q::TDIR )] = &MothershipDummy::OnCfg; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::CFG,Q::BLD )] = &MothershipDummy::OnCfg; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::CFG,Q::RECL )] = &MothershipDummy::OnCfg; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::CFG,Q::DEL )] = &MothershipDummy::OnCfg; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::CFG,Q::STATE )] = &MothershipDummy::OnCfg; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::DUMP,Q::TASK,Q::ALL )] = &MothershipDummy::OnDump; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::DUMP,Q::TASK,Q::NM )] = &MothershipDummy::OnDump; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::DUMP,Q::LIST )] = &MothershipDummy::OnDump; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::RPLY,Q::DEVI,Q::NM )] = &MothershipDummy::OnReply; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::RPLY,Q::DEVI,Q::ID )] = &MothershipDummy::OnReply; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::RPLY,Q::DEVI,Q::ALL )] = &MothershipDummy::OnReply; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::RPLY,Q::DEVI,Q::NGRP)] = &MothershipDummy::OnReply; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::RPLY,Q::DEVI,Q::IGRP)] = &MothershipDummy::OnReply; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::RPLY,Q::DEVI,Q::NSUP)] = &MothershipDummy::OnReply; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::RPLY,Q::DEVI,Q::ISUP)] = &MothershipDummy::OnReply; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::RPLY,Q::DEVI,Q::NF )] = &MothershipDummy::OnReply; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::RPLY,Q::DEVI,Q::TNF )] = &MothershipDummy::OnReply; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::RPLY,Q::SUPV )] = &MothershipDummy::OnReply; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::RPLY,Q::SUPV,Q::NF )] = &MothershipDummy::OnReply; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::RPLY,Q::SUPV,Q::TNF )] = &MothershipDummy::OnReply; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::RPLY,Q::EXTN )] = &MothershipDummy::OnReply; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::RPLY,Q::EXTN,Q::NF )] = &MothershipDummy::OnReply; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::RPLY,Q::EXTN,Q::TNF )] = &MothershipDummy::OnReply; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::RPLY,Q::DEVT,Q::NM )] = &MothershipDummy::OnReply; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::RPLY,Q::DEVT,Q::IN )] = &MothershipDummy::OnReply; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::RPLY,Q::DEVT,Q::OUT )] = &MothershipDummy::OnReply; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::RPLY,Q::DEVT,Q::NF )] = &MothershipDummy::OnReply; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::RPLY,Q::DEVT,Q::TNF )] = &MothershipDummy::OnReply; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::RPLY,Q::ATTR )] = &MothershipDummy::OnReply; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::RPLY,Q::ATTR,Q::NF )] = &MothershipDummy::OnReply; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::RPLY,Q::ATTR,Q::TNF )] = &MothershipDummy::OnReply; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::RPLY,Q::LIST )] = &MothershipDummy::OnReply; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::RPLY,Q::LIST,Q::TNF )] = &MothershipDummy::OnReply; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::RPLY,Q::TASK )] = &MothershipDummy::OnReply; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::RPLY,Q::TASK,Q::TNF )] = &MothershipDummy::OnReply; + (*FnMapx[0])[PMsg_p::KEY(Q::CMND,Q::LOAD )] = &MothershipDummy::OnCmnd; + (*FnMapx[0])[PMsg_p::KEY(Q::CMND,Q::RUN )] = &MothershipDummy::OnCmnd; + (*FnMapx[0])[PMsg_p::KEY(Q::CMND,Q::STOP )] = &MothershipDummy::OnCmnd; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::DIST )] = &MothershipDummy::OnName; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::RECL )] = &MothershipDummy::OnName; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::TDIR )] = &MothershipDummy::OnName; + (*FnMapx[0])[PMsg_p::KEY(Q::SUPR )] = &MothershipDummy::OnSuper; + (*FnMapx[0])[PMsg_p::KEY(Q::SYST,Q::HARD )] = &MothershipDummy::OnSyst; + (*FnMapx[0])[PMsg_p::KEY(Q::SYST,Q::KILL )] = &MothershipDummy::OnSyst; + (*FnMapx[0])[PMsg_p::KEY(Q::SYST,Q::SHOW )] = &MothershipDummy::OnSyst; + (*FnMapx[0])[PMsg_p::KEY(Q::SYST,Q::TOPO )] = &MothershipDummy::OnSyst; + + MPISpinner(); // Spin on MPI messages; exit only on DIE +} + +MothershipDummy::~MothershipDummy() +{ + WALKVECTOR(FnMap_t*,FnMapx,F) // WALKVECTOR and WALKMAP are in macros.h (long include chain) + delete *F; // get rid of derived class function tables +} + +unsigned MothershipDummy::Connect(string svc) +{ + unsigned connErr = MPI_SUCCESS; + // set up the connection in the base class + if ((connErr = SBase::Connect(svc)) != MPI_SUCCESS) return connErr; + FnMapx.push_back(new FnMap_t); // add another function table in the derived class + int fIdx=FnMapx.size()-1; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::CFG,Q::DIST )] = &MothershipDummy::OnCfg; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::CFG,Q::TDIR )] = &MothershipDummy::OnCfg; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::CFG,Q::BLD )] = &MothershipDummy::OnCfg; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::CFG,Q::RECL )] = &MothershipDummy::OnCfg; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::CFG,Q::DEL )] = &MothershipDummy::OnCfg; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::CFG,Q::STATE )] = &MothershipDummy::OnCfg; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::DUMP,Q::TASK,Q::ALL )] = &MothershipDummy::OnDump; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::DUMP,Q::TASK,Q::NM )] = &MothershipDummy::OnDump; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::DUMP,Q::LIST )] = &MothershipDummy::OnDump; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::RPLY,Q::DEVI,Q::NM )] = &MothershipDummy::OnReply; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::RPLY,Q::DEVI,Q::ID )] = &MothershipDummy::OnReply; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::RPLY,Q::DEVI,Q::ALL )] = &MothershipDummy::OnReply; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::RPLY,Q::DEVI,Q::NGRP)] = &MothershipDummy::OnReply; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::RPLY,Q::DEVI,Q::IGRP)] = &MothershipDummy::OnReply; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::RPLY,Q::DEVI,Q::NSUP)] = &MothershipDummy::OnReply; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::RPLY,Q::DEVI,Q::ISUP)] = &MothershipDummy::OnReply; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::RPLY,Q::DEVI,Q::NF )] = &MothershipDummy::OnReply; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::RPLY,Q::DEVI,Q::TNF )] = &MothershipDummy::OnReply; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::RPLY,Q::SUPV )] = &MothershipDummy::OnReply; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::RPLY,Q::SUPV,Q::NF )] = &MothershipDummy::OnReply; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::RPLY,Q::SUPV,Q::TNF )] = &MothershipDummy::OnReply; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::RPLY,Q::EXTN )] = &MothershipDummy::OnReply; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::RPLY,Q::EXTN,Q::NF )] = &MothershipDummy::OnReply; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::RPLY,Q::EXTN,Q::TNF )] = &MothershipDummy::OnReply; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::RPLY,Q::DEVT,Q::NM )] = &MothershipDummy::OnReply; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::RPLY,Q::DEVT,Q::IN )] = &MothershipDummy::OnReply; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::RPLY,Q::DEVT,Q::OUT )] = &MothershipDummy::OnReply; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::RPLY,Q::DEVT,Q::NF )] = &MothershipDummy::OnReply; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::RPLY,Q::DEVT,Q::TNF )] = &MothershipDummy::OnReply; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::RPLY,Q::ATTR )] = &MothershipDummy::OnReply; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::RPLY,Q::ATTR,Q::NF )] = &MothershipDummy::OnReply; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::RPLY,Q::ATTR,Q::TNF )] = &MothershipDummy::OnReply; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::RPLY,Q::LIST )] = &MothershipDummy::OnReply; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::RPLY,Q::LIST,Q::TNF )] = &MothershipDummy::OnReply; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::RPLY,Q::TASK )] = &MothershipDummy::OnReply; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::RPLY,Q::TASK,Q::TNF )] = &MothershipDummy::OnReply; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::CMND,Q::LOAD )] = &MothershipDummy::OnCmnd; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::CMND,Q::RUN )] = &MothershipDummy::OnCmnd; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::CMND,Q::STOP )] = &MothershipDummy::OnCmnd; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::DIST )] = &MothershipDummy::OnName; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::RECL )] = &MothershipDummy::OnName; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::TDIR )] = &MothershipDummy::OnName; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::SUPR )] = &MothershipDummy::OnSuper; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::SYST,Q::HARD )] = &MothershipDummy::OnSyst; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::SYST,Q::KILL )] = &MothershipDummy::OnSyst; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::SYST,Q::SHOW )] = &MothershipDummy::OnSyst; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::SYST,Q::TOPO )] = &MothershipDummy::OnSyst; + return MPI_SUCCESS; +} + +unsigned MothershipDummy::CmLoad(string task) +{ + return 0; +} + +unsigned MothershipDummy::CmRun(string task) +{ + return 0; +} + +unsigned MothershipDummy::CmStop(string task) +{ + return 0; +} + +unsigned MothershipDummy::OnCfg(PMsg_p *msg, unsigned comm) +{ + switch(msg->L(2)) + { + case Q::DIST: + return ConfigDistribute(msg, comm); + case Q::TDIR: + return SBase::ConfigDir(msg, comm); + case Q::BLD: + return SBase::ConfigBuild(msg, comm); + case Q::RECL: + return ConfigRecall(msg, comm); + case Q::DEL: + return SBase::ConfigDelete(msg, comm); + case Q::STATE: + return ConfigState(msg, comm); + default: + Post(700,uint2str(msg->Key()),int2str(Urank)); + return 0; + } +} + +unsigned MothershipDummy::OnCmnd(PMsg_p *msg, unsigned comm) +{ + // co-opt the task /init (which sends LOAD) command to trigger some + // test queries. + if (msg->L(1) == Q::LOAD) + { + PMsg_p dummyState(Comms[0]); + string taskName; + msg->Get(0,taskName); + if (taskName.empty()) + { + Post(510,"Load",int2str(Urank)); + return 0; + } + dummyState.Zname(0,taskName); + dummyState.Zname(1,"Init"); + return ConfigState(&dummyState, 0); + } + // any other command is silently ignored. + return 0; +} + +unsigned MothershipDummy::OnDump(PMsg_p *msg, unsigned comm) +{ + if (msg->L(2) == Q::LIST) return DumpSummary(msg, comm); + if (msg->L(2) != Q::TASK) + { + Post(700,uint2str(msg->Key()),int2str(Urank)); + return 0; + } + switch(msg->L(3)) + { + case Q::ALL: + return DumpAll(msg, comm); + case Q::NM: + return DumpTask(msg, comm); + default: + Post(700,uint2str(msg->Key()),int2str(Urank)); + return 0; + } +} + +unsigned MothershipDummy::OnName(PMsg_p *msg, unsigned comm) +{ + return 0; +} + +unsigned MothershipDummy::OnReply(PMsg_p *msg, unsigned comm) +{ + if ((msg->L(3) == Q::NF) || (msg->L(3) == Q::TNF)) return ReplyNotFound(msg,comm); + switch(msg->L(2)) + { + case Q::DEVI: + switch(msg->L(3)) + { + case Q::NM: + case Q::ID: + return ReplyDevice(msg,comm); + case Q::NSUP: + case Q::ISUP: + return ReplyDevSuper(msg,comm); + case Q::ALL: + case Q::NGRP: + case Q::IGRP: + return ReplyDevices(msg,comm); + } + break; + case Q::SUPV: + return ReplySupers(msg,comm); + case Q::EXTN: + return ReplyDevices(msg,comm); + case Q::DEVT: + return ReplyDevTypes(msg,comm); + case Q::ATTR: + return ReplyAttrs(msg,comm); + case Q::LIST: + return ReplyList(msg,comm); + case Q::TASK: + return ReplyTask(msg,comm); + } + Post(700,uint2str(msg->Key()),int2str(Urank)); + return ERR_NONFATAL; +} + +unsigned MothershipDummy::OnSuper(PMsg_p *msg, unsigned comm) +{ + return 0; +} + +unsigned MothershipDummy::OnSyst(PMsg_p *msg, unsigned comm) +{ + return 0; +} + +unsigned MothershipDummy::SystHW(const vector& args) +{ + return 0; +} + +unsigned MothershipDummy::SystKill() +{ + return 0; +} + +unsigned MothershipDummy::SystShow() +{ + return 0; +} + +unsigned MothershipDummy::SystTopo() +{ + return 0; +} + +void MothershipDummy::Dump(FILE *fp, string task) +{ + if (task.empty()) + { + fprintf(fp,"MothershipDummy summary dump+++++++++++++++++++++++++++\n"); + SBase::Dump(fp); + fprintf(fp,"MothershipDummy summary dump---------------------------\n"); + } + else + { + fprintf(fp,"MothershipDummy dump+++++++++++++++++++++++++++++++++++\n"); + SBase::Dump(fp,task); + fprintf(fp,"Mothership dump-----------------------------------\n"); + } + CommonBase::Dump(fp); +} + +unsigned MothershipDummy::ConfigDistribute(PMsg_p *msg, unsigned comm) +{ + unsigned err = SUCCESS; + if ((err = SBase::ConfigDir(msg,comm))) return err; + if ((err = SBase::ConfigDistribute(msg,comm))) return err; + string taskName = msg->Zname(0); + // temporarily we dump everything out to a file --------------------------------------------- + FILE *dumpFile = fopen("MothershipDummy_dump.txt","w"); + fprintf(dumpFile, "------------------------------Dummy Mothership Dump-----------------------------\n"); + int aCount = 0; + int* mAddr = msg->Get(0,aCount); + // SymAddr_t* mAddr = msg->Get(0,aCount); + if (!mAddr) Post(730,"NAME::CFG::DIST",int2str(Urank),"Mothership address"); + fprintf(dumpFile, "Mothership Address: %d\n", mAddr ? *mAddr : 0); + CMsg_p distMsg(*msg); + distMsg.Dump(dumpFile); + Dump(dumpFile,taskName); + fclose(dumpFile); + //-------------------------------------------------------------------------------------------- + PMsg_p stateSet(Comms[0]); // set up state directly + stateSet.Src(Urank); + stateSet.Tgt(Urank); + stateSet.Zname(0, taskName); + stateSet.Zname(1, "Deployed"); // by creating a message to ourselves + ConfigState(&stateSet, 0); // and calling the state-transition function with it + return err; +} + +unsigned MothershipDummy::ConfigRecall(PMsg_p *msg, unsigned comm) +{ + string taskName = msg->Zname(0); + vector tasks; + unsigned err = SUCCESS; + if (taskName.empty()) + { + if ((err = ListTask(tasks))) + { + Post(721,"gett","list",int2str(Urank)); + return err; + } + } + else tasks.push_back(taskName); + vector::iterator task; + for (task = tasks.begin(); task != tasks.end(); task++) + { + if ((err = DelTask(*task))) + { + Post(721,"delet",*task,int2str(Urank)); + err = ERR_NONFATAL; + } + } + return err; +} + +// for testing purposes we will coopt ConfigState on Dummy motherships +// to query the NameServer for data. +unsigned MothershipDummy::ConfigState(PMsg_p *msg, unsigned comm) +{ + string taskName = msg->Zname(0); + TaskData_t task; + unsigned err = GetTask(taskName, task); + if (err) return SUCCESS; // No such task yet. We can ignore. + if (task.State == Deployed) + { + string nextState = msg->Zname(1); + const vector* deployedDevs; + if (nextState == "Init") // Init state change triggers queries + { + err = GetDevices(taskName, deployedDevs); // but only if devices are loaded + if (!err) + { + // pick a subsample of 10 devices to run single queries on (or, at least, + // as many devices as we have if it's < 10) + unsigned devStep = deployedDevs->size()/10 == 0 ? 1 : deployedDevs->size()/10; + PMsg_p qry; + // find the NameServer's rank and comm + for (unsigned comm = 0; comm < pPmap.size(); comm++) + { + if (pPmap[comm]->U.NameServer != Q::NAP) // use the first match + { + qry.comm = Comms[comm]; + qry.Src(Urank); + qry.Tgt(pPmap[comm]->U.NameServer); + break; + } + } + if (comm == pPmap.size()) // no NameServer found. A severe error. + { + Post(711); + return ERR_INVALID_STATE; + } + // now send some dummy queries. Get a task list first. + qry.Key(Q::NAME,Q::QRY,Q::LIST); + // be sure to set up tags for each query sent so it can be matched + int tag = 1; // stay out of standard Orchestrator-land (this is an MPI tag) + if (qryMap.size()) tag = qryMap.rbegin()->first + 1; + if (tag > MPI_TAG_UB) tag = 1; + qry.Tag(tag); + qryMap[tag]=qry.Key(); + qry.Send(); + qry.L(2,Q::TASK); // now ask about the specific task we are on + qry.Zname(0,taskName); + qry.Tag(++tag); // these 3 lines are standard boilerplate to send + qryMap[tag]=qry.Key(); // a query and update the tag map. + qry.Send(); + qry.L(2,Q::DEVI); + // try all the device queries for a sample of devices (max 10) + for (unsigned dev = 0; dev < deployedDevs->size(); dev+=devStep) + { + qry.L(3,Q::NM); // by name + qry.Zname(1,(*deployedDevs)[dev].Name); + qry.Tag(++tag); + qryMap[tag]=qry.Key(); + qry.Send(); + qry.L(3,Q::NGRP); // in same group as named device + qry.Tag(++tag); + qryMap[tag]=qry.Key(); + qry.Send(); + qry.L(3,Q::NSUP); // supervisor of named device + qry.Tag(++tag); + qryMap[tag]=qry.Key(); + qry.Send(); + qry.L(3,Q::ID); // by ID + qry.Zname(1,""); + // another case where we have to copy into a temporary because + // the device records are const-qualified in the return from + // GetDevices. + SymAddr_t devAddr = (*deployedDevs)[dev].Address; + qry.Put(0,&devAddr); + qry.Tag(++tag); + qryMap[tag]=qry.Key(); + qry.Send(); + qry.L(3,Q::IGRP); // in same group as device ID + qry.Tag(++tag); + qryMap[tag]=qry.Key(); + qry.Send(); + qry.L(3,Q::ISUP); // supervisor of device ID + qry.Tag(++tag); + qryMap[tag]=qry.Key(); + qry.Send(); + } + qry.L(3,Q::ALL); // all devices + qry.Tag(++tag); + qryMap[tag]=qry.Key(); + qry.Send(); + qry.L(3,0); + qry.L(2,Q::SUPV); // all supervisors + qry.Tag(++tag); + qryMap[tag]=qry.Key(); + qry.Send(); + qry.L(2,Q::EXTN); // all externals (should be none for now) + qry.Tag(++tag); + qryMap[tag]=qry.Key(); + qry.Send(); + if (task.AttributeTypes.size()) + { + qry.L(2,Q::ATTR); // matching attributes, if any exist + qry.PutX(2,&(task.AttributeTypes)); // just use all attributes + qry.Tag(++tag); + qryMap[tag]=qry.Key(); + qry.Send(); + } + if (!task.DeviceTypes.size()) // device types *should* exist, + { + // but if there are none send us a message and exit. + Post(706,taskName,int2str(Urank)); + return ERR_NONFATAL; + } + qry.L(2,Q::DEVT); // of same device type + qry.L(3,Q::NM); + qry.Zname(2,task.DeviceTypes.front().Name); + qry.Tag(++tag); + qryMap[tag]=qry.Key(); + qry.Send(); + qry.L(3,Q::IN); // sharing inputs with device type + qry.Tag(++tag); + qryMap[tag]=qry.Key(); + qry.Send(); + qry.L(3,Q::OUT); // sharing outputs with device type + qry.Tag(++tag); + qryMap[tag]=qry.Key(); + qry.Send(); + } // if (!err) + } // if (nextState == "Init") + } // if (task.State == Deployed) + return SBase::ConfigState(msg,comm); // (always) update state through base class +} + +unsigned MothershipDummy::DumpAll(PMsg_p * msg, unsigned comm) +{ + return SBase::DumpAll(msg, comm); +} + +unsigned MothershipDummy::DumpSummary(PMsg_p * msg, unsigned comm) +{ + return SBase::DumpSummary(msg, comm); +} + +unsigned MothershipDummy::DumpTask(PMsg_p * msg, unsigned comm) +{ + return SBase::DumpTask(msg, comm); +} + +void MothershipDummy::MapValue2Key(unsigned value, vector* key) +{ + uint32_t val = static_cast(value); + void* valPtr = static_cast(&val); + // another reason to hate endianness: although the Key method in Msg_p + // computes the key by successive left-shifting, the value as an + // unsigned is stored little-endian. Which means that left shift + // really means byte-wise right shift, from the POV of the underlying + // representation in memory. So it's necessary to count back through + // the unsigned value. + for (int b=sizeof(uint32_t)-1; b >= 0; b--) + key->push_back(static_cast(valPtr)[b]); +} + +string MothershipDummy::QueryType(unsigned keyVal) +{ + vector key; + MapValue2Key(keyVal, &key); + if ((key[0] != Q::NAME) || ((key[1] != Q::QRY) && (key[1] != Q::RPLY))) return "Not a Query"; + switch (key[2]) + { + case Q::DEVI: + switch (key[3]) + { + case Q::NM: + return "Device By Name"; + case Q::ID: + return "Device By ID"; + case Q::ALL: + return "All Devices"; + case Q::NGRP: + return "Devices in Same Group as Named Device"; + case Q::IGRP: + return "Devices in Same Group as Device ID"; + case Q::NSUP: + return "Supervisor of Named Device"; + case Q::ISUP: + return "Supervisor of Device ID"; + default: + return "Unrecognised Device Query"; + } + case Q::SUPV: + return "Supervisors"; + case Q::EXTN: + return "External Devices"; + case Q::DEVT: + switch (key[3]) + { + case Q::NM: + return "Devices of Type"; + case Q::IN: + return "Devices With Input Message Type"; + case Q::OUT: + return "Devices With Output Message Type"; + default: + return "Unrecognised Type Query"; + } + case Q::ATTR: + return "Devices by Attribute"; + case Q::LIST: + return "Task List"; + case Q::TASK: + return "Task Info"; + default: + return "Unrecognised Query"; + } +} + +unsigned MothershipDummy::ReplyAttrs(PMsg_p *msg, unsigned comm) +{ + FILE *dumpFile = fopen("MothershipDummy_dump.txt","a"); + int qryTag = msg->Tag(); + map::iterator qryIt = qryMap.find(qryTag); + if (qryIt == qryMap.end()) + { + fprintf(dumpFile, "ERROR: reply to unknown query %x\n", qryTag); + return ERR_NONFATAL; + } + vector devAddrs; + vector devNames; + vector devAttrs; + msg->Get(0, devAddrs); + msg->GetX(1, devNames); + msg->GetX(2, devAttrs); + unsigned numDevs = devAddrs.size(); + unsigned numNames = devNames.size(); + if (numDevs != numNames) + { + Post(716, int2str(Urank), uint2str(numNames), uint2str(numDevs), "addresse"); + numDevs = (numDevs > numNames) ? numNames : numDevs; + } + fprintf(dumpFile, "--------------------- Devices By Attribute -------------------------------------\n"); + for (unsigned dev = 0; dev < numDevs; dev++) + fprintf(dumpFile, "Device %s: address 0x%lx\n", devNames[dev].c_str(), devAddrs[dev]); + fprintf(dumpFile, "\n"); + fprintf(dumpFile, "Matched attributes:\n"); + for (unsigned attr = 0; attr < devAttrs.size(); attr++) + fprintf(dumpFile, "%s\n", devAttrs[attr].c_str()); + fprintf(dumpFile, "________________________________________________________________________________\n"); + fclose(dumpFile); + qryMap.erase(qryTag); // Reply handled; remove the query request + return SUCCESS; +} + +unsigned MothershipDummy::ReplyDevice(PMsg_p *msg, unsigned comm) +{ + FILE *dumpFile = fopen("MothershipDummy_dump.txt","a"); + int qryTag = msg->Tag(); + map::iterator qryIt = qryMap.find(qryTag); + if (qryIt == qryMap.end()) + { + fprintf(dumpFile, "ERROR: reply to unknown query %x\n", qryTag); + return ERR_NONFATAL; + } + int numDevs; + SymAddr_t* devAddr = msg->Get(0, numDevs); + if (!devAddr) + { + fprintf(dumpFile, "ERROR: device address missing for device %s query\n", msg->Zname(1).c_str()); + return ERR_NONFATAL; + } + string qryType = QueryType(msg->Key()); + fprintf(dumpFile, "--------------------- %s ----------------------------\n", qryType.c_str()); + fprintf(dumpFile, "Device %s: address 0x%lx\n", msg->Zname(1).c_str(), *devAddr); + fprintf(dumpFile, "________________________________________________________________________________\n"); + fclose(dumpFile); + qryMap.erase(qryTag); // Reply handled; remove the query request + return SUCCESS; +} + +unsigned MothershipDummy::ReplyDevices(PMsg_p *msg, unsigned comm) +{ + FILE *dumpFile = fopen("MothershipDummy_dump.txt","a"); + int qryTag = msg->Tag(); + map::iterator qryIt = qryMap.find(qryTag); + if (qryIt == qryMap.end()) + { + fprintf(dumpFile, "ERROR: reply to unknown query %x\n", qryTag); + return ERR_NONFATAL; + } + vector devAddrs; + vector devNames; + msg->Get(0, devAddrs); + msg->GetX(1, devNames); + unsigned numDevs = devAddrs.size(); + unsigned numNames = devNames.size(); + if (numDevs != numNames) + { + Post(716, int2str(Urank), uint2str(numNames), uint2str(numDevs), "addresse"); + numDevs = (numDevs > numNames) ? numNames : numDevs; + } + string devName = msg->Zname(1); + if (msg->L(2) == Q::EXTN) devName = "{Externals}"; + else if (msg->L(3) == Q::ALL) devName = "{All}"; + string qryType = QueryType(msg->Key()); + fprintf(dumpFile, "----------- %s ----------------------------\n", qryType.c_str()); + fprintf(dumpFile, "Matching device %s\n", devName.c_str()); + for (unsigned dev = 0; dev < numDevs; dev++) + fprintf(dumpFile, "Device %s: address 0x%lx\n", devNames[dev].c_str(), devAddrs[dev]); + fprintf(dumpFile, "________________________________________________________________________________\n"); + fclose(dumpFile); + qryMap.erase(qryTag); // Reply handled; remove the query request + return SUCCESS; +} + +unsigned MothershipDummy::ReplyDevSuper(PMsg_p *msg, unsigned comm) +{ + FILE *dumpFile = fopen("MothershipDummy_dump.txt","a"); + int qryTag = msg->Tag(); + map::iterator qryIt = qryMap.find(qryTag); + if (qryIt == qryMap.end()) + { + fprintf(dumpFile, "ERROR: reply to unknown query %x\n", qryTag); + return ERR_NONFATAL; + } + int numDevs; + SymAddr_t* devAddr = msg->Get(0, numDevs); + SymAddr_t* superAddr = msg->Get(1, numDevs); + if (!devAddr) + { + fprintf(dumpFile, "ERROR: device address missing for device %s supervisor query\n", msg->Zname(1).c_str()); + return ERR_NONFATAL; + } + if (!superAddr) + { + fprintf(dumpFile, "ERROR: device address missing for device %s supervisor query\n", msg->Zname(1).c_str()); + return ERR_NONFATAL; + } + string qryType = QueryType(msg->Key()); + fprintf(dumpFile, "--------------------- %s ----------------------------\n", qryType.c_str()); + fprintf(dumpFile, "Device %s: address 0x%lx, supervisor 0x%lx\n", msg->Zname(1).c_str(),*devAddr,*superAddr); + fprintf(dumpFile, "________________________________________________________________________________\n"); + fclose(dumpFile); + qryMap.erase(qryTag); // Reply handled; remove the query request + return SUCCESS; +} + +unsigned MothershipDummy::ReplyDevTypes(PMsg_p *msg, unsigned comm) +{ + FILE *dumpFile = fopen("MothershipDummy_dump.txt","a"); + int qryTag = msg->Tag(); + map::iterator qryIt = qryMap.find(qryTag); + if (qryIt == qryMap.end()) + { + fprintf(dumpFile, "ERROR: reply to unknown query %x\n", qryTag); + return ERR_NONFATAL; + } + vector devAddrs; + vector devNames; + msg->Get(0, devAddrs); + msg->GetX(1, devNames); + unsigned numDevs = devAddrs.size(); + unsigned numNames = devNames.size(); + if (numDevs != numNames) + { + Post(716, int2str(Urank), uint2str(numNames), uint2str(numDevs), "addresse"); + numDevs = (numDevs > numNames) ? numNames : numDevs; + } + string matchName = msg->Zname(2); + string matchType = "device"; + if (msg->L(3) == Q::IN) matchType = "input message"; + if (msg->L(3) == Q::OUT) matchType = "output message"; + string qryType = QueryType(msg->Key()); + fprintf(dumpFile, "----------- %s ----------------------------\n", qryType.c_str()); + fprintf(dumpFile, "Matching %s type %s\n", matchType.c_str(), matchName.c_str()); + for (unsigned dev = 0; dev < numDevs; dev++) + fprintf(dumpFile, "Device %s: address 0x%lx\n", devNames[dev].c_str(), devAddrs[dev]); + fprintf(dumpFile, "________________________________________________________________________________\n"); + fclose(dumpFile); + qryMap.erase(qryTag); // Reply handled; remove the query request + return SUCCESS; +} + +unsigned MothershipDummy::ReplyList(PMsg_p *msg, unsigned comm) +{ + FILE *dumpFile = fopen("MothershipDummy_dump.txt","a"); + int qryTag = msg->Tag(); + map::iterator qryIt = qryMap.find(qryTag); + if (qryIt == qryMap.end()) + { + fprintf(dumpFile, "ERROR: reply to unknown query %x\n", qryTag); + return ERR_NONFATAL; + } + vector tasks; + msg->GetX(1,tasks); + fprintf(dumpFile, "--------------------- Task List -------------------------------------\n"); + WALKVECTOR(string, tasks, task) + fprintf(dumpFile, "%s\n", task->c_str()); + fprintf(dumpFile, "________________________________________________________________________________\n"); + fclose(dumpFile); + qryMap.erase(qryTag); // Reply handled; remove the query request + return SUCCESS; +} + +unsigned MothershipDummy::ReplyNotFound(PMsg_p *msg, unsigned comm) +{ + FILE *dumpFile = fopen("MothershipDummy_dump.txt","a"); + int qryTag = msg->Tag(); + map::iterator qryIt = qryMap.find(qryTag); + if (qryIt == qryMap.end()) + { + fprintf(dumpFile, "ERROR: reply to unknown query %x\n", qryTag); + return ERR_NONFATAL; + } + fprintf(dumpFile,"Reply to query %s on task %s: %s\n", QueryType(qryIt->second).c_str(), + msg->Zname(0).c_str(), msg->L(3) == Q::NF ? "Device Not Found" : "Task Not Found"); + fclose(dumpFile); + qryMap.erase(qryTag); // Reply handled; remove the query request + return SUCCESS; +} + +unsigned MothershipDummy::ReplySupers(PMsg_p *msg, unsigned comm) +{ + FILE *dumpFile = fopen("MothershipDummy_dump.txt","a"); + int qryTag = msg->Tag(); + map::iterator qryIt = qryMap.find(qryTag); + if (qryIt == qryMap.end()) + { + fprintf(dumpFile, "ERROR: reply to unknown query %x\n", qryTag); + return ERR_NONFATAL; + } + vector supAddrs; + vector supNames; + vector supRanks; + msg->Get(0, supAddrs); + msg->GetX(1, supNames); + msg->Get(2, supRanks); + unsigned numDevs = supAddrs.size(); + unsigned numNames = supNames.size(); + unsigned numRanks = supRanks.size(); + if ((numDevs != numNames) || (numDevs != numRanks)) + { + Post(737, int2str(Urank), uint2str(numNames), uint2str(numDevs), uint2str(numRanks)); + numDevs = (numDevs > numNames) ? numNames : numDevs; + numDevs = (numDevs > numRanks) ? numRanks : numDevs; + } + fprintf(dumpFile, "--------------------- Supervisors -------------------------------------\n"); + for (unsigned dev = 0; dev < numDevs; dev++) + fprintf(dumpFile, "Supervisor %s: address 0x%lx, rank %lu\n",supNames[dev].c_str(),supAddrs[dev],supRanks[dev]); + fprintf(dumpFile, "________________________________________________________________________________\n"); + fclose(dumpFile); + qryMap.erase(qryTag); // Reply handled; remove the query request + return SUCCESS; +} + +unsigned MothershipDummy::ReplyTask(PMsg_p *msg, unsigned comm) +{ + FILE *dumpFile = fopen("MothershipDummy_dump.txt","a"); + int qryTag = msg->Tag(); + map::iterator qryIt = qryMap.find(qryTag); + if (qryIt == qryMap.end()) + { + fprintf(dumpFile, "ERROR: reply to unknown query %x\n", qryTag); + return ERR_NONFATAL; + } + string task = msg->Zname(0); + string state = msg->Zname(1); + string tdir = msg->Zname(2); + string fname = msg->Zname(3); + vector msgTypes; + vector attrTypes; + vector counts; + msg->GetX(0,msgTypes); + msg->GetX(1, attrTypes); + msg->Get(2, counts); + if (counts.size() != 3) + { + fprintf(dumpFile, "ERROR: count mismatch for task info: expected 3 counts, received %lu\n", counts.size()); + return ERR_NONFATAL; + } + fprintf(dumpFile, "-------------------------------- Task Info -------------------------------------\n"); + fprintf(dumpFile, "Task: %s\n", task.c_str()); + fprintf(dumpFile, "In state: %s\n", state.c_str()); + fprintf(dumpFile, "Task directory: %s\n", tdir.c_str()); + fprintf(dumpFile, "Source file name: %s\n", fname.c_str()); + fprintf(dumpFile, "---- Message types ----\n"); + WALKVECTOR(string, msgTypes, msgTyp) + fprintf(dumpFile, "%s\n", msgTyp->c_str()); + fprintf(dumpFile, "_______________________\n"); + WALKVECTOR(string, attrTypes, attrTyp) + fprintf(dumpFile, "%s\n", attrTyp->c_str()); + fprintf(dumpFile, "Number of devices: %lu, externals: %lu, supervisors %lu\n",counts[0],counts[1],counts[2]); + fprintf(dumpFile, "________________________________________________________________________________\n"); + fclose(dumpFile); + qryMap.erase(qryTag); // Reply handled; remove the query request + return SUCCESS; +} + diff --git a/Source/Mothership/MothershipDummy.h b/Source/Mothership/MothershipDummy.h new file mode 100644 index 00000000..d9212a50 --- /dev/null +++ b/Source/Mothership/MothershipDummy.h @@ -0,0 +1,63 @@ +#ifndef __MothershipDummy_H__ +#define __MothershipDummy_H__ + +#include "SBase.h" +#include "PMsg_p.hpp" +#include + +class MothershipDummy : public SBase +{ + public: + MothershipDummy(int,char **,string); + virtual ~MothershipDummy(); + + unsigned Connect(string=""); + + private: + + unsigned CmLoad(string); + unsigned CmRun(string); + unsigned CmStop(string); + unsigned OnCfg(PMsg_p *,unsigned); + unsigned OnCmnd(PMsg_p *,unsigned); + unsigned OnDump(PMsg_p *,unsigned); + unsigned OnName(PMsg_p *,unsigned); + unsigned OnReply(PMsg_p *, unsigned); + unsigned OnSuper(PMsg_p *,unsigned); + unsigned OnSyst(PMsg_p *,unsigned); + unsigned SystHW(const vector&); + unsigned SystKill(); + unsigned SystShow(); + unsigned SystTopo(); + + #include "SDecode.cpp" + void Dump(FILE * = stdout, string = ""); + + // unsigned ConfigDir(PMsg_p *, unsigned); + unsigned ConfigDistribute(PMsg_p *, unsigned); + unsigned ConfigRecall(PMsg_p *, unsigned); + unsigned ConfigState(PMsg_p *, unsigned); + unsigned DumpAll(PMsg_p *, unsigned); + unsigned DumpSummary(PMsg_p *, unsigned); + unsigned DumpTask(PMsg_p *, unsigned); + unsigned ReplyAttrs(PMsg_p*, unsigned); + unsigned ReplyDevice(PMsg_p*, unsigned); + unsigned ReplyDevices(PMsg_p*, unsigned); + unsigned ReplyDevSuper(PMsg_p*, unsigned); + unsigned ReplyDevTypes(PMsg_p*, unsigned); + unsigned ReplyList(PMsg_p*, unsigned); + unsigned ReplyNotFound(PMsg_p*, unsigned); + unsigned ReplySupers(PMsg_p*, unsigned); + unsigned ReplyTask(PMsg_p*, unsigned); + + static void MapValue2Key(unsigned value, vector* key); + static string QueryType(unsigned); + + typedef unsigned (MothershipDummy::*pMeth)(PMsg_p *, unsigned); + typedef map FnMap_t; + vector FnMapx; + + map qryMap; +}; + +#endif diff --git a/Source/Mothership/MothershipDummyMain.cpp b/Source/Mothership/MothershipDummyMain.cpp new file mode 100644 index 00000000..b2eb31e5 --- /dev/null +++ b/Source/Mothership/MothershipDummyMain.cpp @@ -0,0 +1,12 @@ +#include "MothershipDummy.h" +#include "Pglobals.h" +#include + +int main(int argc, char * argv[]) +{ + MothershipDummy* mothership = new MothershipDummy(argc, argv, string(csMOTHERSHIPproc)); + delete mothership; + printf("%s Main closing down\n", csMOTHERSHIPproc); + fflush(stdout); + return 0; +} diff --git a/Source/Mothership/MothershipMain.cpp b/Source/Mothership/MothershipMain.cpp index 3a417a00..6a3b078b 100644 --- a/Source/Mothership/MothershipMain.cpp +++ b/Source/Mothership/MothershipMain.cpp @@ -1,10 +1,13 @@ -#include "TMoth.h" +#include "Mothership.h" +//#include "TMoth.h" #include "Pglobals.h" #include int main(int argc, char * argv[]) { - TMoth* mothership = new TMoth(argc, argv, string(csMOTHERSHIPproc)); + Mothership* mothership = new Mothership(argc, argv, string(csMOTHERSHIPproc)); + printf("Deleting mothership"); + fflush(stdout); delete mothership; printf("%s Main closing down\n", csMOTHERSHIPproc); fflush(stdout); diff --git a/Source/Mothership/TMoth.cpp b/Source/Mothership/TMoth.cpp deleted file mode 100644 index b4365432..00000000 --- a/Source/Mothership/TMoth.cpp +++ /dev/null @@ -1,974 +0,0 @@ -//------------------------------------------------------------------------------ - -#include "TMoth.h" -#include -#include -#include -#include "Pglobals.h" -#include "CMsg_p.h" -#include "flat.h" -#include "string.h" -#include "limits.h" - -const int TMoth::NumBoards; - -//============================================================================== - -TMoth::TMoth(int argc,char * argv[],string d) : -CommonBase(argc,argv,d,string(__FILE__)), HostLink() -{ - - - FnMapx.push_back(new FnMap_t); // create a new event map in the derived class - - // Load the incoming event map - (*FnMapx[0])[PMsg_p::KEY(Q::EXIT )] = &TMoth::OnExit; // overloads CommonBase - (*FnMapx[0])[PMsg_p::KEY(Q::CMND,Q::LOAD )] = &TMoth::OnCmnd; - (*FnMapx[0])[PMsg_p::KEY(Q::CMND,Q::RUN )] = &TMoth::OnCmnd; - (*FnMapx[0])[PMsg_p::KEY(Q::CMND,Q::STOP )] = &TMoth::OnCmnd; - (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::DIST )] = &TMoth::OnName; - (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::RECL )] = &TMoth::OnName; - (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::TDIR )] = &TMoth::OnName; - (*FnMapx[0])[PMsg_p::KEY(Q::SUPR )] = &TMoth::OnSuper; - (*FnMapx[0])[PMsg_p::KEY(Q::SYST,Q::HARD )] = &TMoth::OnSyst; - (*FnMapx[0])[PMsg_p::KEY(Q::SYST,Q::KILL )] = &TMoth::OnSyst; - (*FnMapx[0])[PMsg_p::KEY(Q::SYST,Q::SHOW )] = &TMoth::OnSyst; - (*FnMapx[0])[PMsg_p::KEY(Q::SYST,Q::TOPO )] = &TMoth::OnSyst; - (*FnMapx[0])[PMsg_p::KEY(Q::TINS )] = &TMoth::OnTinsel; - - // mothership's address in POETS space is the host thread ID: X coordinate 0, - // Y coordinate max (within box?? TODO: check this!). TODO: Update this for multi-box! - PAddress = TinselMeshYLenWithinBox << (TinselMeshXBits+TinselLogCoresPerBoard+TinselLogThreadsPerCore); - - twig_running = false; - ForwardMsgs = false; // don't forward tinsel traffic yet - - MPISpinner(); // Spin on *all* messages; exit on DIE - DebugPrint("Exiting Mothership. Closedown flags: AcceptConns: %s, " - "ForwardMsgs: %s\n", AcceptConns ? "true" : "false", - ForwardMsgs ? "true" : "false"); - if (twig_running) StopTwig(); // wait for the twig thread, if it's still somehow running. - printf("********* Mothership rank %d on the way out\n",Urank); fflush(stdout); -} - -//------------------------------------------------------------------------------ - -TMoth::~TMoth() -{ - //printf("********* Mothership rank %d destructor\n",Urank); fflush(stdout); - WALKMAP(uint32_t,PinBuf_t*,TwigMap,D) - { - if (D->second) - { - WALKMAP(uint16_t,char*,(*(D->second)),P) - delete[] P->second; - delete D->second; - } - } - WALKMAP(string,TaskInfo_t*,TaskMap,T) - delete T->second; -} - -//------------------------------------------------------------------------------ - -unsigned TMoth::Boot(string task) -{ - DebugPrint("Entering boot stage\n"); - if (TaskMap.find(task) == TaskMap.end()) - { - Post(107,task); - return 1; - } - DebugPrint("Task %s being booted\n", task.c_str()); - switch(TaskMap[task]->status) - { - case TaskInfo_t::TASK_BOOT: - { - Post(513, task, TaskInfo_t::Task_Status.find(TaskMap[task]->status)->second); - TaskMap[task]->status = TaskInfo_t::TASK_ERR; - return 2; - } - DebugPrint("Task is ready to be booted\n"); - case TaskInfo_t::TASK_RDY: - { - uint32_t mX, mY, core, thread; - // create a response bitmap to receive the various startup barrier messages. - // do this before running 'go' so that we can receive the responses in one block. - map*> t_start_bitmap; - vector taskCores = TaskMap[task]->CoresForTask(); - P_addr coreAddress; - WALKVECTOR(P_core*,taskCores,C) - { - unsigned numThreads = (*C)->P_threadm.size(); - if (numThreads) - { - (*C)->get_hardware_address()->populate_a_software_address(&coreAddress); - t_start_bitmap[TMoth::GetHWAddr(coreAddress)] = new vector(numThreads/(8*sizeof(unsigned)),UINT_MAX); - unsigned remainder = numThreads%(8*sizeof(unsigned)); - if (remainder) t_start_bitmap[TMoth::GetHWAddr(coreAddress)]->push_back(UINT_MAX >> ((8*sizeof(unsigned))-remainder)); - } - } - DebugPrint("Task start bitmaps created for %d cores\n", t_start_bitmap.size()); - // actually boot the cores - WALKVECTOR(P_core*,taskCores,C) - { - (*C)->get_hardware_address()->populate_a_software_address(&coreAddress); - fromAddr(TMoth::GetHWAddr(coreAddress),&mX,&mY,&core,&thread); - DebugPrint("Booting %d threads on core 0x%X (at x:%d y:%d c:%d)\n",(*C)->P_threadm.size(),TMoth::GetHWAddr(coreAddress),mX,mY,core); - DebugPrint("* startOne(meshX=%u, meshY=%u, coreId=%u, numThreads=%lu)\n", - mX, mY, core, (*C)->P_threadm.size()); - startOne(mX,mY,core,(*C)->P_threadm.size()); - DebugPrint("%d threads started on core 0x%X (at x:%d y:%d c:%d)\n",(*C)->P_threadm.size(),TMoth::GetHWAddr(coreAddress),mX,mY,core); - DebugPrint("Triggering %d threads on core %d at x:%d y:%d c:%d\n",(*C)->P_threadm.size(),TMoth::GetHWAddr(coreAddress),mX,mY,core); - DebugPrint("* goOne(meshX=%u, meshY=%u, coreId=%u)\n", mX, mY, core); - goOne(mX,mY,core); - } - // per Matt Naylor comment safer to start all cores then issue the go command to all cores separately. - // WALKVECTOR(P_core*,taskCores,C) - // { - // (*C)->get_hardware_address()->populate_a_software_address(&coreAddress); - // DebugPrint("Triggering %d threads on core %d at x:%d y:%d c:%d\n",(*C)->P_threadm.size(),TMoth::GetHWAddr(coreAddress),mX,mY,core); - // goOne(mX,mY,core); - // } - // DebugPrint("%d cores booted\n", taskCores.size()); - P_Sup_Msg_t barrier_msg; - while (!t_start_bitmap.empty()) - { - recvMsg(&barrier_msg, p_sup_hdr_size()); - DebugPrint("Received a message from a core during application barrier\n"); - if ((barrier_msg.header.sourceDeviceAddr & P_SUP_MASK) && (barrier_msg.header.command == P_PKT_MSGTYP_BARRIER)) - { - DebugPrint("Barrier message from thread ID 0x%X\n", barrier_msg.header.sourceDeviceAddr ^ P_SUP_MASK); - fromAddr(((barrier_msg.header.sourceDeviceAddr ^ P_SUP_MASK) >> P_THREAD_OS),&mX,&mY,&core,&thread); - unsigned hw_core = toAddr(mX,mY,core,0); - DebugPrint("Received a barrier acknowledge from core %#X\n", hw_core); - if (t_start_bitmap.find(hw_core) != t_start_bitmap.end()) - { - DebugPrint("Thread %d on core %d responding\n", thread, core); - DebugPrint("Core bitmap for thread %d before acknowledge: %#X\n", thread, (*t_start_bitmap[hw_core])[thread/(8*sizeof(unsigned))]); - (*t_start_bitmap[hw_core])[thread/(8*sizeof(unsigned))] &= (~(1 << (thread%(8*sizeof(unsigned))))); - DebugPrint("Core bitmap for thread %d after acknowledge: %#X\n", thread, (*t_start_bitmap[hw_core])[thread/(8*sizeof(unsigned))]); - vector::iterator S; - DebugPrint("Core bitmap currently has %d elements\n", t_start_bitmap.size()); - for (S = t_start_bitmap[hw_core]->begin(); S != t_start_bitmap[hw_core]->end(); S++) if (*S) break; - DebugPrint("Core bitmap for core %d has %d subelements\n", hw_core, t_start_bitmap[hw_core]->size()); - if (S == t_start_bitmap[hw_core]->end()) - { - DebugPrint("Removing core bitmap for core %d\n", thread, t_start_bitmap[hw_core]->size()); - t_start_bitmap[hw_core]->clear(); - delete t_start_bitmap[hw_core]; - t_start_bitmap.erase(hw_core); - } - } - } - } - DebugPrint("%d cores passed Mothership barrier and awaiting start signal \n", taskCores.size()); - // MPI_Barrier(Comms[0]); // barrier on the mothercore side (temporarily removed until we have multi-mothership systems) - // create a thread (later will be a process) to deal with packets from - // tinsel cores. Have to do it here, after all the initial setup is complete, - // otherwise setup barrier communications may fail. - void* args = this; - // at this point, load the Supervisor for the task. - SuperHandle = dlopen((TaskMap[task]->BinPath+"/libSupervisor.so").c_str(), RTLD_NOW); - if (!SuperHandle) Post(532,(TaskMap[task]->BinPath+"/libSupervisor.so"),int2str(Urank),string(dlerror())); - else - { - Post (540,int2str(Urank)); - int (*SupervisorInit)() = reinterpret_cast(dlsym(SuperHandle, "SupervisorInit")); - string badFunc(""); - if (!SupervisorInit || (*SupervisorInit)()) badFunc = "SupervisorInit"; - else if ((SupervisorCall = reinterpret_cast(dlsym(SuperHandle, "SupervisorCall"))) == NULL) badFunc = "SupervisorCall"; - if (badFunc.size()) Post(533,badFunc,int2str(Urank),string(dlerror())); - } - ForwardMsgs = true; // set forwarding on so thread doesn't immediately exit - if (pthread_create(&Twig_thread,NULL,Twig,args)) - { - twig_running = false; - ForwardMsgs = false; - Post(531, int2str(Urank)); - } - else twig_running =true; - TaskMap[task]->status = TaskInfo_t::TASK_BARR; // now at barrier on the tinsel side. - return 0; - } - case TaskInfo_t::TASK_RUN: - case TaskInfo_t::TASK_STOP: - case TaskInfo_t::TASK_END: - break; - default: - TaskMap[task]->status = TaskInfo_t::TASK_ERR; - } - Post(511,task,"booted",TaskInfo_t::Task_Status.find(TaskMap[task]->status)->second); - return 3; -} - -//------------------------------------------------------------------------------ - -unsigned TMoth::CmLoad(string task) -// Load a task to the system -{ - DebugPrint("%d tasks deployed to Mothership\n", TaskMap.size()); - if (TaskMap.find(task) == TaskMap.end()) - { - Post(515, task, int2str(Urank)); - return 0; - } - DebugPrint("Task %s found\n", task.c_str()); - WALKMAP(string,TaskInfo_t*,TaskMap,T) - { - // only one task can be active at a time (for the moment. Later we may want more sophisticated task mapping - // that allows multiple active tasks on non-overlapping board sets) - if ((T->first != task) && (T->second->status & (TaskInfo_t::TASK_BOOT | TaskInfo_t::TASK_RDY | TaskInfo_t::TASK_BARR | TaskInfo_t::TASK_RUN | TaskInfo_t::TASK_STOP | TaskInfo_t::TASK_ERR))) - { - map::const_iterator othStatus = TaskInfo_t::Task_Status.find(T->second->status); - if (othStatus == TaskInfo_t::Task_Status.end()) - T->second->status = TaskInfo_t::TASK_ERR; - Post(508,T->first); - return 2; - Post(509,task,T->first,othStatus->second); - return 0; - } - } - DebugPrint("Task status confirmed as TASK_LOAD\n"); - if (!((TaskMap[task]->status == TaskInfo_t::TASK_IDLE) || (TaskMap[task]->status == TaskInfo_t::TASK_END))) - { - Post(511,task,"loaded to hardware",TaskInfo_t::Task_Status.find(TaskMap[task]->status)->second); - return 0; - } - TaskMap[task]->status = TaskInfo_t::TASK_BOOT; - DebugPrint("Task status is TASK_BOOT\n"); - long coresThisTask = 0; - long coresLoaded = 0; - DebugPrint("Task will use %ld boards.\n", - TaskMap[task]->BoardsForTask().size()); - // for each board mapped to the task, - WALKVECTOR(P_board*,TaskMap[task]->BoardsForTask(),B) - { - DebugPrint("Board \"%s\" will use %ld mailboxes.\n", - (*B)->Name().c_str(), (*B)->G.SizeNodes()); - WALKPDIGRAPHNODES(AddressComponent, P_mailbox*, - unsigned, P_link*, - unsigned, P_port*, (*B)->G, MB) - { - DebugPrint("Mailbox \"%s\" will use %ld cores\n", - (*B)->G.NodeData(MB)->Name().c_str(), - (*B)->G.NodeData(MB)->P_corem.size()); - // less work to compute as we go along than to call CoresForTask.size() - // MLV: Not so sure in a post-mailbox world any more... - coresThisTask += (*B)->G.NodeData(MB)->P_corem.size(); - } - coresLoaded += LoadBoard(*B); // load the board (unthreaded model) - } - DebugPrint("All boards finished. %ld cores were in the hardware model for " - "this task. %ld cores have been loaded.\n", - coresThisTask, coresLoaded); - // abandoning the boot if load failed - if (coresLoaded < coresThisTask) - { - TaskMap[task]->status = TaskInfo_t::TASK_ERR; - Post(518,task,int2str(coresLoaded),int2str(coresThisTask)); - return 1; - } - TaskMap[task]->status = TaskInfo_t::TASK_RDY; - DebugPrint("Task status is TASK_RDY\n"); - // boot the board (which has to be done from the main thread as it is not - // thread-safe) - return Boot(task); -} - -//------------------------------------------------------------------------------ - -unsigned TMoth::CmRun(string task) -// Run a specified task. This passes the tinsel-side barrier to start -// application execution -{ - if (TaskMap.find(task) == TaskMap.end()) - { - Post(107, task); - return 0; - } - switch(TaskMap[task]->status) - { - case TaskInfo_t::TASK_BOOT: - case TaskInfo_t::TASK_RDY: - { - Post(513, task, TaskInfo_t::Task_Status.find(TaskMap[task]->status)->second); - TaskMap[task]->status = TaskInfo_t::TASK_ERR; - return 1; - } - case TaskInfo_t::TASK_IDLE: - case TaskInfo_t::TASK_END: - Post(511, task,"run",TaskInfo_t::Task_Status.find(TaskMap[task]->status)->second); - return 0; - case TaskInfo_t::TASK_STOP: - Post(512, task,"run","TASK_STOP"); - TaskMap[task]->status = TaskInfo_t::TASK_ERR; - return 2; - case TaskInfo_t::TASK_RUN: - Post(511, task,"run","TASK_RUN"); - return 0; - case TaskInfo_t::TASK_BARR: - { - DebugPrint("Task %s entering tinsel barrier\n",task.c_str()); - P_Msg_Hdr_t barrier_msg; - barrier_msg.messageLenBytes = p_hdr_size(); // barrier is only a header. No payload. - barrier_msg.destEdgeIndex = 0; // no edge index necessary. - barrier_msg.destPin = P_SUP_PIN_INIT; // it goes to the system __init__ pin - barrier_msg.messageTag = P_MSG_TAG_INIT; // and is of message type __init__. - DebugPrint("Building thread list for task %s\n",task.c_str()); - // build a list of the threads in this task (that should be released from barrier) - vector threadsToRelease; - P_addr threadAddress; - WALKVECTOR(P_thread*,TaskMap[task]->ThreadsForTask(),R) - { - (*R)->get_hardware_address()->populate_a_software_address(&threadAddress); - threadsToRelease.push_back(TMoth::GetHWAddr(threadAddress)); - } - DebugPrint("Issuing barrier release to %d threads in task %s, using " - "message address 0x%X\n", threadsToRelease.size(), - task.c_str(), DEST_BROADCAST); - // and then issue the barrier release to the threads. - WALKVECTOR(unsigned,threadsToRelease,R) - { - barrier_msg.destDeviceAddr = DEST_BROADCAST; // send to every device on the thread with a supervisor message - DebugPrint("Attempting to send barrier release message to the thread " - "with hardware address %u.\n", *R); - send(*R,(p_hdr_size()/(4 << TinselLogWordsPerFlit) + (p_hdr_size()%(4 << TinselLogWordsPerFlit) ? 1 : 0)), &barrier_msg, true); - } - DebugPrint("Tinsel threads now on their own for task %s\n",task.c_str()); - TaskMap[task]->status = TaskInfo_t::TASK_RUN; - return 0; - } - default: - TaskMap[task]->status = TaskInfo_t::TASK_ERR; - } - Post(511,task,"run",TaskInfo_t::Task_Status.find(TaskMap[task]->status)->second); - return 2; -} - -//------------------------------------------------------------------------------ - -unsigned TMoth::CmStop(string task) -// Handle a stop command (which ends the simulation) -{ - if (TaskMap.find(task) == TaskMap.end()) - { - Post(107, task); - return 0; - } - switch(TaskMap[task]->status) - { - case TaskInfo_t::TASK_IDLE: - case TaskInfo_t::TASK_END: - Post(511, task,"stopped",TaskInfo_t::Task_Status.find(TaskMap[task]->status)->second); - return 0; - case TaskInfo_t::TASK_BOOT: - case TaskInfo_t::TASK_RDY: - { - Post(813, task, TaskInfo_t::Task_Status.find(TaskMap[task]->status)->second); - return 1; - } - case TaskInfo_t::TASK_BARR: - { - // if we are at the barrier the simplest approach to - // stop cleanly is simply to start and immediately stop. - // thus as long as starting doesn't error, fall through - // to the running condition immediately below. - if (CmRun(task)) - { - TaskMap[task]->status = TaskInfo_t::TASK_ERR; - break; - } - } - case TaskInfo_t::TASK_RUN: - { - TaskMap[task]->status = TaskInfo_t::TASK_STOP; - // set up for shutdown by creating a global stop message - P_Msg_Hdr_t stop_msg; - stop_msg.destEdgeIndex = 0; // ignore edge index. Unused. - stop_msg.destPin = P_SUP_PIN_SYS_SHORT; // goes to the system pin - stop_msg.messageTag = P_MSG_TAG_STOP; // with a stop message type - DebugPrint("Stopping task %s\n",task.c_str()); - // go through each thread of the task, - //vector threads_for_task = TaskMap[task]->ThreadsForTask(); - P_addr threadAddress; - WALKVECTOR(P_thread*, TaskMap[task]->ThreadsForTask(), R) - { - (*R)->get_hardware_address()->populate_a_software_address(&threadAddress); - uint32_t destDevAddr = TMoth::GetHWAddr(threadAddress); - DebugPrint("Stopping thread %d in task %s\n", destDevAddr, task.c_str()); - stop_msg.destDeviceAddr = DEST_BROADCAST; // issue the stop message to all devices - // then issue the stop packet - send(destDevAddr,(p_hdr_size()/(4 << TinselLogWordsPerFlit) + p_hdr_size()%(4 << TinselLogWordsPerFlit) ? 1 : 0), &stop_msg, true); - } - TaskMap[task]->status = TaskInfo_t::TASK_END; - // check to see if there are any other active tasks - WALKMAP(string, TaskInfo_t*, TaskMap, tsk) - if ((tsk->second->status == TaskInfo_t::TASK_BARR) || (tsk->second->status == TaskInfo_t::TASK_RUN)) return 0; - // if not, shut down the Twig thread. - if (twig_running) StopTwig(); - return 0; - } - case TaskInfo_t::TASK_STOP: - break; - default: - TaskMap[task]->status = TaskInfo_t::TASK_ERR; - } - Post(813,task,"stopped",TaskInfo_t::Task_Status.find(TaskMap[task]->status)->second); - return 2; -} - -//------------------------------------------------------------------------------ - -void TMoth::Dump(FILE * fp) -{ - fprintf(fp,"Mothership dump+++++++++++++++++++++++++++++++++++\n"); - fprintf(fp,"Event handler table:\n"); - unsigned cIdx = 0; - WALKVECTOR(FnMap_t*,FnMapx,F) - { - fprintf(fp,"Function table for comm %d:\n", cIdx++); - fprintf(fp,"Key Method\n"); - WALKMAP(unsigned,pMeth,(**F),i) - fprintf(fp,"%#010x %018lx\n",(*i).first,(uint64_t)&(*i).second); //0x%010x - } - fprintf(fp,"Loaded tasks:\n"); - WALKMAP(string,TaskInfo_t*,TaskMap,Task) - { - fprintf(fp,"Task %s in state %s:\n",Task->second->TaskName.c_str(),TaskInfo_t::Task_Status.find(Task->second->status)->second.c_str()); - fprintf(fp,"Reading from binary path %s\n",Task->second->BinPath.c_str()); - fprintf(fp,"Task map dump:\n"); - Task->second->VirtualBox->Dump(fp); - fprintf(fp,".......................................\n"); - } - fprintf(fp,"Mothership dump-----------------------------------\n"); - fflush(fp); - CommonBase::Dump(fp); -} - -//------------------------------------------------------------------------------ - -long TMoth::LoadBoard(P_board* board) -{ - long coresLoaded = 0; - string task; - - // Find the task to load onto this board. - WALKMAP(string, TaskInfo_t*, TaskMap, K) - { - if (K->second->status == TaskInfo_t::TASK_BOOT) task = K->first; - break; - } - if (!task.empty()) // anything to do? - { - TaskInfo_t* task_map = TaskMap[task]; - P_addr coreAddress; - uint32_t mX, mY, core, thread; // Intermediates for HostLink-side - // address components. - WALKPDIGRAPHNODES(AddressComponent, P_mailbox*, - unsigned, P_link*, - unsigned, P_port*, board->G, MB) - { - WALKMAP(AddressComponent, P_core*, - board->G.NodeData(MB)->P_corem, C) - { - // Grab this core's code and data file - string code_f(task_map->BinPath + "/softswitch_code_" + - int2str(task_map->getCore(C->second)) + ".v"); - string data_f(task_map->BinPath + "/softswitch_data_" + - int2str(task_map->getCore(C->second)) + ".v"); - C->second->instructionBinary = new Bin(fopen(code_f.c_str(), - "r")); - C->second->dataBinary = new Bin(fopen(data_f.c_str(), "r")); - - // Populate the P_addr coreAddress from the core's hardware - // address object. Use coreAddress to define mX, mY, core, and - // thread, for compatbilitity with HostLink's loading methods. - C->second->get_hardware_address()-> - populate_a_software_address(&coreAddress); - DebugPrint("Loading core with virtual address Bx:%d, Bd:%d, " - "Mb: %d, Cr:%d\n", - coreAddress.A_box, coreAddress.A_board, - coreAddress.A_mailbox, coreAddress.A_core); - fromAddr(TMoth::GetHWAddr(coreAddress), &mX, &mY, &core, - &thread); - DebugPrint("Loading hardware thread 0x%X at x:%d y:%d c:%d\n", - TMoth::GetHWAddr(coreAddress), mX, mY, core); - - // Load instruction memory, then data memory. - DebugPrint("* loadInstrsOntoCore(codeFilename=%s, meshX=%u, " - "meshY=%u, coreId=%u)\n", - code_f.c_str(), mX, mY, core); - loadInstrsOntoCore(code_f.c_str(), mX, mY, core); - DebugPrint("* loadDataViaCore(dataFilename=%s, meshX=%u, " - "meshY=%u, coreId=%u)\n", - data_f.c_str(), mX, mY, core); - loadDataViaCore(data_f.c_str(), mX, mY, core); - ++coresLoaded; - } - } - } - DebugPrint("Boot process for board %s finished %ld cores loaded\n", - board->Name().c_str(), coresLoaded); - return coresLoaded; -} - -//------------------------------------------------------------------------------ - -unsigned TMoth::NameDist(PMsg_p* mTask_Info) -// receive a broadcast core info block from the NameServer. -{ - CMsg_p task_info(*mTask_Info); - string TaskName; - task_info.Get(0, TaskName); - map::iterator T; - if ((T=TaskMap.find(TaskName)) == TaskMap.end()) - { - DebugPrint("Inserting new task %s from NameDist\n", TaskName.c_str()); - TaskMap[TaskName] = new TaskInfo_t(TaskName); - } - vector> cores; - task_info.Get(cores); - DebugPrint("Task %s has %d cores\n",TaskName.c_str(),cores.size()); - // set up the cores - for (vector>::iterator core = cores.begin(); core != cores.end(); core++) - TaskMap[TaskName]->insertCore(core->first, core->second); - DebugPrint("%d cores inserted into TaskInfo structure for %s\n",cores.size(),TaskName.c_str()); - return 0; -} - -//------------------------------------------------------------------------------ - -unsigned TMoth::NameRecl(PMsg_p* mTask_Info) -// remove (recall) a core info block from the Mothership. -{ - string TaskName; - mTask_Info->Get(0, TaskName); - map::iterator T; - if ((T=TaskMap.find(TaskName)) == TaskMap.end()) // task exists? - { - Post(607, TaskName); // No. Nothing to do. - return 0; - } - switch (TaskMap[TaskName]->status) - { - case TaskInfo_t::TASK_IDLE: - case TaskInfo_t::TASK_BOOT: - case TaskInfo_t::TASK_STOP: - case TaskInfo_t::TASK_END: - break; - case TaskInfo_t::TASK_RDY: - { - Post(813, TaskName, "recalled", "TASK_RDY"); - return 1; - } - case TaskInfo_t::TASK_BARR: - case TaskInfo_t::TASK_RUN: - CmStop(TaskName); // stop any running tasks before recalling them. - break; - default: - { - TaskMap[TaskName]->status = TaskInfo_t::TASK_ERR; - Post(812, TaskName); - return 1; - } - } - system((string("rm -r -f ")+TaskMap[TaskName]->BinPath).c_str()); - delete T->second; // get rid of its TaskInfo object - TaskMap.erase(T); // and then remove it from the task map - return 0; -} - -//------------------------------------------------------------------------------ - -unsigned TMoth::NameTdir(const string& task, const string& dir) -// set the path where binaries for a given task are to be found -{ - map::iterator T; - if ((T=TaskMap.find(task)) == TaskMap.end()) - { - DebugPrint("Inserting new task %s from NameTdir\n", task.c_str()); - TaskMap[task] = new TaskInfo_t(task); - } - TaskMap[task]->BinPath = dir; - return 0; -} - -//------------------------------------------------------------------------------ - -unsigned TMoth::OnCmnd(PMsg_p * Z, unsigned cIdx) -// Handler for a task command sent (probably) from the user. -{ - // get the task that the command is going to operate on - string task; - Z->Get(0,task); - unsigned key = Z->Key(); - if (key == PMsg_p::KEY(Q::CMND,Q::LOAD )) - return CmLoad(task); - if (key == PMsg_p::KEY(Q::CMND,Q::RUN )) - return CmRun(task); - if (key == PMsg_p::KEY(Q::CMND,Q::STOP )) - return CmStop(task); - else - Post(510,"Control",int2str(Urank)); - return 0; -} - -//------------------------------------------------------------------------------ - -void* TMoth::Twig(void* par) -// the Twig processing deals with receiving packets from the managed Tinsel cores. -// Ideally, Twig, OnIdle, and OnTinselOut would reside in a separate *process*. -// For the moment we run Twig in a separate *thread* because to run a separate -// process would require identifying the matching Branch's rank, which could be -// done, using a variety of methods, but none of them trivial, and so that's -// 'for later'. -{ - TMoth* parent = static_cast(par); - //char recv_buf[p_msg_size()]; // buffer for one packet at a time - char *recv_buf = new char[p_msg_size()]; // buffer for one packet at a time - void* p_recv_buf = static_cast(recv_buf); - FILE* OutFile; - char Line[4*P_MSG_MAX_SIZE]; - fpos_t readPos; - fpos_t writePos; - if ( (OutFile = fopen("./DebugOutput.txt", "a+")) ) - { - fsetpos(OutFile, &readPos); - fsetpos(OutFile, &writePos); - } - while (parent->ForwardMsgs) // until told otherwise, - { - // receive all available traffic. Should this be done or only one packet - // and then try again for MPI? We don't expect MPI traffic to be intensive - // and tinsel messsages might be time-critical. - while (parent->canRecv()) - { - DebugPrint("Message received from a Device\n"); - parent->recv(recv_buf); - - uint32_t* device = static_cast(p_recv_buf); // get the first word, which will be a device address - if (!(*device & P_SUP_MASK)) // bound for an external? - { - P_Msg_Hdr_t* m_hdr = static_cast(p_recv_buf); - DebugPrint("Message is bound for external device %d\n", m_hdr->destDeviceAddr); - if (parent->TwigExtMap[m_hdr->destDeviceAddr] == 0) - parent->TwigExtMap[m_hdr->destDeviceAddr] = new deque; - parent->TwigExtMap[m_hdr->destDeviceAddr]->push_back(*(static_cast(p_recv_buf))); - } - else - { - P_Sup_Hdr_t* s_hdr = static_cast(p_recv_buf); - s_hdr->sourceDeviceAddr ^= P_SUP_MASK; - - if (s_hdr->command == P_PKT_MSGTYP_ALIVE) - { - DebugPrint("Thread %d is still alive\n", s_hdr->sourceDeviceAddr >> P_THREAD_OS); - } - else - { - DebugPrint("Message is a Supervisor request from device %d\n", s_hdr->sourceDeviceAddr); - if (parent->TwigMap[s_hdr->sourceDeviceAddr] == 0) // new device talking? - { - DebugPrint("New device %d reporting to Supervisor\n", s_hdr->sourceDeviceAddr); - parent->TwigMap[s_hdr->sourceDeviceAddr] = new PinBuf_t; - } - if ((*(parent->TwigMap[s_hdr->sourceDeviceAddr]))[s_hdr->destPin] == 0) // inactive pin for the device? - { - DebugPrint("New pin %d for device %d reporting to Supervisor\n", s_hdr->destPin, s_hdr->sourceDeviceAddr); - (*(parent->TwigMap[s_hdr->sourceDeviceAddr]))[s_hdr->destPin] = new char[MAX_P_SUP_MSG_BYTES](); - } - P_Sup_Msg_t* recvdMsg = static_cast(static_cast((*(parent->TwigMap[s_hdr->sourceDeviceAddr]))[s_hdr->destPin])); - - memcpy(recvdMsg+s_hdr->seq,recv_buf,p_sup_msg_size()); // stuff message into the persistent buffer - - if (super_buf_recvd(recvdMsg)) - { - DebugPrint("Entire Supervisor message received of length %d\n", s_hdr->cmdLenBytes); - if (parent->OnTinselOut(recvdMsg)) - parent->Post(530, int2str(parent->Urank)); - super_buf_clr(recvdMsg); - } - } - } - } - // Capture anything happening on the DebugLink - which is text output directed at a file. - bool updated = false; - if (OutFile) - { - fsetpos(OutFile, &writePos); - while (parent->pollStdOut(OutFile)) updated = true; - if (updated) - { - DebugPrint("Received a debug output message\n"); - } - fgetpos(OutFile, &writePos); - } - else while (parent->pollStdOut()); // or possibly only dumped to the local console - // output the debug output buffer, which has to be done immediately because we are in a separate thread - if (OutFile && updated) - { - fflush(OutFile); - fsetpos(OutFile, &readPos); - while (!feof(OutFile)) parent->Post(600, string(fgets(Line, 4*P_MSG_MAX_SIZE, OutFile))); - fgetpos(OutFile, &readPos); - } - } - printf("Exiting Twig thread\n"); - delete[] recv_buf; - pthread_exit(par); - return par; -} - -//------------------------------------------------------------------------------ - -void TMoth::OnIdle() -// idle processing deals with forwarding packets from the managed Tinsel cores. -{ - // queues may be changing but we can deal with a static snapshot of the actual - // queue because OnIdle will execute periodically. - WALKMAP(uint32_t,deque*,TwigExtMap,D) - { - int NameSrvComm = RootCIdx(); - // int NameSrvComm = NameSCIdx(); - PMsg_p W(Comms[NameSrvComm]); // return packets to be routed via the NameServer's comm - W.Key(Q::TINS); // it'll be a Tinsel packet - W.Tgt(pPmap[NameSrvComm]->U.Root); // temporary: dump external packets to root - //W.Tgt(pPmap[NameSrvComm]->U.NameServer); // directed to the NameServer (or UserIO, when we have it) - W.Src(Urank); // coming from us - /* well, this is awkward: the PMsg_p type has a Put method for vectors of objects, - which is what we want. Our packet should have a vector of P_Msg_t's. But as things - stand, the messages are trapped in a deque (because we want our twig process to - be able to append to the vector of things to send). Which means copying them out - into a vector. Again, this would be fine if we could copy them directly into a - vector in the PMsg_p, but the interface doesn't allow it - it expects to copy - from vector to vector. So we seem to be stuck with this silly bucket brigade - approach. NOT the most efficient way to move messages. - */ - vector packet; - while (D->second->size()) - { - packet.push_back(D->second->front()); - D->second->pop_front(); - } - W.Put(0,&packet); // stuff the Tinsel messages into the packet - W.Send(); // and away it goes. - } -} - -//------------------------------------------------------------------------------ - -unsigned TMoth::OnName(PMsg_p * Z, unsigned cIdx) -// This handles what happens when the NameServer dumps a name subblock to the -// mothership -{ - unsigned key = Z->Key(); - if (key == PMsg_p::KEY(Q::NAME,Q::DIST )) - { - DebugPrint("NameDist command received\n"); - return NameDist(Z); - } - if (key == PMsg_p::KEY(Q::NAME,Q::RECL )) - { - DebugPrint("NameRecl command received\n"); - return NameRecl(Z); - } - if (key == PMsg_p::KEY(Q::NAME,Q::TDIR )) - { - string task,dir; - Z->Get(0,task); - Z->Get(1,dir); - DebugPrint("NameTdir command received: task %s, directory %s\n", task.c_str(), dir.c_str()); - return NameTdir(task,dir); - } - else - Post(510,"Name",int2str(Urank)); - return 0; -} - -//------------------------------------------------------------------------------ - -unsigned TMoth::OnExit(PMsg_p * Z, unsigned cIdx) -// This is what happens when a user command to stop happens -{ - // We are going away. Shut down any active tasks. - WALKMAP(string, TaskInfo_t*, TaskMap, tsk) - { - if ((tsk->second->status == TaskInfo_t::TASK_BOOT) || (tsk->second->status == TaskInfo_t::TASK_END)) continue; - if (tsk->second->status == TaskInfo_t::TASK_BARR) CmRun(tsk->first); - if (tsk->second->status == TaskInfo_t::TASK_RUN) CmStop(tsk->first); - } - // stop accepting Tinsel messages - if (twig_running) StopTwig(); - return CommonBase::OnExit(Z,cIdx); // exit through CommonBase handler -} - -//------------------------------------------------------------------------------ - -unsigned TMoth::OnSuper(PMsg_p * Z, unsigned cIdx) -// Main hook for user-defined supervisor commands -{ - // set up a return message if we are going to send some reply back - PMsg_p W(Comms[cIdx]); - W.Key(Q::SUPR); - W.Src(Z->Tgt()); - int superReturn = 0; - if ((superReturn = (*SupervisorCall)(Z,&W)) > 0) // Execute. Send a reply if one is expected - { - if (!cIdx && (Z->Tgt() == Urank) && (Z->Src() == Urank)) OnTinsel(&W, 0); // either to Tinsels, - else W.Send(Z->Src()); // or to some external or internal process. - } - if (superReturn < 0) Post(530, int2str(Urank)); - return 0; -} - -//------------------------------------------------------------------------------ - -unsigned TMoth::OnSyst(PMsg_p * Z, unsigned cIdx) -// Handler for system commands sent by the user or operator -{ - unsigned key = Z->Key(); - if (key == PMsg_p::KEY(Q::SYST,Q::HARD )) - { - vector args; - Z->Get(0,args); - if (SystHW(args)) // A system hardware command executes an external process. - { - string cmd; - WALKVECTOR(string,args,arg) - { - cmd+=(*arg); - cmd+=(' '); - } - Post(520,int2str(Urank),cmd); - return 0; - } - return 0; - } - if (key == PMsg_p::KEY(Q::SYST,Q::KILL )) - return SystKill(); // Kill brutally shuts us down by exiting immediately - if (key == PMsg_p::KEY(Q::SYST,Q::SHOW )) - return SystShow(); - if (key == PMsg_p::KEY(Q::SYST,Q::TOPO )) - return SystTopo(); - else - Post(524,int2str(Z->Key())); - return 0; -} - -//------------------------------------------------------------------------------ - -unsigned TMoth::OnTinsel(PMsg_p * Z, unsigned cIdx) -// Handler for direct packets to be injected into the network from an external source -{ - vector msgs; // messages are packed in Tinsel message format - Z->Get(0, msgs); // We assume they're directly placed in the message - WALKVECTOR(P_Msg_t, msgs, msg) // and they're sent blindly - { - uint32_t Len = static_cast(msg->header.messageLenBytes); - uint32_t FlitLen = Len >> TinselLogBytesPerFlit; - if (Len << (32-TinselLogBytesPerFlit)) ++FlitLen; - // if we have to we can run OnIdle to empty receive buffers - send(msg->header.destDeviceAddr, FlitLen, &(*msg), true); - } - return 0; -} - -//------------------------------------------------------------------------------ - -unsigned TMoth::OnTinselOut(P_Sup_Msg_t * packet) -// Deals with what happens when a Tinsel message is received. Generally we -// repack the message for delivery to the Supervisor handler and deal with -// it there. The Supervisor can do one of 2 things: A) process it itself, -// possibly generating another message; B) immediately export it over MPI to -// the user Executive or other external process. -{ - DebugPrint("Processing a command message 0x%x from Tinsel device %d\n", packet->header.command, packet->header.sourceDeviceAddr); - // handle the kill request from a tinsel core, which generally means an assert failed. - if ((packet->header.command == P_SUP_MSG_KILL)) return SystKill(); - // output messages can simply be posted to the LogServer as an informational message. - if ((packet->header.command == P_SUP_MSG_LOG)) - { - DebugPrint("Received a handler_log message from device %d\n", packet->header.sourceDeviceAddr, p_sup_msg_size()); - // Just output the string (this will involve some rubbish at the end where arguments would be; - // to be fixed later). Note that uint8_t*'s have to be reinterpret_casted to char*s. - unsigned msg_len = ((packet->header.cmdLenBytes%p_sup_msg_size()) && (packet->header.seq == packet->header.cmdLenBytes/p_sup_msg_size())) ? packet->header.cmdLenBytes%p_sup_msg_size() : p_sup_msg_size()-p_sup_hdr_size(); - msg_len -= p_sup_hdr_size(); - Post(601, int2str(packet->header.sourceDeviceAddr), int2str(packet->header.seq), string(reinterpret_cast(packet->data), msg_len)); - return 0; - } - DebugPrint("Message from device %d is a Supervisor call. Redirecting\n", packet->header.sourceDeviceAddr); - PMsg_p W(Comms[0]); // Create a new packet on the local comm - W.Key(Q::SUPR); // it'll be a Supervisor packet - W.Src(Urank); // coming from the us - W.Tgt(Urank); // and directed at us - unsigned last_index = packet->header.cmdLenBytes/p_sup_msg_size() + (packet->header.cmdLenBytes%p_sup_msg_size() ? 1 : 0); - vector pkt_v(packet,&packet[last_index]); // maybe slightly more efficient using the constructor - W.Put(0,&pkt_v); // stuff the Tinsel message into the packet - return OnSuper(&W, 0); - // W.Send(); // away it goes. - // return 0; -} - -//------------------------------------------------------------------------------ - -void TMoth::StopTwig() -// Cleanly shut down (or try to) the Twig process. This occurs whenever we -// end a task in preparation for shutting down, or exiting. -{ - if (!twig_running) return; - ForwardMsgs = false; // notify the Twig to shut down - pthread_join(Twig_thread,NULL); // wait for it to do so - if (SuperHandle) // then unload its Supervisor - { - // clean up memory first - int (*SupervisorExit)() = reinterpret_cast(dlsym(SuperHandle, "SupervisorExit")); - if (!SupervisorExit || (*SupervisorExit)() || dlclose(SuperHandle)) - { - Post(534,int2str(Urank)); - SuperHandle = 0; // even if we errored invalidate the Supervisor - } - SupervisorCall = 0; - } - twig_running = false; -} - -//------------------------------------------------------------------------------ - -unsigned TMoth::SystHW(const vector& args) -// Execute some command directly on the Mothership itself. Unlike a Supervisor -// message, which triggers the Supervisor function but doesn't change the -// functionality of the Mothership, this can directly interact with the -// running Mothership to do certain tasks -{ - stringstream cmd(ios_base::out | ios_base::ate); - WALKCVECTOR(string, args, arg) - cmd << *arg; - return system(cmd.str().c_str()); -} - -//------------------------------------------------------------------------------ - -unsigned TMoth::SystKill() -// Kill some process in the local MPI universe. We had better be sure there is -// no traffic destined for this process after such a brutal command! -{ - return 1; -} - -//------------------------------------------------------------------------------ - -unsigned TMoth::SystShow() -// Report the list of processes (active Motherships) on this comm -{ - return 0; -} - -//------------------------------------------------------------------------------ - -unsigned TMoth::SystTopo() -// Report this Mothership's local topology information (boards/cores/connections) -{ - return 0; -} -//============================================================================== diff --git a/Source/Mothership/TMoth.h b/Source/Mothership/TMoth.h deleted file mode 100644 index 0c3ba537..00000000 --- a/Source/Mothership/TMoth.h +++ /dev/null @@ -1,91 +0,0 @@ -#ifndef __TMothH__H -#define __TMothH__H - -#include -#include "CommonBase.h" -#include "Debug.h" -#include "PMsg_p.hpp" -#include "Cli.h" -#include "HostLink.h" -#include "pthread.h" -#include "TaskInfo.h" -#include "poets_msg.h" -#include "P_addr.h" - -//============================================================================== - -class TMoth : public CommonBase, public HostLink -{ - -public: - TMoth(int,char **,string); -virtual ~ TMoth(); - -// somewhat bodgey function to return the hardware address from the composite; needed because of -// the actual hardware mappings; this is the Orchestrator-side equivalent of toAddr. -static inline unsigned GetHWAddr(P_addr& VAddr) { - return (VAddr.A_box << P_BOX_HWOS) - | (VAddr.A_board << P_BOARD_HWOS) - | (VAddr.A_mailbox << P_MAILBOX_HWOS) - | (VAddr.A_core << P_CORE_HWOS) - | (VAddr.A_thread << P_THREAD_HWOS); - }; -// static void* LoadBoard(void*); // threaded version of bootloader -static void* Twig(void*); // thread to handle Tinsel messages - -private: -unsigned Boot(string); -unsigned CmLoad(string); -unsigned CmRun(string); -unsigned CmStop(string); -#include "Decode.cpp" -inline virtual string Dname(){ return typeid(*this).name(); } -void Dump(FILE * = stdout); -long LoadBoard(P_board*); // unthreaded version of bootloader -unsigned NameDist(PMsg_p*); -unsigned NameRecl(PMsg_p*); -unsigned NameTdir(const string&, const string&); -unsigned OnCmnd(PMsg_p *,unsigned); -unsigned OnExit(PMsg_p *,unsigned); -void OnIdle(); -unsigned OnName(PMsg_p *,unsigned); -unsigned OnSuper(PMsg_p *,unsigned); -unsigned OnSyst(PMsg_p *,unsigned); -unsigned OnTinsel(PMsg_p*, unsigned); -unsigned OnTinselOut(P_Sup_Msg_t *); -void StopTwig(); -unsigned SystHW(const vector&); -unsigned SystKill(); -unsigned SystShow(); -unsigned SystTopo(); - -void* SuperHandle; // dynamically loadable supervisor -int (*SupervisorCall)(PMsg_p*, PMsg_p*); // entry point for the Supervisor - -public: -unsigned PAddress; // address of this mothership in POETS-space -bool ForwardMsgs; - -typedef unsigned (TMoth::*pMeth)(PMsg_p *,unsigned); -typedef map FnMap_t; -typedef map PinBuf_t; // type to hold buffers for messages received from devices - -map*> TwigExtMap; // dynamic queues for messages bound for external devices -map TwigMap; // dynamic buffer state for each device from which the Mothership is receiving -map TaskMap; // which tasks are mapped to the machine -vector> BootMap; // which booter is starting which board -vector FnMapx; -pthread_t Twig_thread; // thread for processing tinsel connections -bool twig_running; // flag to show twig thread is active - -static const int NumBoards = NUM_BOARDS_PER_BOX; - -}; - -//============================================================================== - -#endif - - - - diff --git a/Source/NameServer/ABDefs.cpp b/Source/NameServer/ABDefs.cpp new file mode 100644 index 00000000..48c492f8 --- /dev/null +++ b/Source/NameServer/ABDefs.cpp @@ -0,0 +1,54 @@ +#include "ABDefs.hpp" + +namespace AddressBookNS +{ + +// const SymAddr_t INVALID_ADDRESS; + +int DeviceData_t::size() const +{ + int size = 0; + + size += sizeof(SymAddr_t) * 2; + size += Name.size(); + + return size; +} + + +DevTypeRecord_t::DevTypeRecord_t(std::string N) +{ + Name = N; +} + +int DevTypeRecord_t::size() const +{ + int size = 0; + + size += Name.size(); + size += sizeof(MsgIdx) * InMsgs.size(); + size += sizeof(MsgIdx) * OuMsgs.size(); + + return size; +} + + +MsgTypeRecord_t::MsgTypeRecord_t(std::string N) +{ + Name = N; +} + +int MsgTypeRecord_t::size() const +{ + int size = 0; + + size += Name.size(); + size += sizeof(MsgIdx) * Inputs.size(); + size += sizeof(MsgIdx) * Outputs.size(); + + return size; +} + + + +} /* namespace AddressBookNS */ diff --git a/Source/NameServer/ABDefs.hpp b/Source/NameServer/ABDefs.hpp new file mode 100644 index 00000000..15e7e9dc --- /dev/null +++ b/Source/NameServer/ABDefs.hpp @@ -0,0 +1,60 @@ +#ifndef ABDefs_H +#define ABDefs_H + +#include "OSFixes.hpp" + +#include +#include +#include +#include + +namespace AddressBookNS +{ + +//---------------------------------------- +// Temporary typedefs +typedef uint64_t SymAddr_t; // A 64-bit full-symbolic address + +static const SymAddr_t INVALID_ADDRESS = UINT64_MAX; +//typedef unsigned long SymAddr_t; // A 64-bit full-symbolic address + +//---------------------------------------- + +enum TaskState_t { Loaded = 0, Linked, Built, Deployed, Init, Running, Finished, Unknown }; + +typedef unsigned DTypeIdx; +typedef int AttrIdx; +typedef unsigned MsgIdx; + + +struct DeviceData_t { + int size() const; // Get the size of the DeviceData + + SymAddr_t Address; // Device's full sym address. + SymAddr_t Supervisor; // Full sym address of the Device's Supervisor. + std::string Name; // Device's cnonical name. +}; + + +struct DevTypeRecord_t { + DevTypeRecord_t(std::string N = ""); + int size() const; // Get the size of the DeviceType Record + + std::string Name; // Device Type cnonical name. + std::vector InMsgs; // Vector of Input Message indexes + std::vector OuMsgs; // Vector of Output Message indexes +}; + + + +struct MsgTypeRecord_t { + MsgTypeRecord_t(std::string N = ""); + int size() const; // Get the size of the MsgType Record + + std::string Name; // Message Type Name + std::vector Inputs; // Indicies of DeviceTypes that have this MessageType as an input + std::vector Outputs; // Indicies of DeviceTypes that have this MessageType as an output +}; + +} /* namespace AddressBookNS */ +#endif /* ABDefs_H */ diff --git a/Source/NameServer/ABRecord.cpp b/Source/NameServer/ABRecord.cpp new file mode 100644 index 00000000..1fb2e383 --- /dev/null +++ b/Source/NameServer/ABRecord.cpp @@ -0,0 +1,40 @@ +#include "ABRecord.hpp" + +namespace AddressBookNS +{ + +Record_t::Record_t() +{ + Attribute = -1; +} + +Record_t::Record_t(std::string Name, SymAddr_t Addr, uint64_t Super, + DTypeIdx Type, RecordType_t RT, AttrIdx Attr) + : Name(Name) +{ + Address = Addr; + DeviceType = Type; + RecordType = RT; + Attribute = Attr; + Rank = 0; + Supervisor = 0; + if (RecordType == Supervisor) Rank = static_cast(Super); + else Supervisor = static_cast(Super); +} + + +int Record_t::size() const +{ + int size = 0; + + size += sizeof(SymAddr_t) * 2; // Address and Supervisor/Rank + size += sizeof(DTypeIdx); // Device Type Idx + size += sizeof(AttrIdx); // Attribute Idx + size += sizeof(RecordType_t); // Record Type + size += sizeof(char) * Name.size(); // Name + + return size; +} + + +} /* namespace AddressBookNS */ diff --git a/Source/NameServer/ABRecord.hpp b/Source/NameServer/ABRecord.hpp new file mode 100644 index 00000000..ef011887 --- /dev/null +++ b/Source/NameServer/ABRecord.hpp @@ -0,0 +1,39 @@ +#ifndef ABRecord_H +#define ABRecord_H + +#include "ABDefs.hpp" + +namespace AddressBookNS +{ + enum RecordType_t { Device = 0, DeviceExt, External, Supervisor }; + + // data contents factored out for serialisation 17 July 2019 ADR + struct RecordData_t + { + public: + + SymAddr_t Address; // Device's full sym address. + union + { + SymAddr_t Supervisor; // Full sym address of the Device's Supervisor. + unsigned long Rank; // OR Supervisor's MPI Rank. + }; + DTypeIdx DeviceType; // Index of the Device's type in task. + AttrIdx Attribute; // Index of the Device's attribute in task. + RecordType_t RecordType; // Class of device represented by the record. + }; + + class Record_t : public RecordData_t + { + public: + + Record_t(); + Record_t(std::string, SymAddr_t, uint64_t, DTypeIdx, RecordType_t = Device, AttrIdx = -1); + + int size() const; // Size of the record + + std::string Name; // Device's canonical name. + }; + +} /* namespace AddressBookNS */ +#endif /* ABRecord_H */ diff --git a/Source/NameServer/ABTask.cpp b/Source/NameServer/ABTask.cpp new file mode 100644 index 00000000..57706876 --- /dev/null +++ b/Source/NameServer/ABTask.cpp @@ -0,0 +1,802 @@ +#include "ABTask.hpp" + +namespace AddressBookNS +{ + +/*============================================================================== + * TaskData + *============================================================================*/ +TaskData_t::TaskData_t() +{ + Name = ""; + Path = ""; + XML = ""; + ExecutablePath = ""; + State = Loaded; + DeviceCount = 0; + DeviceCountLd = 0; + ExternalCount = 0; + ExternalCountLd = 0; + SupervisorCount = 0; + + State = Loaded; +} + +TaskData_t::TaskData_t(std::string &N, std::string &P, std::string &X, std::string &E, + TaskState_t S, unsigned long DC, unsigned long EC) +{ + Name = N; + Path = P; + XML = X; + ExecutablePath = E; + State = Loaded; + DeviceCount = DC; + DeviceCountLd = 0; + ExternalCount = EC; + ExternalCountLd = 0; + SupervisorCount = 0; + + State = S; +} + +int TaskData_t::size() const +{ + int size = 0; + + size += sizeof(unsigned long)*5; // Size of the 5 longs + size += sizeof(char) * Name.size(); // Size of the name + size += sizeof(char) * Path.size(); // Size of the path + size += sizeof(char) * XML.size(); // Size of the XML Filename + size += sizeof(char) * ExecutablePath.size(); // Size of the exec path + size += sizeof(TaskState_t); + + for(std::vector::const_iterator DT = DeviceTypes.begin(); + DT != DeviceTypes.end(); DT++) // Size of the device types + { + size += DT->size(); + } + + for(std::vector::const_iterator MT = MessageTypes.begin(); + MT != MessageTypes.end(); MT++) // Size of the Message Types + { + size += MT->size(); + } + + for(std::vector::const_iterator AT = AttributeTypes.begin(); + AT != AttributeTypes.end(); AT++) // Size of the Attribute Types + { + size += AT->size(); + } + + return size; +} + +/*============================================================================== + * TaskRecord + *============================================================================*/ +TaskRecord_t::TaskRecord_t() +{ + Name = ""; + Path = ""; + XML = ""; + ExecPath = ""; + DevCntMax = 0; + ExtCntMax = 0; + DevCnt = 0; + SupCnt = 0; + ExtCnt = 0; + + State = Loaded; + + TaskValid = true; + MapValid = true; + LinkValid = true; +} + +TaskRecord_t::TaskRecord_t(std::string &N, std::string &P, std::string &X, std::string &E, + TaskState_t S, unsigned long DC, unsigned long EC) +{ + Name = N; + Path = P; + XML = X; + ExecPath = E; + DevCntMax = DC; + ExtCntMax = EC; + DevCnt = 0; + SupCnt = 0; + ExtCnt = 0; + + State = S; + + TaskValid = true; + MapValid = true; + LinkValid = true; +} + +unsigned TaskRecord_t::Integrity(bool Verbose, FILE * fp) +{ + ABULONG ExtConCnt(0); + ABUNSIGNED MappedSupervisors(0); + + IntegVals_t retVal; + retVal.ret = 0; + retVal.retT = 0; + retVal.retL = 0; + retVal.retM = 0; + + //fprintf(fp,"Task: %s\n", Search->first.c_str()); + + if (Verbose) fprintf(fp, "Integrity check of %s:\n", Name.c_str()); + + //========================================================================== + // Task checks + + // Device Counts - failing these means the task is broken? + if(Devices.size() != DevCnt) + { + // Device Count mismatch. + if (Verbose) fprintf(fp, "Device Count mismatch: %lu!=%lu\n", + static_cast(Devices.size()), DevCnt); + retVal.ret++; + } + + if(Externals.size() != ExtCnt) + { + // External Count mismatch. + if (Verbose) fprintf(fp, "External Count mismatch: %lu!=%lu\n", + static_cast(Externals.size()), ExtCnt); + retVal.ret++; + } + + if(Supervisors.size() != SupCnt) + { + // Supervisor Count mismatch. + if (Verbose) fprintf(fp, "Supervisor Count mismatch: %lu!=%lu\n", + static_cast(Supervisors.size()), SupCnt); + retVal.ret++; + } + + if(SupMap.size() != SupCnt) + { + // Supervisor Count-SupMap size mismatch. + if (Verbose) fprintf(fp, "Supervisor Count<>SupMap size mismatch: %lu!=%lu\n", + static_cast(SupMap.size()), SupCnt); + retVal.ret++; + } + + +#ifdef AB_THREADING + //========================================================================== + // Multi-threaded checks of large structures. Number of threads used scales + // with the number of CPU cores. The checks are read-only so no locks for + // data structure access. Atomic types are used for return values. + unsigned threadCnt = 0, i = 0; + + // Base the scaling off of the available concurrency, with a minimum of 1 + // thread for each check, for a minimum of 4 total threads. Otherwise, + // maximum concurrency is 2* available concurrency. + unsigned int concurrency = std::thread::hardware_concurrency(); + unsigned int halfConcurrency = (concurrency < 2) ? 1 : concurrency / 2; + unsigned int qrtrConcurrency = (concurrency < 4) ? 1 : concurrency / 4; + + // Create a vector of threads of the correct size. + std::vector threads(concurrency + halfConcurrency + + qrtrConcurrency + qrtrConcurrency); + + + //========================================================================== + // Thread(s) for checking DeviceType vectors, etc. + unsigned long dtConcurrency = halfConcurrency; + if (DevTypes.size() < halfConcurrency) // Limit number of threads if we + { // have fewer DeviceTypes than + dtConcurrency = DevTypes.size(); // possible threads. + } + unsigned long devTypeSplit = DevTypes.size() / dtConcurrency; + for(i = 0; i < dtConcurrency; i++) + { + // Handle any remainder in the last thread - integ method checks bounds. + unsigned endMul = (i==(dtConcurrency-1)) ? 2 : 1; + threads[threadCnt+i] = std::thread(&TaskRecord_t::IntegDevTypeMap, this, + Verbose, fp, (devTypeSplit * i), + (devTypeSplit * (i+endMul)), + std::ref(retVal)); + } + threadCnt += i; + + //========================================================================== + // Thread(s) for checking Devices vector + unsigned long dConcurrency = concurrency; + if (Devices.size() < concurrency) // Limit number of threads if we + { // have fewer Devices than + dConcurrency = Devices.size(); // possible threads. + } + unsigned long long devSplit = Devices.size() / dConcurrency; + for(i = 0; i < dConcurrency; i++) + { + // Handle any remainder in the last thread - integ method checks bounds. + unsigned endMul = (i==(dConcurrency-1)) ? 2 : 1; + threads[threadCnt+i] = std::thread(&TaskRecord_t::IntegDevices, this, + Verbose, fp, (devSplit * i), + (devSplit * (i+endMul)), + std::ref(retVal), + std::ref(ExtConCnt)); + } + threadCnt += i; + + //========================================================================== + // Thread(s) for checking Externals vector + unsigned long eConcurrency = qrtrConcurrency; + if (Externals.size() < qrtrConcurrency) // Limit number of threads if we + { // have fewer Externals than + eConcurrency = Externals.size(); // possible threads. + } + unsigned long extSplit = Externals.size() / eConcurrency; + for(i = 0; i < eConcurrency; i++) + { + // Handle any remainder in the last thread - integ method checks bounds. + unsigned endMul = (i==(eConcurrency-1)) ? 2 : 1; + threads[threadCnt+i] = std::thread(&TaskRecord_t::IntegExternals, this, + Verbose, fp, (extSplit * i), + (extSplit * (i+endMul)), + std::ref(retVal)); + } + threadCnt += i; + + //========================================================================== + // Thread(s) for checking Supervisors vector. + unsigned long sConcurrency = qrtrConcurrency; + if (Supervisors.size() < qrtrConcurrency) // Limit number of threads if we + { // have fewer Supervisors than + sConcurrency = Supervisors.size(); // possible threads. + } + unsigned long superSplit = Supervisors.size() / sConcurrency; + for(i = 0; + i < sConcurrency; + i++) + { + // Handle any remainder in the last thread - integ method checks bounds. + unsigned endMul = (i==(sConcurrency-1)) ? 2 : 1; + threads[threadCnt+i] = std::thread(&TaskRecord_t::IntegSupervisors, this, + Verbose, fp, (superSplit * i), + (superSplit * (i+endMul)), + std::ref(retVal), + std::ref(MappedSupervisors)); + } + threadCnt += i; + + +#endif + + // Check integrity of MsgType map + for(std::vector::iterator M + =MsgTypes.begin();M!=MsgTypes.end();M++) // Iterate through message type vector + { + unsigned Idx = static_cast(std::distance(MsgTypes.begin(), M)); // Current Index + + IdxMap_t::const_iterator MSearch = MsgTypeMap.find(M->Name); // Find the Msg Name in the map + if (MSearch == MsgTypeMap.end()) { + // Message Type not mapped + if (Verbose) fprintf(fp, "T: Message Type not mapped: %s\n", + M->Name.c_str()); + retVal.retT++; + } else { + if (MsgTypes[MSearch->second].Name != M->Name){ + // Message Type > Index map is broken. + if (Verbose) fprintf(fp, "T: Message Type > Index map is broken: %s\n", + M->Name.c_str()); + retVal.retT++; + } + + if (Idx != MSearch->second) { + // Index mismatch + if (Verbose) fprintf(fp, "T: Index mismatch: %s, %u!=%u\n", + M->Name.c_str(), Idx, MSearch->second); + retVal.retT++; + } + } + } + + // Check integrity of AttributeType map + for(std::vector::iterator A + =AttrTypes.begin();A!=AttrTypes.end();A++) // Iterate through message type vector + { + unsigned Idx = static_cast(std::distance(AttrTypes.begin(), A)); // Current Index + + IdxMap_t::const_iterator ASearch = AttrTypeMap.find(A->first); // Find the Msg Name in the map + if (ASearch == AttrTypeMap.end()) { + // Attribute Type not mapped + if (Verbose) fprintf(fp, "T: Attribute Type not mapped: %s\n", + A->first.c_str()); + retVal.retT++; + } else { + if (AttrTypes[ASearch->second].first != A->first){ + // Attribute Type > Index map is broken. + if (Verbose) fprintf(fp, "T: Attribute > Index map broken: %s!=%s\n", + A->first.c_str(), AttrTypes[ASearch->second].first.c_str()); + retVal.retT++; + } + + if (Idx != ASearch->second) { + // Index mismatch + if (Verbose) fprintf(fp, "T: Attribute Index mismatch: %u!m%u\n", + Idx, ASearch->second); + retVal.retT++; + } + } + } + + + //========================================================================== + // Map checks + + // Map size integrity checks - Re-mapping should fix. + unsigned long totalDevCnt = DevCnt + ExtCnt; + if(NameMap.size() != totalDevCnt) + { + // NameMap Size mismatch - Map broken + if (Verbose) fprintf(fp, "M: NameMap size mismatch: %lu!=%lu\n", + static_cast(NameMap.size()), + totalDevCnt); + retVal.retM++; + } + + + //========================================================================== + // Link checks + + // Link integrity checks - Re-linking should fix. + if(AddrMap.size() != (totalDevCnt + SupCnt)) + { + // AddressMap Size mismatch - Link broken + if (Verbose) fprintf(fp, "L: AddrMap Size mismatch: %lu!=%lu\n", + static_cast(AddrMap.size()), + (DevCnt + SupCnt)); + retVal.retL++; + } + + +#ifdef AB_THREADING + // Wait for all of the threads to finish + for(i = 0; i < threadCnt; i++) + { + threads[i].join(); + } +#else + // If we are not using threading, run all of the (long) integrity checks single-threaded. + + //========================================================================== + // Check integrity of DeviceType map + IntegDevTypeMap(Verbose, fp, 0, DevTypes.size(), retVal); + + //========================================================================== + // Device checks + IntegDevices(Verbose, fp, 0, Devices.size(), retVal, ExtConCnt); // Check ALL devices + + //========================================================================== + // External checks + IntegExternals(Verbose, fp, 0, Externals.size(), retVal); // Check ALL externals + + //========================================================================== + // Supervisor checks + IntegSupervisors(Verbose, fp, 0, Supervisors.size(), retVal, MappedSupervisors); // Check ALL supervisors +#endif + + + //========================================================================== + // Check ExtCon size. + if(ExtCon.size() != ExtConCnt) + { + // External Connection vector size mismatch - Map broken + if (Verbose) fprintf(fp, "M: External Connection Vector size mismatch: %lu!=%lu\n", + static_cast(ExtCon.size()), + static_cast(ExtConCnt)); + retVal.retM++; + } + + //========================================================================== + // Check Supervisor map size + if (MappedSupervisors != SupMap.size()) { + // Supervisor Map size missmatch + if (Verbose) fprintf(fp, "L: Supervisor Size mismatch: %u!=%lu\n", + static_cast(MappedSupervisors), + static_cast(SupMap.size())); + retVal.retL++; + } + + //========================================================================== + // Set dirty flags if appropriate + if (Verbose) fprintf(fp, "\nResults:\n"); + + if (retVal.ret > 0) + { + if (Verbose) fprintf(fp, "\tGeneral Breakage: %d\n", static_cast(retVal.ret)); + } + + + if (retVal.retT > 0) // Task integrity compromised - need to rbuild the task + { + TaskValid = false; + if (Verbose) fprintf(fp, "\tDirty Task: %d\n", static_cast(retVal.retT)); + retVal.ret += retVal.retT; + } + + if (retVal.retM > 0) // Map integrity compromised - need to rebuild the map + { + MapValid = false; + if (Verbose) fprintf(fp, "\tDirty Map: %d\n", static_cast(retVal.retM)); + retVal.ret += retVal.retM; + } + + if (retVal.retL > 0) // Link integrity compromised - need to rebuild the link + { + LinkValid = false; + if (Verbose) fprintf(fp, "\tDirty Link: %d\n", static_cast(retVal.retL)); + retVal.ret += retVal.retL; + } + + if (Verbose && (retVal.ret == 0)) fprintf(fp, "\tTask Clean\n"); + + return retVal.ret; +} + +void TaskRecord_t::IntegDevTypeMap(bool Verbose, FILE * fp, unsigned long DTStart, + unsigned long DTEnd, IntegVals_t &retVal) +{ + for(std::vector::iterator D = DevTypes.begin() + DTStart; + (D != DevTypes.end()) && (D < (DevTypes.begin() + DTEnd)); + D++) // Iterate through device type vector + { + unsigned Idx = static_cast(std::distance(DevTypes.begin(), D)); // Current Index + + IdxMap_t::const_iterator DTSearch = DevTypeMap.find(D->first.Name); // Find the DevType name in the map + if (DTSearch == DevTypeMap.end()) { + // DevType not mapped + if (Verbose) fprintf(fp, "T: DevType not mapped: %s\n", + D->first.Name.c_str()); + retVal.retT++; + + } else { + if (DevTypes[DTSearch->second].first.Name != D->first.Name){ + // DevType > Index map is broken. + if (Verbose) fprintf(fp, "T: DevTypeMap Broken: %s!=%s\n", + D->first.Name.c_str(), + DevTypes[DTSearch->second].first.Name.c_str()); + retVal.retT++; + } + + if (Idx != DTSearch->second) { + // Index mismatch + if (Verbose) fprintf(fp, "T: DevTypeMap Index mismatch: %s, %u!=%u\n", + D->first.Name.c_str(), Idx, DTSearch->second); + retVal.retT++; + } + } + + //Check the Input Msg Indexes + for(std::vector::const_iterator I + =D->first.InMsgs.begin();I!=D->first.InMsgs.end();I++) + { + const std::vector *InMsgs = &MsgTypes[*I].Inputs; + if (std::find(InMsgs->begin(), InMsgs->end(), Idx) == InMsgs->end()) + { + // MsgType->DevType (Input Message) Vector broken. + if (Verbose) fprintf(fp, "T: MsgType->DevType(InMsg) Vector broken: \ + DevType %u (%s) not found in MsgType InMsg Vector %u\n", + Idx, D->first.Name.c_str(), *I); //DevTypes[Idx].first.Name + retVal.retT++; + } + } + + //Check the Output Msg Indexes + for(std::vector::const_iterator O + =D->first.OuMsgs.begin();O!=D->first.OuMsgs.end();O++) + { + const std::vector *OuMsgs = &MsgTypes[*O].Outputs; + if (std::find(OuMsgs->begin(), OuMsgs->end(), Idx) == OuMsgs->end()) + { + // MsgType->DevType (Output Message) Vector broken. + if (Verbose) fprintf(fp, "T: MsgType->DevType(OuMsg) Vector broken: \ + DevType %u (%s) not found in MsgType OuMsg Vector %u\n", + Idx, D->first.Name.c_str(), *O); + retVal.retT++; + } + } + } +} + + +void TaskRecord_t::IntegDevices(bool Verbose, FILE * fp, unsigned long DStart, + unsigned long DEnd, IntegVals_t &retVal, + ABULONG &ExtConCnt) +{ + for(std::vector::const_iterator D = Devices.begin() + DStart; + (D != Devices.end()) && (D < (Devices.begin() + DEnd)); + D++) // Iterate through section of Device vector + { + // Check record type + if((D->RecordType != Device) && (D->RecordType != DeviceExt)) + { + // Non-Device in the Device vector. Something, somewhere has gone very VERY wrong + if (Verbose) fprintf(fp, "Non-Device in Device Vector: %s (%" SYMA_FMT "), %u\n", + D->Name.c_str(), D->Address, D->RecordType); + retVal.ret++; + } else { + // Search Addr Map + AddrMap_t::const_iterator DASearch = AddrMap.find(D->Address); // Find the dev Addr in the map + if (DASearch == AddrMap.end()) { + // DevAddr not mapped - Link broken + if (Verbose) fprintf(fp, "L: DevAddr not mapped: %s, %" SYMA_FMT "\n", + D->Name.c_str(), D->Address); + retVal.retL++; + } else if (DASearch->second != &(*D)) { + // Address does not map to THIS device - Link broken + if (Verbose) fprintf(fp, "L: AddrMap to wrong device: %s, %" PTR_FMT "!=%" PTR_FMT "\n", + D->Name.c_str(), reinterpret_cast(DASearch->second), + reinterpret_cast(&(*D))); + retVal.retL++; + } + + // Search Name Map + NameMap_t::const_iterator DNSearch = NameMap.find(D->Name); // Find the dev Name in the map + if (DNSearch == NameMap.end()) { + // DevName not mapped + if (Verbose) fprintf(fp, "M: Device Name not mapped: %s\n", + D->Name.c_str()); + retVal.retM++; + } else if (DNSearch->second != &(*D)) { + // Name does not map to THIS device - Map Broken + if (Verbose) fprintf(fp, "M: Namemap to wrong device: %s, %" PTR_FMT "!=%" PTR_FMT "\n", + D->Name.c_str(), reinterpret_cast(DNSearch->second), + reinterpret_cast(&(*D))); + retVal.retM++; + } + + // Check Device type Index + if (D->DeviceType >= DevTypes.size()) { + // DeviceType Index out of range + if (Verbose) fprintf(fp, "DeviceType Index out of range: %s (%" SYMA_FMT "), DType: %d>%d\n", + D->Name.c_str(), D->Address, D->DeviceType, + static_cast(DevTypes.size())); + retVal.ret++; + } else { + // Find the pointer to the device record in the Device Type vector + const RecordVect_t *DTypeV = &DevTypes[D->DeviceType].second; + if (std::find(DTypeV->begin(), DTypeV->end(), &(*D)) == DTypeV->end()) + { + // Device not in Device Type vector - Map Broken + if (Verbose) fprintf(fp, "M: Device %s (%" SYMA_FMT ") not in DevicetypeVector %u (%s)\n", + D->Name.c_str(), D->Address, D->DeviceType, + DevTypes[D->DeviceType].first.Name.c_str()); + retVal.retM++; + } + } + + // Search Supervisor + SupMap_t::const_iterator DSSearch = SupMap.find(D->Supervisor); // Find the dev Addr in the map + if (DSSearch == SupMap.end()) { + // Device's Supervisor not found - Link broken + if (Verbose) fprintf(fp, "L: Dev's Supervisor not found in AddrMap: %s, %" SYMA_FMT "\n", + D->Name.c_str(), D->Supervisor); + retVal.retL++; + } else { + // Search Supervsor Vector + const RecordVect_t *SupV = &DSSearch->second; + if (std::find(SupV->begin(), SupV->end(), &(*D)) == SupV->end()) + { + // Pointer to Device Record not found in Supervisor Vector. + if (Verbose) fprintf(fp, "L: Ptr to Device not in Supervisor \ + vector: %s, Supervisor: %" SYMA_FMT "\n", + D->Name.c_str(), D->Supervisor); + retVal.retL++; + } + } + + // Check if the Attribute Index is out of range + if(D->Attribute >= static_cast(AttrTypes.size())) + { + // Attribute Index out of range. + if (Verbose) fprintf(fp, "Attribute Index out of range: %s (%" SYMA_FMT "), Attr: %d>%d\n", + D->Name.c_str(), D->Address, D->Attribute, + static_cast(AttrTypes.size())); + retVal.ret++; + } else if (D->Attribute > -1) { + // Find the pointer to the device record in the Attribute Type vector + const RecordVect_t *ATypeV = &AttrTypes[static_cast(D->Attribute)].second; + if (std::find(ATypeV->begin(), ATypeV->end(), &(*D)) == ATypeV->end()) + { + // Device not in Attribute Type vector - Map Broken + if (Verbose) fprintf(fp, "L: Dev not in Attribute vector: %s\ + , Attribute: %d (%s)\n", D->Name.c_str(), D->Attribute, + AttrTypes[static_cast(D->Attribute)].first.c_str()); + retVal.retM++; + } + } + + // If ExtCon, search ExtCon Vector, ExtConCnt++ + if(D->RecordType == DeviceExt) + { + if (std::find(ExtCon.begin(), ExtCon.end(), &(*D)) == ExtCon.end()) + { + // Device with External Connection not found in ExtCon - Map Broken + if (Verbose) fprintf(fp, "M: Device with External connection not in\ + ExtCon: %s (%" SYMA_FMT ")\n", D->Name.c_str(), D->Address); + retVal.retM++; + } + ExtConCnt++; + } + } + } +} + +void TaskRecord_t::IntegExternals(bool Verbose, FILE * fp, unsigned long EStart, + unsigned long EEnd, IntegVals_t &retVal) +{ + for(std::vector::const_iterator E = Externals.begin() + EStart; + (E != Externals.end()) && (E < (Externals.begin() + EEnd)); + E++) // Iterate through External vector + { + // Check record type + if(E->RecordType != External) + { + // Non-External in the External vector. Something, somewhere has gone very VERY wrong + if (Verbose) fprintf(fp, "Non-External in External Vector: %s, \ + %" SYMA_FMT ", %d\n", E->Name.c_str(), E->Address, E->RecordType); + retVal.ret++; + } else { + // Search Addr Map + AddrMap_t::const_iterator EASearch = AddrMap.find(E->Address); // Find the dev Addr in the map + if (EASearch == AddrMap.end()) { + // ExtAddr not mapped - Link broken + if (Verbose) fprintf(fp, "L: ExtAddr not mapped: %s, %" SYMA_FMT "\n", + E->Name.c_str(), E->Address); + retVal.retL++; + } else if (EASearch->second != &(*E)) { + // Address does not map to THIS external - Link broken + if (Verbose) fprintf(fp, "L: AddrMap to wrong External: %s, %" PTR_FMT "!=%" PTR_FMT "\n", + E->Name.c_str(), reinterpret_cast(EASearch->second), + reinterpret_cast(&(*E))); + retVal.retL++; + } + + // Search Name Map + NameMap_t::const_iterator ENSearch = NameMap.find(E->Name); // Find the dev Name in the map + if (ENSearch == NameMap.end()) { + // ExtName not mapped + if (Verbose) fprintf(fp, "M: External Name not mapped: %s (%" SYMA_FMT ")\n", + E->Name.c_str(), E->Address); + retVal.retM++; + } else if (ENSearch->second != &(*E)) { + // Name does not map to THIS external - Map Broken + if (Verbose) fprintf(fp, "M: Namemap to wrong external: %s, %" PTR_FMT "!=%" PTR_FMT "\n", + E->Name.c_str(), reinterpret_cast(ENSearch->second), + reinterpret_cast(&(*E))); + retVal.retM++; + } + + // Check Device type Index + if (E->DeviceType >= DevTypes.size()) { + // DeviceType Index out of range + if (Verbose) fprintf(fp, "DeviceType Index out of range: %s (%" SYMA_FMT ")\ + , DType: %u>%d\n", E->Name.c_str(), E->Address, E->DeviceType, + static_cast(DevTypes.size())); + retVal.ret++; + } else { + // Find the pointer to the device record in the Device Type vector + const RecordVect_t *DTypeV = &DevTypes[E->DeviceType].second; + if (std::find(DTypeV->begin(), DTypeV->end(), &(*E)) == DTypeV->end()) + { + // External not in Device Type vector - Map Broken + if (Verbose) fprintf(fp, "M: External %s (%" SYMA_FMT ") not in \ + DeviceType Vector %u (%s)\n", + E->Name.c_str(), E->Address, E->DeviceType, + DevTypes[E->DeviceType].first.Name.c_str()); + retVal.retM++; + } + } + + // Check if the Attribute Index is out of range + if(E->Attribute >= static_cast(AttrTypes.size())) + { + // Attribute Index out of range. + if (Verbose) fprintf(fp, "Attribute Index out of range: %s (%" SYMA_FMT "), \ + Attr: %d>%d\n", E->Name.c_str(), E->Address, E->Attribute, + static_cast(AttrTypes.size())); + retVal.ret++; + } else if (E->Attribute > -1) { + // Find the pointer to the external record in the Attribute Type vector + const RecordVect_t *ATypeV = &AttrTypes[static_cast(E->Attribute)].second; + if (std::find(ATypeV->begin(), ATypeV->end(), &(*E)) == ATypeV->end()) + { + // External not in Attribute Type vector - Map Broken + if (Verbose) fprintf(fp, "L: Ext not in Attribute vector: %s, \ + Attribute: %d (%s)\n", E->Name.c_str(), E->Attribute, + AttrTypes[static_cast(E->Attribute)].first.c_str()); + retVal.retM++; + } + } + } + } +} + +void TaskRecord_t::IntegSupervisors(bool Verbose, FILE * fp, unsigned long SStart, + unsigned long SEnd, IntegVals_t &retVal, + ABUNSIGNED &MappedSupervisors) +{ + for(std::vector::const_iterator S = Supervisors.begin() + SStart; + (S != Supervisors.end()) && (S < (Supervisors.begin() + SEnd)); + S++) // Iterate through Supervisor vector + { + // Check record type + if(S->RecordType != Supervisor) + { + // Non-Supervisor in the Supervisor vector. Something, somewhere has gone very VERY wrong + if (Verbose) fprintf(fp, "Non-Supervisor in Supervisor Vector: %s, \ + %" SYMA_FMT ", %d\n", S->Name.c_str(), S->Address, S->RecordType); + retVal.ret++; + } else { + // Check that Supervisor is listed in Addr Map + AddrMap_t::const_iterator SASearch = AddrMap.find(S->Address); // Find the Addr in the map + if (SASearch == AddrMap.end()) { + // SupAddr not mapped - Link broken + if (Verbose) fprintf(fp, "L: SupAddr not mapped: %s, %" SYMA_FMT "\n", + S->Name.c_str(), S->Address); + retVal.retL++; + } else if (SASearch->second != &(*S)) { + // Address does not map to THIS supervisor - Link broken + if (Verbose) fprintf(fp, "L: AddrMap to wrong Supervisor: %s, %" PTR_FMT "!=%" PTR_FMT "\n", + S->Name.c_str(), reinterpret_cast(SASearch->second), + reinterpret_cast(&(*S))); + retVal.retL++; + } + + // Check that Supervisor is listed in SupMap + SupMap_t::const_iterator SMSearch = SupMap.find(S->Address); + if (SMSearch == SupMap.end()) + { + // SupAddr not mapped - Link broken + if (Verbose) fprintf(fp, "L: SupAddr not in SupMap: %s, %" SYMA_FMT "\n", + S->Name.c_str(), S->Address); + retVal.retL++; + } else { + MappedSupervisors++; + + // Check all devices allocated to supervisor use THIS supervisor + for(RecordVect_t::const_iterator SMV + =SMSearch->second.begin();SMV!=SMSearch->second.end();SMV++) // Iterate through Supervisor's devices + { + if((*SMV)->Supervisor != SMSearch->first) + { + // Supervisor Address in device record does not match THIS supervisor - Link broken + if (Verbose) fprintf(fp, "L: Dev SupAddr mismatch: %s, %"\ + SYMA_FMT "!=%" SYMA_FMT "\n", (*SMV)->Name.c_str(), + (*SMV)->Supervisor, SMSearch->first); + retVal.retL++; + } + } + } + + // Check Device type Index + if (S->DeviceType >= DevTypes.size()) { + // DeviceType Index out of range + if (Verbose) fprintf(fp, "DeviceType Index out of range: Supervisor (%" SYMA_FMT \ + "), DType: %u>%d\n", S->Address, S->DeviceType, + static_cast(DevTypes.size())); + retVal.ret++; + } else { + // Find the pointer to the device record in the Device Type vector + const RecordVect_t *DTypeV = &DevTypes[S->DeviceType].second; + if (std::find(DTypeV->begin(), DTypeV->end(), &(*S)) == DTypeV->end()) + { + // Supervisor not in Device Type vector - Map Broken + if (Verbose) fprintf(fp, "M: Supervisor (%" SYMA_FMT \ + ") not in DeviceType Vector %u (%s)\n", + S->Address, S->DeviceType, + DevTypes[S->DeviceType].first.Name.c_str()); + retVal.retM++; + } + } + } + } +} + +} /* namespace AddressBookNS */ diff --git a/Source/NameServer/ABTask.hpp b/Source/NameServer/ABTask.hpp new file mode 100644 index 00000000..bff1c296 --- /dev/null +++ b/Source/NameServer/ABTask.hpp @@ -0,0 +1,139 @@ +#ifndef ABTask_H +#define ABTask_H + +#include "ABDefs.hpp" +#include "ABRecord.hpp" + +#include +#include +#include + +#if (__cplusplus >= 201103) && (\ + (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L)) ||\ + defined (__unix__) || (defined (__APPLE__) && defined (__MACH__))) + //C++11+ and C11+ or POSIX: + // fprintf is thread safe so let's thread stuff! + #include + #include + #define AB_THREADING + + // We need Atomic types if we are using threading. + #define ABUNSIGNED std::atomic + #define ABULONG std::atomic +#else + #define ABUNSIGNED unsigned + #define ABULONG unsigned long + +#endif + +namespace AddressBookNS +{ + +typedef std::map IdxMap_t; +typedef std::vector RecordVect_t; +typedef std::map SupMap_t; +typedef std::map RecordMap_t; +typedef std::map AddrMap_t; +typedef std::map NameMap_t; + +typedef std::pair DevTypePair; +typedef std::pair AttrTypePair; + +struct IntegVals_t { + ABUNSIGNED ret; + ABUNSIGNED retT; + ABUNSIGNED retL; + ABUNSIGNED retM; +}; + +struct TaskData_t { + TaskData_t(); + TaskData_t(std::string &N, std::string &P, std::string &X, std::string &E, + TaskState_t S, unsigned long DC, unsigned long EC); + + int size() const; // Get the size (in bytes) of the TaskData + + std::string Name; // = ""; + std::string Path; // = ""; + std::string XML; // = ""; + std::string ExecutablePath; // = ""; + TaskState_t State; // = Loaded; + unsigned long DeviceCount; // = 0; // The maximum number of devices in task + unsigned long DeviceCountLd; // = 0; // The number of devices currently loaded + unsigned long ExternalCount; // = 0; // The maximum number of externals in task + unsigned long ExternalCountLd; // = 0; // The number of externals currently loaded + unsigned long SupervisorCount; // = 0; // The number of allocated supervisors + std::vector DeviceTypes; + std::vector MessageTypes; + std::vector AttributeTypes; +}; + + +class TaskRecord_t { +public: + TaskRecord_t(); + TaskRecord_t(std::string &N, std::string &P, std::string &X, std::string &E, + TaskState_t S, unsigned long DC, unsigned long EC); + TaskRecord_t(TaskData_t &Data); + + unsigned Integrity(bool Verbose = false, FILE * = stdout); + + int size() const; // Get the size (in bytes) of the TaskData + + + std::string Name; // = ""; + std::string Path; // = ""; + std::string XML; // = ""; + std::string ExecPath; // = ""; + unsigned long DevCntMax; // = 0; // The expected number of devices + unsigned long ExtCntMax; // = 0; // The expected number of external devices + unsigned long DevCnt; // = 0; // The loaded number of Devices + unsigned long SupCnt; // = 0; // The loaded number of Supervisors + unsigned long ExtCnt; // = 0; // The loaded number of External Devices + + TaskState_t State; // = Loaded; //State of the task + + std::vector Devices; // List of Devices in the task + std::vector Externals; // List of Externals in the task + std::vector Supervisors; // List of Supervisors in the task + + std::vector MsgTypes; // List of task Msg types from XML + //std::vector MsgTypes; // List of task Msg types from XML + std::vector DevTypes; // List of task Dev Types from XML + std::vector AttrTypes; // List of task Attrs from XML + + RecordVect_t ExtCon; // List of Devices with external conns + + // Task String Data Maps + IdxMap_t MsgTypeMap; // Map Msg type string to Idx + IdxMap_t DevTypeMap; // Map Dev type Name to Idx + IdxMap_t AttrTypeMap; // Map Attr type string to Idx + + //Device Maps + SupMap_t SupMap; // Supervisor>DeviceAddress map + AddrMap_t AddrMap; // Addr>record map basis of DNAME, DGRP, SNAME + NameMap_t NameMap; // Name>record map basis of DNAME, DGRP, SNAME + + bool TaskValid; // = true; // Indicate whether the task string info is valid + bool MapValid; // = true; // Indicate whether the task maps are valid + bool LinkValid; // = true; // Indicate whether the Device > Address links are valid + +private: + void IntegDevices(bool Verbose, FILE * fp, unsigned long DStart, + unsigned long DEnd, IntegVals_t &retVal, + ABULONG &ExtConCnt); + + void IntegExternals(bool Verbose, FILE * fp, unsigned long EStart, + unsigned long EEnd, IntegVals_t &retVal); + + void IntegSupervisors(bool Verbose, FILE * fp, unsigned long SStart, + unsigned long SEnd, IntegVals_t &retVal, + ABUNSIGNED &MappedSupervisors); + + void IntegDevTypeMap(bool Verbose, FILE * fp, unsigned long DTStart, + unsigned long DTEnd, IntegVals_t &retVal); +}; + + +} /* namespace AddressBookNS */ +#endif /* ABTask_H */ diff --git a/Source/NameServer/AddressBook.cpp b/Source/NameServer/AddressBook.cpp new file mode 100644 index 00000000..61eb4368 --- /dev/null +++ b/Source/NameServer/AddressBook.cpp @@ -0,0 +1,1361 @@ +//============================================================================== +#include "AddressBook.hpp" +//#include "Debug.h" + +#include + +//============================================================================== +namespace AddressBookNS +{ +#ifdef EXCEPTS +#define ERETURN(X, Y) throw(X) +#else +#define ERETURN(X, Y) return Y +#endif + +// static constant definitions +const unsigned AddressBook::SUCCESS; +const unsigned AddressBook::ERR_NONFATAL; +const unsigned AddressBook::ERR_INVALID_TASK; +const unsigned AddressBook::ERR_INVALID_DEVTYPE; +const unsigned AddressBook::ERR_INVALID_DEVICE; +const unsigned AddressBook::ERR_INVALID_MESSAGE_TYPE; +const unsigned AddressBook::ERR_INVALID_ATTRIBUTE; +const unsigned AddressBook::ERR_DEVICE_DATA_MISMATCH; +const unsigned AddressBook::ERR_TASKNAME_USED; +const unsigned AddressBook::ERR_DEVICENAME_USED; +const unsigned AddressBook::ERR_DEVICE_ADDR_USED; +const unsigned AddressBook::ERR_TASK_NOT_FOUND; +const unsigned AddressBook::ERR_DEVICE_NOT_FOUND; +const unsigned AddressBook::ERR_INVALID_MAP; +const unsigned AddressBook::ERR_INVALID_SUPERVISOR; +const unsigned AddressBook::ERR_INVALID_STATE; + +//Constructors +AddressBook::AddressBook(std::string d) +{ + ABderived = d; + TaskCount = 0; +} + +AddressBook::~AddressBook() +{ + // Teardown the TaskMap. Everything else should be handled properly. + for(TaskMap_t::iterator T=TaskMap.begin();T!=TaskMap.end();T++) + { + TaskRecord_t* TRec = &(*(T->second)); + delete TRec; + + } +} + + +/*============================================================================== + * GetTaskCount: Get the number of currently loaded tasks + *============================================================================*/ +unsigned AddressBook::GetTaskCount(void) +{ + return TaskMap.size(); +} + +//============================================================================== +//Task Manipulation +unsigned AddressBook::AddTask(const std::string &TaskName, TaskData_t &Data) +{ + // Check that the task does not already exist. + TaskRecord_t *TRec = FindTask(TaskName); + if(TRec != PNULL) + { + //throw("Task Name Already Used"); + return ERR_TASKNAME_USED; + } + + // validate here and exit if the device type data is invalid. These will + // actually be checked again when device types are added below; a duplication, + // but the number of device types and number of message types is going to + // be relatively small, so this isn't going to be too costly + unsigned Ret = ValidateTask(Data); + if(Ret) return Ret; + + // Build a TaskRecord + TaskRecord_t * Task = new TaskRecord_t; + + Task->Name = TaskName; + Task->Path = Data.Path; + Task->XML = Data.XML; + Task->ExecPath = Data.ExecutablePath; + + Task->DevCntMax = Data.DeviceCount; + Task->ExtCntMax = Data.ExternalCount; + + // Pre-reserve the Vectors of Records for consistent pointers. + Task->Devices.reserve(Data.DeviceCount + 1); + Task->Externals.reserve(Data.ExternalCount + 1); + Task->Supervisors.reserve(MAXSUPERVISORS + 1); + + // String Data - copy it over, initialise vectors and create index maps. + // MessageType strings. + Task->MsgTypes.resize(Data.MessageTypes.size()); //pre-alloc vector + std::vector *MTypes = &(Data.MessageTypes); + for(std::vector::const_iterator M=MTypes->begin(); + M!=MTypes->end(); M++) + { + Task->MsgTypes[static_cast(M - MTypes->begin())].Name = *M; + Task->MsgTypeMap.insert(IdxMap_t::value_type(*M, (M-MTypes->begin()))); + } + + // AttributeType strings. + Task->AttrTypes.resize(Data.AttributeTypes.size()); + std::vector *ATypes = &(Data.AttributeTypes); + for(std::vector::const_iterator A=ATypes->begin(); + A!=ATypes->end(); A++) + { + AttrTypePair APair(*A,RecordVect_t()); + Task->AttrTypes[static_cast(A - ATypes->begin())] = APair; + Task->AttrTypeMap.insert(IdxMap_t::value_type(*A, (A-ATypes->begin()))); + } + + // Add to the Task Map. + TaskMap.insert(TaskMap_t::value_type(Task->Name, Task)); + ++TaskCount; // Pedantic: avoid assignment to temporary 5 July 2019 ADR + + /* Insert DeviceTypes. Changed order and call subfunction 5 July 2019 ADR + Original had pre-allocation below, but it's hard to see how this + really gains anything because a DevTypeRecord_t contains 2 internal + vectors (which would be zero-initialised) and a resize is thus inevitable + anyway when actual device types are inserted. + */ + // Task->DevTypes.resize(Data.DeviceTypes.size()); //pre-alloc vector + std::vector *DTypes = &(Data.DeviceTypes); + for(std::vector::iterator D=DTypes->begin(); + D!=DTypes->end(); D++) + AddDeviceType(Task->Name, *D); // add is bound to succeed because we have + // pre-validated. + return SUCCESS; +} + +/*============================================================================== + * AddDeviceType: Append another device type to the task. + *============================================================================*/ +unsigned AddressBook::AddDeviceType(const std::string &TaskName, const DevTypeRecord_t &DeviceType) +{ + // Check that the task exists. + TaskRecord_t *Task = FindTask(TaskName); + if(Task == PNULL) + { + return ERR_TASK_NOT_FOUND; + } + // and that the new device type has valid message indices + if (unsigned retVal = ValidateDeviceType(DeviceType, Task->MsgTypes.size())) + return retVal; + DevTypePair DPair(DeviceType,RecordVect_t()); + Task->DevTypes.push_back(DPair); + unsigned devTypIdx = Task->DevTypes.size() - 1; + Task->DevTypeMap.insert(IdxMap_t::value_type(DeviceType.Name, devTypIdx)); + + // Cross-ref to Message Types. + for(std::vector::const_iterator I=DeviceType.InMsgs.begin(); + I!=DeviceType.InMsgs.end(); I++) // Input Messages + { + Task->MsgTypes[*I].Inputs.push_back(devTypIdx); + } + for(std::vector::const_iterator O=DeviceType.OuMsgs.begin(); + O!=DeviceType.OuMsgs.end(); O++) // Output Messages + { + Task->MsgTypes[*O].Outputs.push_back(devTypIdx); + } + return SUCCESS; +} + +/*============================================================================== + * ClearTask: Removes all the devices from a task + *============================================================================*/ +unsigned AddressBook::ClearTask(const std::string &TaskName) +{ + // The easiest way to do this is simply to copy the data to be saved into + // a TaskData_t object, remove the task, then re-add it with all the device + // data removed. + TaskData_t cTask; + unsigned err = SUCCESS; + if ((err = GetTask(TaskName, cTask)) != SUCCESS) return err; + cTask.DeviceCountLd = 0; + cTask.ExternalCountLd = 0; + cTask.SupervisorCount = 0; + if ((err = DelTask(TaskName)) != SUCCESS) return err; + return AddTask(TaskName, cTask); +} + +/*============================================================================== + * ListTask: Populate a vector with all of the names of loaded tasks + *============================================================================*/ +unsigned AddressBook::ListTask(std::vector &Tasks) +{ + // Enumerate the Task List. + for(TaskMap_t::iterator T=TaskMap.begin();T!=TaskMap.end();T++) + { + Tasks.push_back(T->second->Name); + } + return SUCCESS; +} + +/*============================================================================== + * GetTask: Get a task by name + *============================================================================*/ +unsigned AddressBook::GetTask(const std::string &TaskName, TaskData_t &Task) +{ + TaskRecord_t *TRec = FindTask(TaskName); + if(TRec == PNULL) + { + //throw("Task Does Not Exist"); + return ERR_TASK_NOT_FOUND; + } + + Task.Name = TRec->Name; + Task.Path = TRec->Path; + Task.XML = TRec->XML; + Task.ExecutablePath = TRec->ExecPath; + Task.State = TRec->State; + Task.DeviceCount = TRec->DevCntMax; + Task.DeviceCountLd = TRec->DevCnt; + Task.ExternalCount = TRec->ExtCntMax; + Task.ExternalCountLd = TRec->ExtCnt; + Task.SupervisorCount = TRec->SupCnt; + + // Copy MessageType strings + for(std::vector::const_iterator M=TRec->MsgTypes.begin(); + M!=TRec->MsgTypes.end(); M++) + { + Task.MessageTypes.push_back(M->Name); + } + + // Copy DeviceType strings + for(std::vector::const_iterator D=TRec->DevTypes.begin(); + D!=TRec->DevTypes.end(); D++) + { + Task.DeviceTypes.push_back(D->first); + } + + // Copy AttributeType strings + for(std::vector::const_iterator A=TRec->AttrTypes.begin(); + A!=TRec->AttrTypes.end(); A++) + { + Task.AttributeTypes.push_back(A->first); + } + + return SUCCESS; +} + +/*============================================================================== + * DelTask: Delete the named task + *============================================================================*/ +unsigned AddressBook::DelTask(const std::string &TaskName) +{ + TaskRecord_t *TRec = FindTask(TaskName); + if(TRec == PNULL) + { + //throw("Task Does Not Exist"); + return ERR_TASK_NOT_FOUND; + } + + // Erase it + delete TRec; + TaskMap.erase(TaskName); + + TaskCount--; + + return SUCCESS; +} + +/*============================================================================== + * TaskState: Get the state of the task + *============================================================================*/ +TaskState_t AddressBook::TaskState(std::string &TaskName) +{ + TaskRecord_t *TRec = FindTask(TaskName); + if(TRec == PNULL) + { + //throw("Task Does Not Exist"); + return static_cast(Unknown); + } + + return TRec->State; +} + +/*============================================================================== + * TaskState: Set the state of the task + *============================================================================*/ +unsigned AddressBook::TaskState(std::string &TaskName, TaskState_t State) +{ + TaskRecord_t *TRec = FindTask(TaskName); + if(TRec == PNULL) + { + //throw("Task Does Not Exist"); + return ERR_TASK_NOT_FOUND; + } + + TRec->State = State; + + return SUCCESS; +} + +/*============================================================================== + * TaskExecPath: Get the path to the executables for the task + *============================================================================*/ +std::string AddressBook::TaskExecPath(std::string &TaskName) +{ + TaskRecord_t *TRec = FindTask(TaskName); + if(TRec == PNULL) + { + //throw("Task Does Not Exist"); + return ""; + } + + return TRec->ExecPath; +} + +/*============================================================================== + * TaskExecPath: Set the path to the executables for the task + *============================================================================*/ +unsigned AddressBook::TaskExecPath(std::string &TaskName, std::string &NewPath) +{ + TaskRecord_t *TRec = FindTask(TaskName); + if(TRec == PNULL) + { + ERETURN("Task Does Not Exist", ERR_TASK_NOT_FOUND); + } + + TRec->ExecPath = NewPath; + return SUCCESS; +} + +/*============================================================================== + * TaskValid: Indicates whether the Task's data is valid + *============================================================================*/ +bool AddressBook::TaskValid(std::string &TaskName) +{ + TaskRecord_t *TRec = FindTask(TaskName); + if(TRec == PNULL) + { + ERETURN("Task Does Not Exist", false); + } + + return TRec->TaskValid; +} + +/*============================================================================== + * TaskMapValid: Indicates whether the Task's mappings are valid + *============================================================================*/ +bool AddressBook::TaskMapValid(std::string &TaskName) +{ + TaskRecord_t *TRec = FindTask(TaskName); + if(TRec == PNULL) + { + ERETURN("Task Does Not Exist", false); + } + + return TRec->MapValid; +} + +/*============================================================================== + * TaskLinkValid: Indicates whether the Task's linkage is valid + *============================================================================*/ +bool AddressBook::TaskLinkValid(std::string &TaskName) +{ + TaskRecord_t *TRec = FindTask(TaskName); + if(TRec == PNULL) + { + ERETURN("Task Does Not Exist", false); + } + + return TRec->LinkValid; +} + + +/*============================================================================== + * RebuildTask: Rebuild the Task's MessageType/DeviceType maps + *============================================================================*/ +unsigned AddressBook::RebuildTask(std::string &TaskName) +{ + TaskRecord_t *TRec = FindTask(TaskName); + if(TRec == PNULL) + { + ERETURN("Task Does Not Exist", ERR_TASK_NOT_FOUND); + } + + //TODO: test this + + // Rebuild MessageType map + TRec->MsgTypeMap.clear(); + for(std::vector::iterator M=TRec->MsgTypes.begin(); + M!=TRec->MsgTypes.end(); M++) + { + M->Inputs.clear(); // Clear the Input vector data. + M->Outputs.clear(); // Clear the Output vector data. + TRec->MsgTypeMap.insert(IdxMap_t::value_type(M->Name, (M-TRec->MsgTypes.begin()))); + } + + //Rebuild DeviceType Map and links to MessageType - leave the contents of the Device Vector alone! + TRec->DevTypeMap.clear(); + for(std::vector::iterator D=TRec->DevTypes.begin(); + D!=TRec->DevTypes.end(); D++) + { + TRec->DevTypeMap.insert(IdxMap_t::value_type(D->first.Name, (D-TRec->DevTypes.begin()))); + + // Cross-ref to Message Types. + for(std::vector::iterator I=D->first.InMsgs.begin(); + I!=D->first.InMsgs.end(); I++) // Input Messages + { + TRec->MsgTypes[*I].Inputs.push_back(static_cast(D-TRec->DevTypes.begin())); + } + for(std::vector::iterator O=D->first.OuMsgs.begin(); + O!=D->first.OuMsgs.end(); O++) // Output Messages + { + TRec->MsgTypes[*O].Outputs.push_back(static_cast(D-TRec->DevTypes.begin())); + } + } + + //Rebuild AttributeType map - leave the contents of the Device Vector alone! + TRec->AttrTypeMap.clear(); + for(std::vector::iterator A=TRec->AttrTypes.begin(); + A!=TRec->AttrTypes.end(); A++) + { + TRec->AttrTypeMap.insert(IdxMap_t::value_type(A->first, (A-TRec->AttrTypes.begin()))); + } + + return SUCCESS; +} + +/*============================================================================== + * BuildMaps: Build Task Maps + *============================================================================*/ +unsigned AddressBook::BuildMaps(std::string &TaskName) +{ + TaskRecord_t *TRec = FindTask(TaskName); + if(TRec == PNULL) + { + ERETURN("Task Does Not Exist", ERR_TASK_NOT_FOUND); + } + + if(TRec->MapValid || TRec->NameMap.size() > 0 + || TRec->ExtCon.size() > 0) ClearMap(TRec); // Existing map, clear it + + + // Map all of the Devices + for(std::vector::iterator DRec=TRec->Devices.begin(); + DRec!=TRec->Devices.end(); DRec++) + { + MapDevice(TRec, &(*DRec)); + } + + // Map all of the Externals + for(std::vector::iterator ERec=TRec->Externals.begin(); + ERec!=TRec->Externals.end(); ERec++) + { + MapDevice(TRec, &(*ERec)); + } + + // Map all of the Supervisors + for(std::vector::iterator SRec=TRec->Supervisors.begin(); + SRec!=TRec->Supervisors.end(); SRec++) + { + // Add the supervisor to the correct DeviceType list. + TRec->DevTypes[SRec->DeviceType].second.push_back(&(*SRec)); + } + + TRec->MapValid = true; // added 18 July 2019 ADR update the map status + + return SUCCESS; +} + +/*============================================================================== + * BuildLink: Build Task linkage + *============================================================================*/ +unsigned AddressBook::BuildLink(std::string &TaskName) +{ + TaskRecord_t *TRec = FindTask(TaskName); + if(TRec == PNULL) + { + ERETURN("Task Does Not Exist", ERR_TASK_NOT_FOUND); + } + + if(TRec->LinkValid || TRec->AddrMap.size() > 0) ClearLink(TRec); // Existing link, clear it + + // Link all of the Devices + for(std::vector::iterator DRec=TRec->Devices.begin(); + DRec!=TRec->Devices.end(); DRec++) + { + LinkDevice(TRec, &(*DRec)); + } + + // Link all of the Externals + for(std::vector::iterator ERec=TRec->Externals.begin(); + ERec!=TRec->Externals.end(); ERec++) + { + TRec->AddrMap.insert(AddrMap_t::value_type(ERec->Address, &(*ERec))); + } + + // Link all of the Supervisors + for(std::vector::iterator SRec=TRec->Supervisors.begin(); + SRec!=TRec->Supervisors.end(); SRec++) + { + TRec->AddrMap.insert(AddrMap_t::value_type(SRec->Address, &(*SRec))); + } + + TRec->LinkValid = true; // added 18 July 2019 ADR update the link status + + return SUCCESS; +} + + +//============================================================================== +//Device Addition +unsigned AddressBook::AddDevice(std::string &TaskName, Record_t &DevRec, bool Validate) +{ + TaskRecord_t *TRec = FindTask(TaskName); + if(TRec == PNULL) + { + // DebugPrint("No such task: %s\n", TaskName.c_str()); + ERETURN("Task Does Not Exist", ERR_TASK_NOT_FOUND); + } + + Record_t *DRec; + + if (Validate) // Check that the device does not already exist + { // and that the indices are valid. + unsigned Ret = ValidateDevice(TRec, DevRec); + // DebugPrint("Device validation %s\n", (Ret ? "failed" : "succeeded")); + if(Ret) return Ret; + } + + if ((DevRec.RecordType == Device) || (DevRec.RecordType == DeviceExt)) + { + // Add a Device + // DebugPrint("Ordinary device being added\n"); + CheckVector(TRec, TRec->Devices); // Sanity check for reallocation. + + TRec->Devices.push_back(DevRec); // Add Dev to task. + TRec->DevCnt++; + DRec = &TRec->Devices.back(); + + LinkDevice(TRec, DRec); // Add Device to name map & supervisor list. + MapDevice(TRec, DRec); // Add dev to other maps & lists. + + } else if (DevRec.RecordType == External) { + // Add an External + // DebugPrint("External device being added\n"); + CheckVector(TRec, TRec->Externals); // Sanity check for reallocation + + TRec->Externals.push_back(DevRec); + TRec->ExtCnt++; + DRec = &TRec->Externals.back(); + + TRec->AddrMap.insert(AddrMap_t::value_type(DRec->Address, DRec)); + MapDevice(TRec, DRec); // Add dev to other maps & lists. + + } else if (DevRec.RecordType == Supervisor) { + // Add a Supervisor + // DebugPrint("Supervisor device being added\n"); + CheckVector(TRec, TRec->Supervisors); // Sanity check for reallocation. + + TRec->Supervisors.push_back(DevRec); + TRec->SupCnt++; + DRec = &TRec->Supervisors.back(); + + TRec->AddrMap.insert(AddrMap_t::value_type(DRec->Address, DRec)); + + // Add the supervisor to the correct DeviceType list. + TRec->DevTypes[DRec->DeviceType].second.push_back(DRec); + + SupMap_t::iterator SSearch = TRec->SupMap.find(DRec->Address); + if (SSearch == TRec->SupMap.end()) + { //Supervisor is NOT in the map yet. + TRec->SupMap.insert(SupMap_t::value_type(DRec->Address, + RecordVect_t())); + } + } + // added 18 July 2019 ADR - if Map and Link were previously invalidated + // rebuild everything + unsigned err; + if (!TRec->MapValid && ((err = BuildMaps(TRec->Name)) != SUCCESS)) return err; + if (!TRec->LinkValid && ((err = BuildLink(TRec->Name)) != SUCCESS)) return err; + return SUCCESS; +} + + +/*============================================================================== + * UpdateDevice: Update a device with a new address and supervisor. + * This implicitly marks the Linkage as invalid. + *============================================================================*/ +unsigned AddressBook::UpdateDevice(std::string &TaskName, DeviceData_t &Data) +{ + TaskRecord_t *TRec = FindTask(TaskName); + if(TRec == PNULL) + { + ERETURN("Task Does Not Exist", ERR_TASK_NOT_FOUND); + } + + // Check that the Device Name exists + NameMap_t::iterator DSearch = TRec->NameMap.find(Data.Name); + if (DSearch == TRec->NameMap.end()) { + return ERR_DEVICE_NOT_FOUND; + } + Record_t *DRec = DSearch->second; + + TRec->LinkValid = false; // Mark the Task linkage as dirty + DRec->Address = Data.Address; // Update the Device Address + DRec->Supervisor = Data.Supervisor; // Update the Device Supervisor + return SUCCESS; +} + +/*============================================================================== + * GetDeviceCount: Get the number of devices reserved in the named task + *============================================================================*/ +long AddressBook::GetDeviceCount(std::string &TaskName) +{ + TaskRecord_t *TRec = FindTask(TaskName); + if(TRec == PNULL) + { + ERETURN("Task Does Not Exist", ERR_BAD_COUNT); + } + + return static_cast(TRec->DevCntMax); +} + +/*============================================================================== + * GetLoadedDeviceCount: Get the number of devices loaded in the named task + *============================================================================*/ +long AddressBook::GetLoadedDeviceCount(std::string &TaskName) +{ + TaskRecord_t *TRec = FindTask(TaskName); + if(TRec == PNULL) + { + ERETURN("Task Does Not Exist", ERR_BAD_COUNT); + } + + return static_cast(TRec->DevCnt); +} + +/*============================================================================== + * GetExternalCount: Get the number of externals reserved in the named task + *============================================================================*/ +long AddressBook::GetExternalCount(std::string &TaskName) +{ + TaskRecord_t *TRec = FindTask(TaskName); + if(TRec == PNULL) + { + ERETURN("Task Does Not Exist", ERR_BAD_COUNT); + } + + return static_cast(TRec->ExtCntMax); +} + +/*============================================================================== + * GetLoadedExternalCount: Get the number of externals loaded in the named task + *============================================================================*/ +long AddressBook::GetLoadedExternalCount(std::string &TaskName) +{ + TaskRecord_t *TRec = FindTask(TaskName); + if(TRec == PNULL) + { + ERETURN("Task Does Not Exist", ERR_BAD_COUNT); + } + + return static_cast(TRec->ExtCnt); +} + +/*============================================================================== + * GetSupervisorCount: Get the number of supervisors allocated to the task + *============================================================================*/ +int AddressBook::GetSupervisorCount(std::string &TaskName) +{ + TaskRecord_t *TRec = FindTask(TaskName); + if(TRec == PNULL) + { + ERETURN("Task Does Not Exist", ERR_BAD_COUNT); + } + + return static_cast(TRec->SupCnt); +} + + +//============================================================================== +//Device Queries + +/*============================================================================== + * FindDevice: Find a device by Address. + * Gives a Pointer to Const Device Record + *============================================================================*/ +unsigned AddressBook::FindDevice(std::string &TaskName, SymAddr_t Address, const Record_t* &DRec) +{ + TaskRecord_t *TRec = FindTask(TaskName); + if(TRec == PNULL) + { + ERETURN("Task Does Not Exist", ERR_TASK_NOT_FOUND); + } + + if(!(TRec->TaskValid && TRec->MapValid && TRec->LinkValid)) // Return NotFound if dirty + { + return ERR_INVALID_MAP; + } + + AddrMap_t::iterator DSearch = TRec->AddrMap.find(Address); // Find the Device + if (DSearch == TRec->AddrMap.end()) { + ERETURN("Device Not Found", ERR_DEVICE_NOT_FOUND); + } + + DRec = &(*DSearch->second); + return SUCCESS; +} + + +/*============================================================================== + * FindDevice: Find a device by Name. + * Gives a Pointer to Const Device Record + *============================================================================*/ +unsigned AddressBook::FindDevice(std::string &TaskName, std::string &Name, const Record_t* &DRec) +{ + TaskRecord_t *TRec = FindTask(TaskName); + if(TRec == PNULL) + { + ERETURN("Task Does Not Exist", ERR_TASK_NOT_FOUND); + } + + if(!(TRec->TaskValid && TRec->MapValid && TRec->LinkValid)) // Return NotFound if dirty + { + return ERR_INVALID_MAP; + } + + NameMap_t::iterator DSearch = TRec->NameMap.find(Name); // Find the Device + if (DSearch == TRec->NameMap.end()) { + ERETURN("Device Not Found", ERR_DEVICE_NOT_FOUND); + } + + DRec = &(*DSearch->second); + return SUCCESS; +} + + +/*============================================================================== + * FindBySuper: Find a vector of devices supervised by Supervisor. + * Gives a Pointer to a const Vector of Pointers to const Device + * Records. + *============================================================================*/ +unsigned AddressBook::FindBySuper(std::string &TaskName, SymAddr_t Supervisor, const RecordVect_t* &Records) +{ + TaskRecord_t *TRec = FindTask(TaskName); + if(TRec == PNULL) + { + ERETURN("Task Does Not Exist", ERR_TASK_NOT_FOUND); + } + + if(!(TRec->TaskValid && TRec->MapValid && TRec->LinkValid)) // Return NotFound if dirty + { + return ERR_INVALID_MAP; + } + + SupMap_t::iterator SSearch = TRec->SupMap.find(Supervisor); // Find the Supervisor + if (SSearch == TRec->SupMap.end()) { + ERETURN("Device Not Found", ERR_DEVICE_NOT_FOUND); + } + + Records = &SSearch->second; + return SUCCESS; +} + +unsigned AddressBook::FindByType(std::string &TaskName, std::string Type, const RecordVect_t* &Records) +{ + TaskRecord_t *TRec = FindTask(TaskName); + if(TRec == PNULL) + { + ERETURN("Task Does Not Exist", ERR_TASK_NOT_FOUND); + } + + if(!(TRec->TaskValid && TRec->MapValid && TRec->LinkValid)) // Return NotFound if dirty + { + return ERR_INVALID_MAP; + } + + IdxMap_t::iterator TSearch = TRec->DevTypeMap.find(Type); // Find the Index + if (TSearch == TRec->DevTypeMap.end()) { + ERETURN("Device Type Not Found", ERR_DEVICE_NOT_FOUND); + } + + Records = &(TRec->DevTypes[TSearch->second].second); + + return SUCCESS; +} + +unsigned AddressBook::FindByAttribute(std::string &TaskName, std::string Attribute, const RecordVect_t* &Records) +{ + TaskRecord_t *TRec = FindTask(TaskName); + if(TRec == PNULL) + { + ERETURN("Task Does Not Exist", ERR_TASK_NOT_FOUND); + } + + if(!(TRec->TaskValid && TRec->MapValid && TRec->LinkValid)) // Return NotFound if dirty + { + return ERR_INVALID_MAP; + } + + IdxMap_t::iterator ASearch = TRec->AttrTypeMap.find(Attribute); // Find the Index + if (ASearch == TRec->AttrTypeMap.end()) { + ERETURN("Device Type Not Found", ERR_DEVICE_NOT_FOUND); + } + + Records = &(TRec->AttrTypes[ASearch->second].second); + + return SUCCESS; +} + +unsigned AddressBook::FindByInMsg(std::string &TaskName, std::string Msg, const RecordVect_t* &Records) +{ + TaskRecord_t *TRec = FindTask(TaskName); + if(TRec == PNULL) + { + ERETURN("Task Does Not Exist", ERR_TASK_NOT_FOUND); + } + + if(!(TRec->TaskValid && TRec->MapValid && TRec->LinkValid)) // Return NotFound if dirty + { + return ERR_INVALID_MAP; + } + + return SUCCESS; +} + +unsigned AddressBook::FindByOuMsg(std::string &TaskName, std::string Msg, const RecordVect_t* &Records) +{ + TaskRecord_t *TRec = FindTask(TaskName); + if(TRec == PNULL) + { + ERETURN("Task Does Not Exist", ERR_TASK_NOT_FOUND); + } + + if(!(TRec->TaskValid && TRec->MapValid && TRec->LinkValid)) // Return NotFound if dirty + { + return ERR_INVALID_MAP; + } + + return SUCCESS; +} + +unsigned AddressBook::GetDevices(std::string &TaskName, const std::vector* &Records) +{ + TaskRecord_t *TRec = FindTask(TaskName); + if(TRec == PNULL) + { + ERETURN("Task Does Not Exist", ERR_TASK_NOT_FOUND); + } + + if(!(TRec->TaskValid && TRec->MapValid && TRec->LinkValid)) // Return NotFound if dirty + { + return ERR_INVALID_MAP; + } + + Records = &(TRec->Devices); + + return SUCCESS; +} + +unsigned AddressBook::GetExternals(std::string &TaskName, const std::vector* &Records) +{ + TaskRecord_t *TRec = FindTask(TaskName); + if(TRec == PNULL) + { + ERETURN("Task Does Not Exist", ERR_TASK_NOT_FOUND); + } + + if(!(TRec->TaskValid && TRec->MapValid && TRec->LinkValid)) // Return NotFound if dirty + { + return ERR_INVALID_MAP; + } + + Records = &(TRec->Externals); + + return SUCCESS; +} + +unsigned AddressBook::GetSupervisors(std::string &TaskName, const std::vector* &Records) +{ + TaskRecord_t *TRec = FindTask(TaskName); + if(TRec == PNULL) + { + ERETURN("Task Does Not Exist", ERR_TASK_NOT_FOUND); + } + + if(!(TRec->TaskValid && TRec->MapValid && TRec->LinkValid)) // Return NotFound if dirty + { + return ERR_INVALID_MAP; + } + + Records = &(TRec->Supervisors); + + return SUCCESS; +} + +unsigned AddressBook::GetExtCon(std::string &TaskName, const RecordVect_t* &Records) +{ + TaskRecord_t *TRec = FindTask(TaskName); + if(TRec == PNULL) + { + ERETURN("Task Does Not Exist", ERR_TASK_NOT_FOUND); + } + + if(!(TRec->TaskValid && TRec->MapValid && TRec->LinkValid)) // Return NotFound if dirty + { + return ERR_INVALID_MAP; + } + + Records = &(TRec->ExtCon); + + return SUCCESS; +} + + + + +//============================================================================== +//Private Methods + +/*============================================================================== + * FindTask: Return a pointer to the named task. + *============================================================================*/ +TaskRecord_t * AddressBook::FindTask(const std::string &TaskName) +{ + // Check that the task exists. + TaskMap_t::iterator TSearch = TaskMap.find(TaskName); + if (TSearch == TaskMap.end()) { + ERETURN("Task Does Not Exist", PNULL); + } + + return TSearch->second; +} + + +unsigned AddressBook::MapDevice(TaskRecord_t *TRec, Record_t *DRec) +{ + // Add device to name map + TRec->NameMap.insert(NameMap_t::value_type(DRec->Name, DRec)); + + // Add the device to the correct DeviceType list. + TRec->DevTypes[DRec->DeviceType].second.push_back(DRec); + + if(DRec->RecordType == DeviceExt) // Device has an external conn + { + TRec->ExtCon.push_back(DRec); // Add it to the list of such devices. + } + + // Add the device to the correct attribute list + if(DRec->Attribute > -1) + { + TRec->AttrTypes[static_cast(DRec->Attribute)].second.push_back(DRec); + } + + return SUCCESS; +} + +unsigned AddressBook::LinkDevice(TaskRecord_t *TRec, Record_t *DRec) +{ + // Add Device to Address map. + TRec->AddrMap.insert(AddrMap_t::value_type(DRec->Address, DRec)); + + // Add the device to the correct Supervisor list. + // Supervisors may be loaded after devices, may need to build the map. + SupMap_t::iterator SSearch = TRec->SupMap.find(DRec->Supervisor); + if (SSearch == TRec->SupMap.end()) { //Supervisor is NOT in the map yet. + std::pair LastVal; // Have to do this because C++98 does not like initialiser lists... + LastVal = TRec->SupMap.insert(SupMap_t::value_type(DRec->Supervisor, RecordVect_t())); + LastVal.first->second.push_back(DRec); // RecordVect_t({DRec}) in the insert would be neater and not need the pair. + + } else { //Supervisor is already in the map. + SSearch->second.push_back(DRec); + } + + return SUCCESS; +} + + +/*============================================================================== + * ValidateTask: Check that the task has a valid vector of DeviceTypes. + *============================================================================*/ +unsigned AddressBook::ValidateTask(TaskData_t &Data) +{ + for(std::vector::const_iterator D=Data.DeviceTypes.begin(); + D!=Data.DeviceTypes.end(); D++) + { + if (unsigned retVal = ValidateDeviceType(*D, Data.MessageTypes.size())) + return retVal; + } + + return SUCCESS; +} + +/*============================================================================== + * ValidateDeviceType: Check that DeviceType Message indices are valid and not + * out of range. + * Added and separated from ValidateTask 5 July 2019 ADR + *============================================================================*/ +unsigned AddressBook::ValidateDeviceType(const DevTypeRecord_t& DevTypRec, unsigned MaxIdx) +{ + for(std::vector::const_iterator I=DevTypRec.InMsgs.begin(); + I!=DevTypRec.InMsgs.end(); I++) + { + if(*I >= MaxIdx) + { + ERETURN("Invalid MessageType Index", ERR_INVALID_MESSAGE_TYPE); + } + } + + // changed to iterate over OuMsgs (was InMsgs) 5 July 2019 ADR + for(std::vector::const_iterator O=DevTypRec.OuMsgs.begin(); + O!=DevTypRec.OuMsgs.end(); O++) + { + if(*O >= MaxIdx) + { + ERETURN("Invalid MessageType Index", ERR_INVALID_MESSAGE_TYPE); + } + } + return 0; +} + + +/*============================================================================== + * ValidateDevice: Check that the device does not already exist in the name map + * and that indices in the Record are not out of range. + *============================================================================*/ +unsigned AddressBook::ValidateDevice(TaskRecord_t *TRec, Record_t &DevRec) +{ + // Check that the device does not already exist in the maps + AddrMap_t::const_iterator DASearch = TRec->AddrMap.find(DevRec.Address); + if (DASearch != TRec->AddrMap.end()) { + // DebugPrint("Device address %llu already exists\n", DevRec.Address); + ERETURN("DUPADDR: Device Address already added", ERR_DEVICE_ADDR_USED); + } + + NameMap_t::const_iterator DNSearch = TRec->NameMap.find(DevRec.Name); + if (DNSearch != TRec->NameMap.end()) { + // DebugPrint("Device name %s already exists\n", DevRec.Name.c_str()); + ERETURN("DUPNAME: Device Name already added", ERR_DEVICENAME_USED); + } + + // Check that the indices are valid BEFORE adding the Device. + if (DevRec.DeviceType >= TRec->DevTypes.size()) { + // DebugPrint("Device type %u does not exist\n", DevRec.DeviceType); + ERETURN("Invalid DeviceType Index", ERR_INVALID_DEVTYPE); + } + + if (DevRec.Attribute >= static_cast(TRec->AttrTypes.size())) { + // DebugPrint("Device attribute type %u does not exist\n", DevRec.Attribute); + ERETURN("Invalid AttributeType Index", ERR_INVALID_ATTRIBUTE); + } + + return SUCCESS; // Validation Successful +} + +/*============================================================================== + * CheckVector: Check that adding a Record to the vector won't cause a + * reallocation as this would silently invalidate all of the maps. + * If the vector is at capacity, increase it's size, clear the task + * linkage & mappings and mark both as dirty. + * + * As the Record vectors are pre-sized based on what should be in + * the task plus one element, there should never be a reallocation. + *============================================================================*/ +unsigned AddressBook::CheckVector(TaskRecord_t *TRec, std::vector &Vect) +{ + if(Vect.size() == Vect.capacity()) + { + Vect.reserve(static_cast(Vect.capacity()*1.25)); + ClearLink(TRec); + ClearMap(TRec); + return ERR_INVALID_MAP; + } + return SUCCESS; +} + + +/*============================================================================== + * ClearMap: Clear the Device to DeviceType/Attribute/(Message) Type links + * and mark the linkage as dirty. Also clears the Name>Record map + * and list of records with external connections. + *============================================================================*/ +unsigned AddressBook::ClearMap(TaskRecord_t *TRec) +{ + TRec->MapValid = false; // Mark the map as dirty. + TRec->NameMap.clear(); // Clear the Name map + TRec->ExtCon.clear(); // Clear External Connections List. + + // Clear DevType->DeviceRecord vectors + for(std::vector::iterator D=TRec->DevTypes.begin(); + D!=TRec->DevTypes.end();D++) + { + D->second.clear(); + } + + // Clear Attribute->DeviceRecord vectors + for(std::vector::iterator A=TRec->AttrTypes.begin(); + A!=TRec->AttrTypes.end();A++) + { + A->second.clear(); + } + + return SUCCESS; +} + + +/*============================================================================== + * ClearLink: Clear the Address>Record map and the Supervisor>Device links. + * Also marks the linkage as dirty. + *============================================================================*/ +unsigned AddressBook::ClearLink(TaskRecord_t *TRec) +{ + TRec->LinkValid = false; // Mark the linkage as dirty. + TRec->AddrMap.clear(); // Clear the Addres>DeviceRecord map. + + //Clear the Device Record vectors for each supervisor. + for(SupMap_t::iterator S=TRec->SupMap.begin();S!=TRec->SupMap.end();S++) + { + S->second.clear(); + } + + return SUCCESS; +} + + +//============================================================================== +//Integrity Checks +unsigned AddressBook::IntegCheck(void) +{ + std::vector Temp; + return IntegCheck(Temp); +} + +unsigned AddressBook::IntegCheck(std::vector &TaskIntegs) +{ + unsigned TInteg, Ret = 0; + + for(TaskMap_t::const_iterator T=TaskMap.begin(); + T!=TaskMap.end();T++) + { + TInteg = T->second->Integrity(); + TaskIntegs.push_back(TInteg); + if(TInteg) Ret++; + } + + return Ret; +} + +unsigned AddressBook::IntegTask(std::string &TaskName, bool Verbose, FILE * fp) +{ + TaskRecord_t *TRec = FindTask(TaskName); + if(TRec == PNULL) + { + ERETURN("Task Does Not Exist", ERR_TASK_NOT_FOUND); + } + + return TRec->Integrity(Verbose, fp); +} + + + +//============================================================================== +//Dumps + +void AddressBook::Dump(std::string &Name) +{ + Dump(stdout, Name); +} + +void AddressBook::Dump(FILE * fp, std::string Name) +{ + fprintf(fp,"AddressBook+++++++++++++++++++++++++++++++++\n"); + fprintf(fp,"ABderived (this derived process) : %s\n",ABderived.c_str()); + + if(Name == std::string("")) // Dump high-level task data. + { + fprintf(fp,"Total number of tasks: %d:\n", TaskCount); + for(TaskMap_t::iterator T=TaskMap.begin(); + T!=TaskMap.end();T++) + { + TaskRecord_t *TRec = T->second; + fprintf(fp,"Task %ld: Key: %s At: %" PTR_FMT "\n", + static_cast(std::distance(TaskMap.begin(), T)), + T->first.c_str(), reinterpret_cast(TRec)); + fprintf(fp,"\tName: %s\n", TRec->Name.c_str()); + fprintf(fp,"\tPath: %s\n", TRec->Path.c_str()); + fprintf(fp,"\tXML: %s\n", TRec->XML.c_str()); + fprintf(fp,"\tExecPath: %s\n", TRec->ExecPath.c_str()); + fprintf(fp,"\tExternal Count: %ld\n", TRec->ExtCntMax); + fprintf(fp,"\tDevice Count: %ld\n", TRec->DevCntMax); + fprintf(fp,"\tState: %d\n", TRec->State); + fprintf(fp,"\tAllocated Supervisors: %ld\n", TRec->SupCnt); + fprintf(fp,"\tLoaded Externals: %ld\n", TRec->ExtCnt); + fprintf(fp,"\tLoaded Devices: %ld\n", TRec->DevCnt); + } + } else { // Dump task-specific data. + // Check that the task exists. + TaskMap_t::const_iterator Search = TaskMap.find(Name); + if (Search == TaskMap.end()) { + fprintf(fp,"ERROR: Task %s not found.\n", Name.c_str()); + } else { + TaskRecord_t *TRec = Search->second; + + //Dump Task Data + fprintf(fp,"Task: %s at %" PTR_FMT "\n", Search->first.c_str(), reinterpret_cast(TRec)); + fprintf(fp,"\tPath: %s\n", TRec->Path.c_str()); + fprintf(fp,"\tExecPath: %s\n", TRec->ExecPath.c_str()); + fprintf(fp,"\tExternal Count: %ld\n", TRec->ExtCntMax); + fprintf(fp,"\tDevice Count: %ld\n", TRec->DevCntMax); + fprintf(fp,"\tState: %d\tTaskValid: %d\tMap Valid: %d\tLink Valid: %d\n", + TRec->State, TRec->TaskValid, TRec->MapValid, TRec->LinkValid); + + //Dump Message Types + fprintf(fp,"\tMessage Types:\t%lu\n", static_cast(TRec->MsgTypes.size())); + fprintf(fp,"\t\tIdx\tName\tMapIdx\tMapName\tDTypeI\tDTypeO\n"); + for(std::vector::const_iterator M + =TRec->MsgTypes.begin();M!=TRec->MsgTypes.end();M++) + { + fprintf(fp,"\t\t%ld\t%s\t", static_cast(M-TRec->MsgTypes.begin()), M->Name.c_str()); + + IdxMap_t::const_iterator MSearch = TRec->MsgTypeMap.find(M->Name); + if (MSearch == TRec->MsgTypeMap.end()) { + fprintf(fp,"Not Mapped\t"); + } else { + fprintf(fp,"%d\t%s\t", MSearch->second, + TRec->MsgTypes[MSearch->second].Name.c_str()); + } + + for(std::vector::const_iterator I= + M->Inputs.begin(); I!=M->Inputs.end(); I++) + { // Input Messages + fprintf(fp,"%d,",*I); + } + fprintf(fp,"\t"); + + for(std::vector::const_iterator O= + M->Outputs.begin();O!=M->Outputs.end(); O++) + { // Output Messages + fprintf(fp,"%d,",*O); + } + fprintf(fp,"\n"); + } + fprintf(fp,"\n"); + + //Dump Device Types + fprintf(fp,"\tDevice Types:\t%lu\n", static_cast(TRec->DevTypes.size())); + fprintf(fp,"\t\tIdx\tName\tMapIdx\tMapName\tDevCnt\tInMsgs\tOuMsgs\n"); + for(std::vector::const_iterator D + =TRec->DevTypes.begin();D!=TRec->DevTypes.end();D++) + { + fprintf(fp,"\t\t%ld\t%s\t", static_cast(D-TRec->DevTypes.begin()), D->first.Name.c_str()); + + //Print the DevTypeMap mapping + IdxMap_t::const_iterator DSearch = TRec->DevTypeMap.find(D->first.Name); + if (DSearch == TRec->DevTypeMap.end()) { + fprintf(fp,"Not Mapped\t"); + } else { + fprintf(fp,"%d\t%s\t", DSearch->second, + TRec->DevTypes[DSearch->second].first.Name.c_str()); + } + + //Print the number of devices of this type. + fprintf(fp,"%lu\t", static_cast(D->second.size())); + + //Print the Input Msg Indices + for(std::vector::const_iterator I + =D->first.InMsgs.begin();I!=D->first.InMsgs.end();I++) + { + fprintf(fp,"%d,", *I); + } + fprintf(fp,"\t"); + + //Print the Output Msg Indices + for(std::vector::const_iterator O + =D->first.OuMsgs.begin();O!=D->first.OuMsgs.end();O++) + { + fprintf(fp,"%d,", *O); + } + fprintf(fp,"\n"); + + } + fprintf(fp,"\n"); + + //Dump Attribute Types + fprintf(fp,"\tAttribute Types:\t%lu\n", static_cast(TRec->AttrTypes.size())); + if(TRec->AttrTypes.size()>0) + { + fprintf(fp,"\t\tIdx\tName\tMapIdx\tMapName\tDevCnt\n"); + for(std::vector::const_iterator A + =TRec->AttrTypes.begin();A!=TRec->AttrTypes.end();A++) + { + fprintf(fp,"\t\t%ld\t%s\t", static_cast(A-TRec->AttrTypes.begin()), + A->first.c_str()); + + IdxMap_t::const_iterator ASearch = TRec->AttrTypeMap.find(A->first); + if (ASearch == TRec->AttrTypeMap.end()) { + fprintf(fp,"Not Mapped\t"); + } else { + fprintf(fp,"%d\t%s\t", ASearch->second, + TRec->AttrTypes[ASearch->second].first.c_str()); + } + + //Print the number of devices with this attribute. + fprintf(fp,"%ld\n", static_cast(A->second.size())); + } + fprintf(fp,"\n"); + } + + // Dump allocated Supervisors + fprintf(fp,"\tAllocated Supervisors: %ld\n", TRec->SupCnt); + if(TRec->SupCnt>0) + { + fprintf(fp,"\t\tIdx\tAddress\tRank\tDev Count\n"); + for(std::vector::const_iterator R + =TRec->Supervisors.begin();R!=TRec->Supervisors.end();R++) + { + //TODO: Write the map search to get count of devices for + // a supervisor. + fprintf(fp,"\t\t%ld\t%" SYMA_FMT "\t%lu\t%d\n", + static_cast(R-TRec->Supervisors.begin()), + R->Address, R->Rank, 0); + } + fprintf(fp,"\n"); + } + + // Dump loaded Externals + fprintf(fp,"\tLoaded Externals: %ld\n", TRec->ExtCnt); + if(TRec->ExtCnt>0) + { + fprintf(fp,"\t\tIdx\tName\tAddress\n"); + for(std::vector::const_iterator R + =TRec->Externals.begin();R!=TRec->Externals.end();R++) + { + fprintf(fp,"\t\t%ld\t%s\t%" SYMA_FMT "\n", + static_cast(R-TRec->Externals.begin()), + R->Name.c_str(), R->Address); + } + fprintf(fp,"\n"); + } + + + // Dump loaded Devices + fprintf(fp,"\tLoaded Devices: %ld\n", TRec->DevCnt); + if(TRec->DevCnt>0) + { + fprintf(fp,"\t\tIdx\tName\tAddress\t\tSupervisor\n"); + for(std::vector::const_iterator R + =TRec->Devices.begin();R!=TRec->Devices.end();R++) + { + fprintf(fp,"\t\t%ld\t%s\t%" SYMA_FMT "\t%" SYMA_FMT "\n", + static_cast(R-TRec->Devices.begin()), + R->Name.c_str(), R->Address, R->Supervisor); + } + } + } + } + + fprintf(fp,"AddressBook---------------------------------\n"); + fflush(fp); +} +} /* namespace AddressBookNS */ diff --git a/Source/NameServer/AddressBook.hpp b/Source/NameServer/AddressBook.hpp new file mode 100644 index 00000000..78a7d949 --- /dev/null +++ b/Source/NameServer/AddressBook.hpp @@ -0,0 +1,166 @@ +/*============================================================================== + * AddressBook: A back-end implementation for the POETS sbase & Nameserver + * + * Tasks can be *maped* and *linked*: + * Mapped: Device Records connected to Device (and Message) and Attribute + * types. Device Records with External Connections also listed. + * Linked: Device Records connected to Addresses in Address Map and listed + * in the relevant Supervisor vector. + * + * Linkage is invalidated when/if a Device Record is updated after load. + * Mapping is invalidated if more devices than expected are loaded. + * + * Struct sizing with no Pragma Packing: + * Win 64-Bit Win 32-Bit Mac 64-Bit + * TaskRecord_t 560 324 464 + * Record_t 72 64 52 + * TaskData_t 280 184 216 + * DeviceData_t 56 48 40 + * + * Memory Footprint* 221.7MB 153.2MB 194MB + * *1 task with 1 million devices, 1 supervisor and 1 external. + * + * + * + *============================================================================*/ + +#ifndef AddressBook_H +#define AddressBook_H + +#include "OSFixes.hpp" + +#include "ABDefs.hpp" +#include "ABTask.hpp" + +#include +#include +#include +#include +#include +#include +#include + + + +#define MAXSUPERVISORS 16 + +//============================================================================== +namespace AddressBookNS // changed name to avoid conflicts with the class itself + // 14 July 2019 ADR +{ +typedef std::map TaskMap_t; + +class AddressBook +{ +public: + AddressBook(std::string d); + ~AddressBook(); + + unsigned GetTaskCount(void); + + // Task Manipulation + unsigned AddTask(const std::string &TaskName, TaskData_t &Data); + unsigned ListTask(std::vector &Tasks); + unsigned GetTask(const std::string &TaskName, TaskData_t &Task); + unsigned DelTask(const std::string &TaskName); + + unsigned AddDeviceType(const std::string &TaskName, const DevTypeRecord_t &DeviceType); // add a new DeviceType + unsigned ClearTask(const std::string &TaskName); // deletes all the devices for a task without removing the task. + + TaskState_t TaskState(std::string &TaskName); // Get the task state + unsigned TaskState(std::string &TaskName, TaskState_t State); // Set the task state + + std::string TaskExecPath(std::string &TaskName); // Get the path of a task + unsigned TaskExecPath(std::string &TaskName, std::string &NewPath); // Set the path of a task + + bool TaskValid(std::string &TaskName); + bool TaskMapValid(std::string &TaskName); + bool TaskLinkValid(std::string &TaskName); + + unsigned RebuildTask(std::string &TaskName); + unsigned BuildMaps(std::string &TaskName); + unsigned BuildLink(std::string &TaskName); + + // Device Manipulation + unsigned AddDevice(std::string &TaskName, Record_t &Device, bool Validate = true); + unsigned UpdateDevice(std::string &TaskName, DeviceData_t &Data); + + // Find a device + unsigned FindDevice(std::string &TaskName, std::string &Name, const Record_t* &DRec); + unsigned FindDevice(std::string &TaskName, SymAddr_t Address, const Record_t* &DRec); + + // Find all devices supervised by X + unsigned FindBySuper(std::string &TaskName, SymAddr_t Supervisor, const RecordVect_t* &Records); + + unsigned FindByType(std::string &TaskName, std::string Type, const RecordVect_t* &Records); + unsigned FindByAttribute(std::string &TaskName, std::string Attribute, const RecordVect_t* &Records); + + unsigned FindByInMsg(std::string &TaskName, std::string Msg, const RecordVect_t* &Records); //TODO + unsigned FindByOuMsg(std::string &TaskName, std::string Msg, const RecordVect_t* &Records); //TODO + + // device getters. Note that a vector is different from a RecordVect_t, + // which is a vector + unsigned GetDevices(std::string &TaskName, const std::vector* &Records); + unsigned GetExternals(std::string &TaskName, const std::vector* &Records); + unsigned GetSupervisors(std::string &TaskName, const std::vector* &Records); + unsigned GetExtCon(std::string &TaskName, const RecordVect_t* &Records); + + // Device Count getters + long GetDeviceCount(std::string &TaskName); + long GetLoadedDeviceCount(std::string &TaskName); + long GetExternalCount(std::string &TaskName); + long GetLoadedExternalCount(std::string &TaskName); + int GetSupervisorCount(std::string &TaskName); + + // Integrity + unsigned IntegCheck(void); // Integrity check of ALL tasks + unsigned IntegCheck(std::vector &TaskIntegs); + unsigned IntegTask(std::string &TaskName, bool Verbose = false, FILE * = stdout); // Ingerity Check a Task by name + //unsigned IntegTask(const TaskRecord_t *TRec, bool Verbose = false, FILE * = stdout); // Ingerity Check a Task by pointer + + // Debugging + void Dump(std::string &Name); + void Dump(FILE * = stdout, std::string Name = ""); + + static const long ERR_BAD_COUNT = -1; + + static const unsigned SUCCESS = 0; + static const unsigned ERR_NONFATAL = 0; + static const unsigned ERR_INVALID_TASK = 1; + static const unsigned ERR_INVALID_DEVTYPE = 2; + static const unsigned ERR_INVALID_DEVICE = 3; + static const unsigned ERR_INVALID_MESSAGE_TYPE = 4; + static const unsigned ERR_INVALID_ATTRIBUTE = 5; + static const unsigned ERR_DEVICE_DATA_MISMATCH = 6; + static const unsigned ERR_TASKNAME_USED = 7; + static const unsigned ERR_DEVICENAME_USED = 8; + static const unsigned ERR_DEVICE_ADDR_USED = 9; + static const unsigned ERR_TASK_NOT_FOUND = 10; + static const unsigned ERR_DEVICE_NOT_FOUND = 11; + static const unsigned ERR_INVALID_MAP = 12; + static const unsigned ERR_INVALID_SUPERVISOR = 13; + static const unsigned ERR_INVALID_STATE = 14; + +private: + TaskMap_t TaskMap; + int TaskCount; + std::string ABderived; // changed to allow multiple inheritance (from CommonBase) 14 July 2019 ADR + + TaskRecord_t * FindTask(const std::string &TaskName); + + unsigned ValidateDevice(TaskRecord_t *TRec, Record_t &DevRec); + unsigned ValidateDeviceType(const DevTypeRecord_t& DevTypRec, unsigned MaxIdx); + unsigned ValidateTask(TaskData_t &Data); + + unsigned CheckVector(TaskRecord_t *TRec, std::vector &Vect); + + unsigned LinkDevice(TaskRecord_t *TRec, Record_t *DRec); // Add Device to name map & supervisor list. + unsigned MapDevice(TaskRecord_t *TRec, Record_t *DRec); // Add Device to other maps & lists. + + unsigned ClearMap(TaskRecord_t *TRec); + unsigned ClearLink(TaskRecord_t *TRec); + +}; +} /* namespace AddressBookNS */ + +#endif /* AddressBook_H */ diff --git a/Source/NameServer/NameServer.cpp b/Source/NameServer/NameServer.cpp index e577ad56..c905af57 100644 --- a/Source/NameServer/NameServer.cpp +++ b/Source/NameServer/NameServer.cpp @@ -1,130 +1,404 @@ //------------------------------------------------------------------------------ #include "NameServer.h" -#include "CommonBase.h" #include "PMsg_p.hpp" #include "mpi.h" #include "Pglobals.h" -#include "Ns_el.h" -#include "jnj.h" +// #include "jnj.h" +#include "CMsg_p.h" #include //============================================================================== NameServer::NameServer(int argc,char * argv[],string d): - CommonBase(argc,argv,d,string(__FILE__)) + SBase(argc,argv,d,string(__FILE__)) { + FnMapx.push_back(new FnMap_t); // create a default function table // Load the message map -FnMapx[PMsg_p::KEY(Q::CANDC,Q::CLEAR)] = &NameServer::OnClr; -FnMapx[PMsg_p::KEY(Q::CANDC,Q::DUMP )] = &NameServer::OnDump; -FnMapx[PMsg_p::KEY(Q::TASK, Q::FWD )] = &NameServer::OnFwd; -FnMapx[PMsg_p::KEY(Q::CANDC,Q::LOAD )] = &NameServer::OnLoad; -FnMapx[PMsg_p::KEY(Q::CANDC,Q::LOG )] = &NameServer::OnLog; -FnMapx[PMsg_p::KEY(Q::CANDC,Q::MONI )] = &NameServer::OnMoni; -FnMapx[PMsg_p::KEY(Q::TASK, Q::QUERY)] = &NameServer::OnQuery; - -MPISpinner(); // Spin on MPI messages; exit only on DIE - -//printf("********* NameServer rank %d on the way out\n",Urank); fflush(stdout); + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::CFG,Q::DIST )] = &NameServer::OnCfg; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::CFG,Q::TDIR )] = &NameServer::OnCfg; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::CFG,Q::BLD )] = &NameServer::OnCfg; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::CFG,Q::RECL )] = &NameServer::OnCfg; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::CFG,Q::DEL )] = &NameServer::OnCfg; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::CFG,Q::STATE )] = &NameServer::OnCfg; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::DUMP,Q::TASK,Q::ALL)] = &NameServer::OnDump; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::DUMP,Q::TASK,Q::NM )] = &NameServer::OnDump; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::DUMP,Q::LIST )] = &NameServer::OnDump; + + MPISpinner(); // Spin on MPI messages; exit only on DIE + + printf("********* NameServer rank %d on the way out\n",Urank); fflush(stdout); } //------------------------------------------------------------------------------ NameServer::~NameServer() { -//printf("********* NameServer rank %d destructor\n",Urank); fflush(stdout); + WALKVECTOR(FnMap_t*,FnMapx,F) // WALKVECTOR and WALKMAP are in macros.h (long include chain) + delete *F; // get rid of derived class function tables + printf("********* NameServer rank %d destructor\n",Urank); fflush(stdout); } //------------------------------------------------------------------------------ -void NameServer::Dump(FILE * fp) +unsigned NameServer::Connect(string svc) { -fprintf(fp,"NameServer dump+++++++++++++++++++++++++++++++++++\n"); - -fprintf(fp,"NameServer dump-----------------------------------\n"); -CommonBase::Dump(fp); + unsigned connErr = MPI_SUCCESS; + // set up the connection in the base class + if ((connErr = SBase::Connect(svc)) != MPI_SUCCESS) return connErr; + FnMapx.push_back(new FnMap_t); // add another function table in the derived class + int fIdx=FnMapx.size()-1; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::CFG,Q::DIST )] = &NameServer::OnCfg; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::CFG,Q::TDIR )] = &NameServer::OnCfg; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::CFG,Q::BLD )] = &NameServer::OnCfg; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::CFG,Q::RECL )] = &NameServer::OnCfg; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::CFG,Q::DEL )] = &NameServer::OnCfg; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::CFG,Q::STATE )] = &NameServer::OnCfg; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::DUMP,Q::TASK,Q::ALL)] = &NameServer::OnDump; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::DUMP,Q::TASK,Q::NM )] = &NameServer::OnDump; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::DUMP,Q::LIST )] = &NameServer::OnDump; + return MPI_SUCCESS; } //------------------------------------------------------------------------------ -void NameServer::Load1(Ns_0 * p0) +void NameServer::Dump(FILE * fp, string task) { -static unsigned cnt=0; -printf("\n********** %u\n\n",cnt++); -p0->Dump(); +if (task.empty()) +{ + fprintf(fp,"NameServer summary dump+++++++++++++++++++++++++++\n"); + SBase::Dump(fp); + fprintf(fp,"NameServer summary dump---------------------------\n"); } - -//------------------------------------------------------------------------------ - -unsigned NameServer::OnClr(PMsg_p *) +else { - - -return 0; + fprintf(fp,"NameServer dump+++++++++++++++++++++++++++++++++++\n"); + SBase::Dump(fp,task); + fprintf(fp,"NameServer dump-----------------------------------\n"); +} +CommonBase::Dump(fp); } //------------------------------------------------------------------------------ -unsigned NameServer::OnDump(PMsg_p *) +unsigned NameServer::OnCfg(PMsg_p * msg, unsigned comm) { - - -return 0; + switch(msg->L(2)) + { + case Q::DIST: + return ConfigDistribute(msg, comm); + case Q::TDIR: + return ConfigDir(msg, comm); + case Q::BLD: + return SBase::ConfigBuild(msg, comm); + case Q::RECL: + return ConfigRecall(msg, comm); + case Q::DEL: + return SBase::ConfigDelete(msg, comm); + case Q::STATE: + return ConfigState(msg, comm); + default: + Post(700,uint2str(msg->Key()),int2str(Urank)); + return 0; + } } //------------------------------------------------------------------------------ -unsigned NameServer::OnFwd(PMsg_p *) +unsigned NameServer::OnDump(PMsg_p *msg, unsigned comm) { - - -return 0; + if (msg->L(2) == Q::LIST) return DumpSummary(msg, comm); + if (msg->L(2) != Q::TASK) + { + Post(700,uint2str(msg->Key()),int2str(Urank)); + return 0; + } + switch(msg->L(3)) + { + case Q::ALL: + return DumpAll(msg, comm); + case Q::NM: + return DumpTask(msg, comm); + default: + Post(700,uint2str(msg->Key()),int2str(Urank)); + return 0; + } } //------------------------------------------------------------------------------ -unsigned NameServer::OnLoad(PMsg_p * pZ) -// Load some fraction of the NameServer datastructure with the entities supplied -// in the message. +unsigned NameServer::ConfigDir(PMsg_p * msg, unsigned comm) { -Ns_el * pN = new Ns_el(pZ); // Unwrap the message -int cnt=0; -unsigned * ploc = pN->Get(0,cnt);// Get hold of the key vector -if (ploc==0) Post(909); -vector * pV = pN->Construct(); // Expose the entities -WALKVECTOR(Ns_0 *,(*pV),i) Load1(*i); // Add to database -delete pN; -return 0; + string taskName = msg->Zname(0); + const vector* supervisors; + unsigned err = GetSupervisors(taskName, supervisors); + if (err == AddressBook::ERR_TASK_NOT_FOUND) + { + Post(724,taskName,int2str(Urank)); + return err; + } + if (err == AddressBook::ERR_INVALID_MAP) + { + Post(727,int2str(Urank)); + return ERR_NONFATAL; + } + // finding supervisors naively by rank starting with comm 0 will work for + // the moment but this should be fixed to permit arbitrary lookup by comm. + for (vector::const_iterator s = supervisors->begin(); s != supervisors->end(); s++) + { + for (unsigned sComm = 0; sComm < pPmap.size(); sComm++) + { + WALKVECTOR(int,pPmap[sComm]->U.Mothership,m) + { + if (*m == static_cast(s->Rank)) + { + msg->comm = Comms[sComm]; // relay the message to all supervisors + msg->Send(*m); // associated with this task. + break; + } + } + if (msg->Tgt() == static_cast(s->Rank)) break; // supervisor found + } + if (msg->Tgt() != static_cast(s->Rank)) // supervisor not found + { + Post(728,int2str(Urank),uint2str(s->Rank)); + return ERR_INVALID_SUPERVISOR; + } + } + return SBase::ConfigDir(msg,comm); } -//------------------------------------------------------------------------------ - -unsigned NameServer::OnLog(PMsg_p *) +unsigned NameServer::ConfigDistribute(PMsg_p * msg, unsigned comm) { - - -return 0; + string taskName = msg->Zname(0); + int rcount = 0; + // SymAddr_t superAddr = msg->Get(0,rcount); // use once symbolic addresses are implemented + int* superAddr = msg->Get(0,rcount); + if (!superAddr) + { + Post(730, "NAME::CFG::DIST",int2str(Urank),"Mothership Address"); + return ERR_NONFATAL; + } + const vector* supervisors; + unsigned err = GetSupervisors(taskName, supervisors); + if (err == AddressBook::ERR_TASK_NOT_FOUND) + { + Post(724,taskName,int2str(Urank)); + return err; + } + if (err == AddressBook::ERR_INVALID_MAP) + { + Post(727,int2str(Urank)); + return ERR_NONFATAL; + } + // finding supervisors naively by rank starting with comm 0 will work for + // the moment but this should be fixed to permit arbitrary lookup by comm. + for (vector::const_iterator s = supervisors->begin(); s != supervisors->end(); s++) + { + for (unsigned sComm = 0; sComm < pPmap.size(); sComm++) + { + WALKVECTOR(int,pPmap[sComm]->U.Mothership,m) + { + // if ((s->Address == *superAddr) && (*m == static_cast(s->Rank))) // SymAddr_t form + if ((static_cast(s->Rank) == *superAddr) && (*m == static_cast(s->Rank))) + { + DebugPrint("Deploying name data to Mothership at rank %d\n",*m); + // send over the task NameServer data + PMsg_p taskInfo(Comms[sComm]); + taskInfo.Src(Urank); + taskInfo.Key(Q::NAME,Q::DATA,Q::TASK); + TaskData_t taskData; + if ((err = GetTask(taskName,taskData))) + { + Post(724,taskName,int2str(Urank)); + return err; + } + vector devCounts; + devCounts.push_back(taskData.DeviceCount); + devCounts.push_back(taskData.ExternalCount); + taskInfo.Zname(0,taskData.Name); + taskInfo.Zname(1,taskData.Path); + taskInfo.Zname(2,taskData.XML); + taskInfo.PutX(0,&taskData.MessageTypes); + taskInfo.PutX(1,&taskData.AttributeTypes); + taskInfo.Put(2,&devCounts); + taskInfo.Send(*m); + taskInfo.Key(Q::NAME,Q::DATA,Q::DEVT); + WALKVECTOR(DevTypeRecord_t,taskData.DeviceTypes,devType) + { + taskInfo.Zname(2,devType->Name); + taskInfo.Put(0,&devType->InMsgs); + taskInfo.Put(1,&devType->OuMsgs); + taskInfo.Send(*m); + } + vectordevNames; + vectordevAddrs; + vectordevAttrs; + vector superRanks; + // send over the Supervisor device info + taskInfo.Key(Q::NAME,Q::DATA,Q::SUPV); + devNames.push_back(s->Name); + devAddrs.push_back(s->Address); + devAttrs.push_back(s->Attribute); + superRanks.push_back(s->Rank); + taskInfo.Zname(2,taskData.DeviceTypes[s->DeviceType].Name); + taskInfo.PutX(0,&devNames); + taskInfo.Put(1,&devAddrs); + taskInfo.Put(2,&devAttrs); + taskInfo.Put(3,&superRanks); + taskInfo.Send(*m); + devNames.clear(); + devAddrs.clear(); + devAttrs.clear(); + superRanks.clear(); + const RecordVect_t* devicesThisSuper; + if ((err = FindBySuper(taskName,s->Address,devicesThisSuper))) + { + switch (err) + { + case ERR_TASK_NOT_FOUND: + Post(724,taskName,int2str(Urank)); + return err; + case ERR_INVALID_MAP: + Post(727,int2str(Urank)); + return ERR_NONFATAL; + case ERR_DEVICE_NOT_FOUND: + Post(718,taskName,int2str(Urank)); + return err; + default: + Post(136); + return err; + } + } + if (devicesThisSuper->size()) + { + RecordType_t devRType((*devicesThisSuper->begin())->RecordType); + DTypeIdx devTypeIndex = (*devicesThisSuper->begin())->DeviceType; + taskInfo.Zname(2,taskData.DeviceTypes[devTypeIndex].Name); + switch(devRType) + { + case Device: + taskInfo.Key(Q::NAME,Q::DATA,Q::DEVI); + break; + case DeviceExt: + taskInfo.Key(Q::NAME,Q::DATA,Q::DEVE); + break; + case External: + taskInfo.Key(Q::NAME,Q::DATA,Q::EXTN); + break; + case Supervisor: + taskInfo.Key(Q::NAME,Q::DATA,Q::SUPV); + } + for (RecordVect_t::const_iterator dR = devicesThisSuper->begin(); dR != devicesThisSuper->end(); dR++) + { + if (((*dR)->DeviceType != devTypeIndex) || ((*dR)->RecordType != devRType)) + { + if ((devRType == Device) || (devRType == DeviceExt)) + { + /* awkwardly, because GetSupervisors returns a const vector*, + internal members are const-qualified. But the Put functionality expects + non-const objects when creating the data stream (because the stream itself + is non-const and it uses an initialiser form to stream the data in). In + turn this means we have to create a temporary copy of the Supervisor + address for no other reason than to be able to heave it into another + buffer. + */ + SymAddr_t supAddr = s->Address; + taskInfo.Put(3,&supAddr); + } + taskInfo.PutX(0,&devNames); + taskInfo.Put(1,&devAddrs); + taskInfo.Put(2,&devAttrs); + taskInfo.Send(*m); + devTypeIndex = (*dR)->DeviceType; + taskInfo.Zname(2,taskData.DeviceTypes[devTypeIndex].Name); + devRType = (*dR)->RecordType; + switch(devRType) + { + case Device: + taskInfo.Key(Q::NAME,Q::DATA,Q::DEVI); + break; + case DeviceExt: + taskInfo.Key(Q::NAME,Q::DATA,Q::DEVE); + break; + case External: + taskInfo.Key(Q::NAME,Q::DATA,Q::EXTN); + break; + case Supervisor: + taskInfo.Key(Q::NAME,Q::DATA,Q::SUPV); + } + devNames.clear(); + devAddrs.clear(); + devAttrs.clear(); + superRanks.clear(); + } + if ((*dR)->RecordType == Supervisor) + { + if ((*dR)->Address != s->Address) + { + Post(729,(*dR)->Name,s->Name); + return ERR_INVALID_SUPERVISOR; + } + superRanks.push_back((*dR)->Rank); + taskInfo.Put(3,&superRanks); + } + devNames.push_back((*dR)->Name); + devAddrs.push_back((*dR)->Address); + devAttrs.push_back((*dR)->Attribute); + } + if ((devRType == Device) || (devRType == DeviceExt)) + { + SymAddr_t supAddr = s->Address; + taskInfo.Put(3,&supAddr); + } + taskInfo.PutX(0,&devNames); + taskInfo.Put(1,&devAddrs); + taskInfo.Put(2,&devAttrs); + taskInfo.Send(*m); + } + msg->comm = Comms[sComm]; // relay the message to the supervisor + msg->Send(*m); // being deployed to in this message + break; + } + } + if (msg->Tgt() == static_cast(s->Rank)) break; // supervisor found + } + if (msg->Tgt() != static_cast(s->Rank)) // supervisor not found + { + Post(728,int2str(Urank),uint2str(s->Rank)); + return ERR_INVALID_SUPERVISOR; + } + else break; // distribute is one supervisor at a time + } + // debug output ----------------------------------- + FILE* nsOutput = fopen("DistributionDump.txt","w"); + CMsg_p distMsg(*msg); + distMsg.Dump(nsOutput); + fclose(nsOutput); + //------------------------------------------------- + if ((err = SBase::ConfigDir(msg,comm))) // distribution messages contain the binary directory + { + Post(724,taskName,int2str(Urank)); // no task of this name. + return err; // this should have been caught earlier. + } + return SBase::ConfigDistribute(msg, comm); } -//------------------------------------------------------------------------------ - -unsigned NameServer::OnMoni(PMsg_p *) +unsigned NameServer::ConfigRecall(PMsg_p * msg, unsigned comm) { - - -return 0; + return 0; } -//------------------------------------------------------------------------------ - -unsigned NameServer::OnQuery(PMsg_p *) +/* NameServer only needs set up its own state. Motherships don't + need a state until deployed, because it's only at this point that + name services data is uploaded to them, and after they do have it, + further state changes involve direct explicit commands to the Motherships + anyway. +*/ +unsigned NameServer::ConfigState(PMsg_p * msg, unsigned comm) { - - -return 0; + return SBase::ConfigState(msg, comm); } -//------------------------------------------------------------------------------ - //============================================================================== diff --git a/Source/NameServer/NameServer.h b/Source/NameServer/NameServer.h index 7b957495..0065a68d 100644 --- a/Source/NameServer/NameServer.h +++ b/Source/NameServer/NameServer.h @@ -1,32 +1,34 @@ #ifndef __NameServerH__H #define __NameServerH__H -#include "CommonBase.h" -#include "Ns_el.h" +#include "SBase.h" +#include "Debug.h" //============================================================================== -class NameServer : public CommonBase +class NameServer : public SBase { public: NameServer(int,char **,string); virtual ~ NameServer(); + unsigned Connect (string=""); + unsigned OnCfg (PMsg_p *, unsigned); + unsigned OnDump (PMsg_p *, unsigned); + private: -#include "Decode.cpp" -void Dump(FILE * = stdout); -void Load1(Ns_0 *); -unsigned OnClr (PMsg_p *); -unsigned OnDump (PMsg_p *); -unsigned OnFwd (PMsg_p *); -unsigned OnLoad (PMsg_p *); -unsigned OnLog (PMsg_p *); -unsigned OnMoni (PMsg_p *); -unsigned OnQuery(PMsg_p *); - -typedef unsigned (NameServer::*pMeth)(PMsg_p *); -map FnMapx; +#include "SDecode.cpp" + void Dump(FILE * = stdout, string s = ""); + + unsigned ConfigDir (PMsg_p *, unsigned); + unsigned ConfigDistribute (PMsg_p *, unsigned); + unsigned ConfigRecall (PMsg_p *, unsigned); + unsigned ConfigState (PMsg_p *, unsigned); + +typedef unsigned (NameServer::*pMeth)(PMsg_p *, unsigned); +typedef map FnMap_t; +vector FnMapx; }; diff --git a/Source/NameServer/Ns_el.cpp b/Source/NameServer/Ns_el.cpp deleted file mode 100644 index dff9678d..00000000 --- a/Source/NameServer/Ns_el.cpp +++ /dev/null @@ -1,481 +0,0 @@ -//------------------------------------------------------------------------------ - -#include "Ns_el.h" - -//============================================================================== - -Ns_el::Ns_el() -{ -Put(); -Put(); -Put(); -Put(); -} - -//------------------------------------------------------------------------------ - -Ns_el::Ns_el(byte * s,int l):PMsg_p(s,l) -{ -Put(); -Put(); -Put(); -Put(); -} - -//------------------------------------------------------------------------------ - -Ns_el::Ns_el(PMsg_p * p):PMsg_p(*p) -{ -Put(); -Put(); -Put(); -Put(); -Dump(); -} - -//------------------------------------------------------------------------------ - -Ns_el::~Ns_el() -{ -} - -//------------------------------------------------------------------------------ - -vector * Ns_el::Construct() -// Receiver side (NameServer) function: -// Routine to build and expose a vector of entities derived from the current -// state of the object. -// If there's a problem - and there shouldn't be - a entity type 'X' is written -// to the output vector and the routine returns prematurely (i.e. the 'X' entity -// is the last in the returned vector). Subsequent parts of the PMsg_p are -// ignored. -{ -Ns_X * pX = new Ns_X(); // Error entity -static vector vN; -vN.clear(); // Output entity vector -int cnt; // Stored key vector size -unsigned * ploc = Get(0,cnt);// Get hold of the key vector -try { - if (ploc==0) throw((*pX)("KEY VECTOR")); // If it's not there.... - for (int i=0;i(*ploc,c2); - if (pEtype==0) throw((*pX)("ENTITY TYPE")); - vector vs; // Holder for the packed name vector - Ns_0 * p; // Entity - GetX(*ploc,vs); // Slurp it out - switch (*pEtype) { - case 'D' : { Ns_el::Dindex_t * pDindex = Get(*ploc,c2); - if (pDindex==0) throw((*pX)("DEVICE INDEX")); - p = new Ns_dev(pDindex,vs); // No idea why - - vN.push_back(p); // Another BORLAND bug? - } break; - case 'S' : { Ns_el::Sindex_t * pSindex = Get(*ploc,c2); - if (pSindex==0) throw((*pX)("SUPERVISOR INDEX")); - p = new Ns_sup(pSindex,vs); // No idea why - - vN.push_back(p); // Another BORLAND bug? - } break; - case 'T' : { Ns_el::Tindex_t * pTindex = Get(*ploc,c2); - if (pTindex==0) throw((*pX)("TASK INDEX")); - p = new Ns_tsk(pTindex,vs); // No idea why - - vN.push_back(p); // Another BORLAND bug? - } break; - case 'O' : { Ns_el::Oindex_t * pOindex = Get(*ploc,c2); - if (pOindex==0) throw((*pX)("OWNER INDEX")); - p = new Ns_own(pOindex,vs); // No idea why - - vN.push_back(p); // Another BORLAND bug? - } break; - default : throw((*pX)("UNRECOGNISED ENTITY TYPE")); - } // switch - p->type = *pEtype; - p->key = *ploc; - } // for -} // try -catch(Ns_0 * X) { // Cockup: Push the already allocated - vN.push_back(X); // error entity onto the vector and bail - return &vN; -} -delete pX; // Don't need the error entity -return &vN; -} - -//------------------------------------------------------------------------------ - -void Ns_el::Dump(FILE * fp) -// The usual, but the logic is a bit fiddly here. We want to dump the *contents* -// of the PMsg_p class that is the base class of Ns_el. Calling PMsg_p::Dump() -// will not do the biz, because PMsg_p doesn't know anything about types, so the -// Dump() will be largely impenetrable. The only state variable of Ns_el is -// keyv, the vector of keys that have currently been used to load the class. -// So we walk keyv, extracting the (integer) keys. We can use these to get the -// entity types: Get(keyv[?],...), and this gives us the complete -// signature for the entity: {key,type}. -// If keyv is empty, this is either because the object is genuinely empty, -// or because the POETS entities have been streamed to the internal byte -// buffer. -{ -fprintf(fp,"Ns_el+++++++++++++++++++++++++++++++++++++\n"); -fprintf(fp,"Current key vector (%zu elements):\n",keyv.size()); -WALKVECTOR(unsigned,keyv,i)fprintf(fp,"%3u\n",*i); -fprintf(fp,"+ + + + + + + + + + + + + + + + + + + + + + + + + +\n"); -fprintf(fp,"Walking the entities; dumping with unstreamed keys:\n"); - // Dump with the unstreamed keys -WALKVECTOR(unsigned,keyv,i) Dump0(fp,*i); -fprintf(fp,"+ + + + + + + + + + + + + + + + + + + + + + + + + +\n"); -fprintf(fp,"Walking the entities; dumping with stored key vector:\n"); -int cnt; // Stored key vector size -unsigned * ploc = Get(0,cnt);// Get hold of the key vector -fprintf(fp,"Stored key vector has %u elements\n",cnt); - // Dump with streamed keys -if (ploc!=0) for(int i=0;i(k,cnt); -unsigned char Etype = 'X'; -if (pEtype==0) fprintf(fp,"*** TYPE UNLOAD FAIL ***\n"); -else Etype = *pEtype; -vector vs; -switch (Etype) { - case 'D' : { fprintf(fp,"\nDevice key %3u:\n",k); - Ns_el::Dindex_t Dindex; - Ns_el::Dindex_t * pDindex = Get(k,cnt); - if (pDindex==0) { - fprintf(fp,"*** DEVICE INDEX UNLOAD FAIL ***\n"); - break; - } - else Dindex = *pDindex; - fprintf(fp,"key = %u\n",Dindex.key); - if (Dindex.key!=k) fprintf(fp,"*** KEY MISMATCH ***\n"); - Dindex.addr.Dump(fp); - fprintf(fp,"attr = %u\n",Dindex.attr); - fprintf(fp,"bin = %u\n",Dindex.bin); - fprintf(fp,"inP(size) = %u\n",Dindex.inP/2); - fprintf(fp,"ouP(size) = %u\n",Dindex.ouP/2); - GetX(k,vs); - fprintf(fp,"Name vector (%zu elements)\n",vs.size()); - WALKVECTOR(string,vs,i) fprintf(fp,"%s\n",(*i).c_str()); - } break; - case 'S' : { fprintf(fp,"\nSupervisor key %3u:\n",k); - Ns_el::Sindex_t Sindex; - Ns_el::Sindex_t * pSindex = Get(k,cnt); - if (pSindex==0) { - fprintf(fp,"*** SUPERVISOR INDEX UNLOAD FAIL ***\n"); - break; - } - else Sindex = *pSindex; - fprintf(fp,"key = %u\n",Sindex.key); - if (Sindex.key!=k) fprintf(fp,"*** KEY MISMATCH ***\n"); - Sindex.addr.Dump(fp); - fprintf(fp,"attr = %u\n",Sindex.attr); - fprintf(fp,"bin = %u\n",Sindex.bin); - GetX(k,vs); - fprintf(fp,"Name vector (%zu elements)\n",vs.size()); - WALKVECTOR(string,vs,i) fprintf(fp,"%s\n",(*i).c_str()); - } break; - case 'T' : { fprintf(fp,"\nTask key %3u:\n",k); - Ns_el::Tindex_t Tindex; - Ns_el::Tindex_t * pTindex = Get(k,cnt); - if (pTindex==0) { - fprintf(fp,"*** TASK INDEX UNLOAD FAIL ***\n"); - break; - } - else Tindex = *pTindex; - fprintf(fp,"key = %u\n",Tindex.key); - if (Tindex.key!=k) fprintf(fp,"*** KEY MISMATCH ***\n"); - GetX(k,vs); - fprintf(fp,"Name vector (%zu elements)\n",vs.size()); - WALKVECTOR(string,vs,i) fprintf(fp,"%s\n",(*i).c_str()); - } break; - - case 'O' : { fprintf(fp,"\nOwner key %3u:\n",k); - Ns_el::Oindex_t Oindex; - Ns_el::Oindex_t * pOindex = Get(k,cnt); - if (pOindex==0) { - fprintf(fp,"*** OWNER INDEX UNLOAD FAIL ***\n"); - break; - } - else Oindex = *pOindex; - fprintf(fp,"key = %u\n",Oindex.key); - if (Oindex.key!=k) fprintf(fp,"*** KEY MISMATCH ***\n"); - GetX(k,vs); - fprintf(fp,"Name vector (%zu elements)\n",vs.size()); - WALKVECTOR(string,vs,i) fprintf(fp,"%s\n",(*i).c_str()); - } break; - default : fprintf(fp,"*** UNRECOGNISED ENTITY TYPE ***\n");; -} - -} - -//------------------------------------------------------------------------------ - -int Ns_el::Length() -// Retrieve length of streamed message -{ -if (!keyv.empty()) { - Put(0,&keyv[0],keyv.size()); // Load the key list - keyv.clear(); // Everything is in the stream buffer -} -Msg_p::Stream(); -return (int)Msg_p::vm.size(); -} - -//------------------------------------------------------------------------------ - -void Ns_el::PutD(string Dname, // Device name - string Dtype, // Device type - string Sname, // Supervisor name - string Tname, // Task name - string Oname, // Owner name - vector inpin, // Device input pin names - vector inpintype, // Device input pin types - vector oupin, // Device output pin names - vector oupintype, // Device output pin types - unsigned key, // PMsg_p key - P_addr_t addr, // POETS hardware address - unsigned attr, // Device attribute - unsigned bin) // Binary file indentifier -{ -struct Ns_el::Dindex_t Dindex; -// This rather clunky initialisation section is the highest common factor of my -// three C++ compilers..... -Dindex.key = key; // PMsg_p key -Dindex.addr = addr; // POETS composite address -Dindex.attr = attr; // POETS device attribute -Dindex.bin = bin; // ..and so on: see above -Dindex.inP = inpin.size() + inpintype.size(); -Dindex.ouP = oupin.size() + oupintype.size(); -vector vs; // Name container -vs.push_back(Dname); // Device name -vs.push_back(Dtype); // Device type -vs.push_back(Sname); // Supervisor name -vs.push_back(Tname); // Task name -vs.push_back(Oname); // Owner name -vs.insert(vs.end(),inpin.begin(),inpin.end()); -vs.insert(vs.end(),inpintype.begin(),inpintype.end()); -vs.insert(vs.end(),oupin.begin(),oupin.end()); -vs.insert(vs.end(),oupintype.begin(),oupintype.end()); -unsigned char Entity = 'D'; -Put(key,&Entity); // Entity type (device/supervisor...) -Put(key,&Dindex); // Index structure -PutX(key,&vs); // All the strings in one container -keyv.push_back(key); // Add to the vector of used keys -} - -//------------------------------------------------------------------------------ - -void Ns_el::PutO(string Oname, // Owner name - unsigned key) // PMsg_p key -{ -struct Ns_el::Oindex_t Oindex; -// This rather clunky initialisation section is the highest common factor of my -// three C++ compilers..... -Oindex.key = key; // PMsg_p key -vector vs; // Name container -vs.push_back(Oname); // Owner name -unsigned char Entity = 'O'; -Put(key,&Entity); // Entity type (device/supervisor...) -Put(key,&Oindex); // Index structure -PutX(key,&vs); // All the strings in one container -keyv.push_back(key); // Add to the vector of used keys -} - -//------------------------------------------------------------------------------ - -void Ns_el::PutS(string Sname, // Supervisor name - string Tname, // Task name - string Oname, // Owner name - unsigned key, // PMsg_p key - P_addr_t addr, // POETS hardware address - unsigned attr, // Device attribute - unsigned bin) // Binary file indentifier -{ -struct Ns_el::Sindex_t Sindex; -// This rather clunky initialisation section is the highest common factor of my -// three C++ compilers..... -Sindex.key = key; // PMsg_p key -Sindex.addr = addr; // POETS composite address -Sindex.attr = attr; // POETS device attribute -Sindex.bin = bin; // ..and so on: see above -vector vs; // Name container -vs.push_back(Sname); // Supervisor name -vs.push_back(Tname); // Task name -vs.push_back(Oname); // Owner name -unsigned char Entity = 'S'; -Put(key,&Entity); // Entity type (device/supervisor...) -Put(key,&Sindex); // Index structure -PutX(key,&vs); // All the strings in one container -keyv.push_back(key); // Add to the vector of used keys -} - -//------------------------------------------------------------------------------ - -void Ns_el::PutT(string Tname, // Task name - string Oname, // Owner name - unsigned key) // PMsg_p key -{ -struct Ns_el::Tindex_t Tindex; -// This rather clunky initialisation section is the highest common factor of my -// three C++ compilers..... -Tindex.key = key; // PMsg_p key -vector vs; // Name container -vs.push_back(Tname); // Task name -vs.push_back(Oname); // Owner name -unsigned char Entity = 'T'; // Guess -Put(key,&Entity); // Entity type (device/supervisor...) -Put(key,&Tindex); // Index structure -PutX(key,&vs); // All the strings in one container -keyv.push_back(key); // Add to the vector of used keys -} - -//============================================================================== - -Ns_X * Ns_X::operator()(string s) -{ -msg = s; -return this; -} - -//============================================================================== - -Ns_dev::Ns_dev(Ns_el::Dindex_t * pDindex,vector & vs) -{ -addr = pDindex->addr; -attr = pDindex->attr; -bin = pDindex->bin; -Ename = vs[0]; -Etype = vs[1]; -Sname = vs[2]; -Tname = vs[3]; -Oname = vs[4]; -vector::iterator inbegin = vs.begin()+5; -vector::iterator inend = inbegin + pDindex->inP/2; -vector::iterator intypebegin = inend; -vector::iterator intypeend = intypebegin + pDindex->inP/2; -vector::iterator oubegin = intypeend; -vector::iterator ouend = oubegin + pDindex->ouP/2; -vector::iterator outypebegin = ouend; -vector::iterator outypeend = outypebegin + pDindex->ouP/2; -inpin = vector(inbegin,inend); -inpintype = vector(intypebegin,intypeend); -oupin = vector(oubegin,ouend); -oupintype = vector(outypebegin,outypeend); -} - -//------------------------------------------------------------------------------ - -void Ns_dev::Dump(FILE * fp) -{ -fprintf(fp,"Ns_dev++++++++++++++++++++++++++++++++++++\n"); -addr.Dump(fp); -fprintf(fp,"Device type = %s\n",Etype.c_str()); -fprintf(fp,"Super name = %s\n",Sname.c_str()); -fprintf(fp,"Task name = %s\n",Tname.c_str()); -fprintf(fp,"Owner name = %s\n",Oname.c_str()); -fprintf(fp,"attr = %u\n",attr); -fprintf(fp,"bin = %u\n",bin); -fprintf(fp,"\nInput pin name vector (%zu elements)\n",inpin.size()); -WALKVECTOR(string,inpin,i) fprintf(fp,"%s\n",(*i).c_str()); -fprintf(fp,"\nInput pin type name vector (%zu elements)\n",inpintype.size()); -WALKVECTOR(string,inpintype,i) fprintf(fp,"%s\n",(*i).c_str()); -fprintf(fp,"\nOutput pin name vector (%zu elements)\n",oupin.size()); -WALKVECTOR(string,oupin,i) fprintf(fp,"%s\n",(*i).c_str()); -fprintf(fp,"\nOutput pin type name vector (%zu elements)\n",oupintype.size()); -WALKVECTOR(string,oupintype,i) fprintf(fp,"%s\n",(*i).c_str()); -Ns_0::Dump(fp); -fprintf(fp,"Ns_dev------------------------------------\n"); -} - -//============================================================================== - -Ns_0::Ns_0() -{ -} - -//------------------------------------------------------------------------------ - -void Ns_0::Dump(FILE * fp) -{ -fprintf(fp,"Ns_0++++++++++++++++++++++++++++++++++++++\n"); -fprintf(fp,"key = %u\n",key); -fprintf(fp,"type = %c\n",type); -fprintf(fp,"entity name = %s\n",Ename.c_str()); -fprintf(fp,"Ns_0--------------------------------------\n"); -} - -//============================================================================== - -Ns_sup::Ns_sup(Ns_el::Sindex_t * pSindex,vector & vs) -{ -Ename = vs[0]; -Tname = vs[1]; -Oname = vs[2]; -addr = pSindex->addr; -attr = pSindex->attr; -bin = pSindex->bin; -} - -//------------------------------------------------------------------------------ - -void Ns_sup::Dump(FILE * fp) -{ -fprintf(fp,"Ns_sup++++++++++++++++++++++++++++++++++++\n"); -addr.Dump(fp); -fprintf(fp,"Task name = %s\n",Tname.c_str()); -fprintf(fp,"Owner name = %s\n",Oname.c_str()); -fprintf(fp,"attr = %u\n",attr); -fprintf(fp,"bin = %u\n",bin); -Ns_0::Dump(fp); -fprintf(fp,"Ns_sup------------------------------------\n"); -} - -//============================================================================== - -Ns_tsk::Ns_tsk(Ns_el::Tindex_t *,vector & vs) -{ -Ename = vs[0]; -Oname = vs[1]; -} - -//------------------------------------------------------------------------------ - -void Ns_tsk::Dump(FILE * fp) -{ -fprintf(fp,"Ns_tsk++++++++++++++++++++++++++++++++++++\n"); -fprintf(fp,"Owner name = %s\n",Oname.c_str()); -Ns_0::Dump(fp); -fprintf(fp,"Ns_tsk------------------------------------\n"); -} - -//============================================================================== - -Ns_own::Ns_own(Ns_el::Oindex_t *,vector & vs) -{ -Ename = vs[0]; -} - -//------------------------------------------------------------------------------ - -void Ns_own::Dump(FILE * fp) -{ -fprintf(fp,"Ns_own++++++++++++++++++++++++++++++++++++\n"); -Ns_0::Dump(fp); -fprintf(fp,"Ns_own------------------------------------\n"); -} - -//============================================================================== - diff --git a/Source/NameServer/Ns_el.h b/Source/NameServer/Ns_el.h deleted file mode 100644 index daed295a..00000000 --- a/Source/NameServer/Ns_el.h +++ /dev/null @@ -1,169 +0,0 @@ -#ifndef __Ns_elH__H -#define __Ns_elH__H - -#include "PMsg_p.hpp" -#include "P_addr.h" -#include "NameBase.h" -#include -#include -using namespace std; - -//============================================================================== - -class Ns_0; -class Ns_el : public PMsg_p -{ -public: - Ns_el(); - Ns_el(byte *,int); - Ns_el(PMsg_p *); -virtual ~ Ns_el(); - -vector * Construct(); -void Dump(FILE * = stdout); -void Dump0(FILE *,unsigned); -int Length(); -void PutD(string,string,string,string,string,vector, - vector,vector,vector,unsigned, - P_addr_t,unsigned,unsigned); -void PutO(string,unsigned); -void PutS(string,string,string,unsigned,P_addr_t,unsigned,unsigned); -void PutT(string,string,unsigned); - -struct Dindex_t { - unsigned key; - P_addr_t addr; - unsigned attr; - unsigned bin; - unsigned inP; - unsigned ouP; -}; -struct Oindex_t { - unsigned key; -}; -struct Sindex_t { - unsigned key; - P_addr_t addr; - unsigned attr; - unsigned bin; -}; -struct Tindex_t { - unsigned key; -}; - -vector keyv; - -}; - -//============================================================================== - -class Ns_0 : public NameBase -{ -public: - Ns_0(); -virtual ~ Ns_0(){} - -virtual void Dump(FILE * = stdout); - -unsigned key; -unsigned char type; -string Ename; - -}; - -//============================================================================== - -class Ns_X : public Ns_0 -{ -public: - Ns_X(string = ""){} -Ns_X * operator()(string); -virtual ~ Ns_X(){} - -private: -string msg; - -protected: -virtual void Dump(FILE * = stdout){} -}; - -//============================================================================== - -class Ns_dev : public Ns_0 -{ -public: - Ns_dev(Ns_el::Dindex_t *,vector &); -virtual ~ Ns_dev(){} - -protected: -virtual void Dump(FILE * = stdout); - -private: -string Etype; -string Sname; -string Tname; -string Oname; -P_addr_t addr; -unsigned attr; -unsigned bin; -vector inpin; -vector inpintype; -vector oupin; -vector oupintype; -}; - -//============================================================================== - -class Ns_sup : public Ns_0 -{ -public: - Ns_sup(Ns_el::Sindex_t *,vector &); -virtual ~ Ns_sup(){} - -protected: -virtual void Dump(FILE * = stdout); - -private: -string Tname; -string Oname; -P_addr_t addr; -unsigned attr; -unsigned bin; - -}; - -//============================================================================== - -class Ns_tsk : public Ns_0 -{ -public: - Ns_tsk(Ns_el::Tindex_t *,vector &); -virtual ~ Ns_tsk(){} - -protected: -virtual void Dump(FILE * = stdout); - -private: -string Oname; - -}; - -//============================================================================== - -class Ns_own : public Ns_0 -{ -public: - Ns_own(Ns_el::Oindex_t *,vector &); -virtual ~ Ns_own(){} - -protected: -virtual void Dump(FILE * = stdout); -}; - -//============================================================================== - -#endif - - - - diff --git a/Source/NameServer/SBase.cpp b/Source/NameServer/SBase.cpp new file mode 100644 index 00000000..9d2094e6 --- /dev/null +++ b/Source/NameServer/SBase.cpp @@ -0,0 +1,1550 @@ + +#include "SBase.h" +#include "Pglobals.h" +#include // debug + + +const map SBase::StateVals({{"Loaded",Loaded}, + {"Linked",Linked}, + {"Built",Built}, + {"Deployed",Deployed}, + {"Init",Init}, + {"Running",Running}, + {"Finished",Finished}, + {"Unknown",Unknown}}); +const map SBase::StateStrs({{Loaded,"Loaded"}, + {Linked,"Linked"}, + {Built,"Built"}, + {Deployed,"Deployed"}, + {Init,"Init"}, + {Running,"Running"}, + {Finished,"Finished"}, + {Unknown,"Unknown"}}); +// constructor. Arguments will almost always come from higher-level +// constructors since we expect to derive from SBase. +SBase::SBase(int argc,char ** argv,string d,string s): CommonBase(argc,argv,d,s), AddressBook(d) +{ + FnMapx.push_back(new FnMap_t); // create a default function table (for the *nameserver base* class!) + // Load event handler map (we need to handle a fair number of different SBase requests!) + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::SEND,Q::DEVI,Q::NM )] = &SBase::OnSend; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::SEND,Q::DEVI,Q::ID )] = &SBase::OnSend; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::SEND,Q::DEVI,Q::ALL )] = &SBase::OnSend; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::SEND,Q::DEVI,Q::NGRP)] = &SBase::OnSend; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::SEND,Q::DEVI,Q::IGRP)] = &SBase::OnSend; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::SEND,Q::DEVI,Q::NSUP)] = &SBase::OnSend; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::SEND,Q::DEVI,Q::ISUP)] = &SBase::OnSend; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::SEND,Q::SUPV )] = &SBase::OnSend; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::SEND,Q::EXTN )] = &SBase::OnSend; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::SEND,Q::DEVT,Q::NM )] = &SBase::OnSend; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::SEND,Q::DEVT,Q::IN )] = &SBase::OnSend; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::SEND,Q::DEVT,Q::OUT )] = &SBase::OnSend; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::SEND,Q::ATTR )] = &SBase::OnSend; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::QRY,Q::DEVI,Q::NM )] = &SBase::OnQuery; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::QRY,Q::DEVI,Q::ID )] = &SBase::OnQuery; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::QRY,Q::DEVI,Q::ALL )] = &SBase::OnQuery; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::QRY,Q::DEVI,Q::NGRP )] = &SBase::OnQuery; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::QRY,Q::DEVI,Q::IGRP )] = &SBase::OnQuery; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::QRY,Q::DEVI,Q::NSUP )] = &SBase::OnQuery; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::QRY,Q::DEVI,Q::ISUP )] = &SBase::OnQuery; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::QRY,Q::LIST )] = &SBase::OnQuery; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::QRY,Q::TASK )] = &SBase::OnQuery; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::QRY,Q::SUPV )] = &SBase::OnQuery; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::QRY,Q::EXTN )] = &SBase::OnQuery; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::QRY,Q::DEVT,Q::NM )] = &SBase::OnQuery; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::QRY,Q::DEVT,Q::IN )] = &SBase::OnQuery; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::QRY,Q::DEVT,Q::OUT )] = &SBase::OnQuery; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::QRY,Q::ATTR )] = &SBase::OnQuery; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::CFG,Q::DIST )] = &SBase::OnCfg; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::CFG,Q::TDIR )] = &SBase::OnCfg; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::CFG,Q::BLD )] = &SBase::OnCfg; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::CFG,Q::RECL )] = &SBase::OnCfg; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::CFG,Q::DEL )] = &SBase::OnCfg; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::CFG,Q::STATE )] = &SBase::OnCfg; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::RPLY,Q::DEVI )] = &SBase::OnReply; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::RPLY,Q::DEVI,Q::NF )] = &SBase::OnReply; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::RPLY,Q::DEVI,Q::TNF )] = &SBase::OnReply; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::RPLY,Q::SUPV )] = &SBase::OnReply; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::RPLY,Q::SUPV,Q::TNF )] = &SBase::OnReply; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::RPLY,Q::EXTN )] = &SBase::OnReply; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::RPLY,Q::EXTN,Q::TNF )] = &SBase::OnReply; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::RPLY,Q::DEVT )] = &SBase::OnReply; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::RPLY,Q::DEVT,Q::TNF )] = &SBase::OnReply; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::RPLY,Q::ATTR )] = &SBase::OnReply; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::RPLY,Q::ATTR,Q::TNF )] = &SBase::OnReply; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::RPLY,Q::LIST )] = &SBase::OnReply; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::RPLY,Q::TASK )] = &SBase::OnReply; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::RPLY,Q::TASK,Q::TNF )] = &SBase::OnReply; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::DATA,Q::TASK )] = &SBase::OnData; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::DATA,Q::DEVT )] = &SBase::OnData; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::DATA,Q::DEVI )] = &SBase::OnData; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::DATA,Q::DEVE )] = &SBase::OnData; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::DATA,Q::EXTN )] = &SBase::OnData; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::DATA,Q::SUPV )] = &SBase::OnData; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::CMDC,Q::MONI,Q::ON )] = &SBase::OnCmd; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::CMDC,Q::MONI,Q::OFF )] = &SBase::OnCmd; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::CMDC,Q::LOGN,Q::ON )] = &SBase::OnCmd; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::CMDC,Q::LOGN,Q::OFF )] = &SBase::OnCmd; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::DUMP,Q::TASK,Q::ALL )] = &SBase::OnDump; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::DUMP,Q::TASK,Q::NM )] = &SBase::OnDump; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::DUMP,Q::LIST )] = &SBase::OnDump; +} + +// destructor only needs to get rid of function tables. +SBase::~SBase() +{ + WALKVECTOR(FnMap_t*,FnMapx,F) // WALKVECTOR and WALKMAP are in macros.h (long include chain) + delete *F; // get rid of derived class function tables +} + +//============================================================================== +/* + OnCfg - handle Mothership configuration commands +*/ +//============================================================================== +unsigned SBase::OnCfg(PMsg_p *msg, unsigned comm) +{ + switch(msg->L(2)) + { + case Q::DIST: + return ConfigDistribute(msg, comm); // send core data to Motherships + case Q::TDIR: + return ConfigDir(msg, comm); // send binary data directory to Motherships + case Q::BLD: + return ConfigBuild(msg, comm); // rebuild this SBase's name database + case Q::RECL: + return ConfigRecall(msg, comm); // recall (remove) a task from Motherships + case Q::DEL: + return ConfigDelete(msg, comm); // delete a task from this SBase + case Q::STATE: + return ConfigState(msg, comm); // Update task running state + default: + Post(700,uint2str(msg->Key()),int2str(Urank)); + return 0; + } +} + +//============================================================================== +/* + OnCmd - handle low-level SBase commands +*/ +//============================================================================== +unsigned SBase::OnCmd(PMsg_p *msg, unsigned comm) +{ + switch(msg->L(2)) + { + case Q::INTG: + return CmdIntegrity(msg); // integrity check (a long operation) + case Q::MONI: + return 0; // monitor function not yet implemented + case Q::LOG: + return 0; // nameserver logging not yet implemented + default: + Post(700,uint2str(msg->Key()),int2str(Urank)); + return 0; + } +} + +//============================================================================== +/* + OnCmd - load name data to this SBase +*/ +//============================================================================== +unsigned SBase::OnData(PMsg_p *msg, unsigned comm) +{ + switch(msg->L(2)) + { + case Q::TASK: + return DataTask(msg); // general task data. Needed before we can load devices. + case Q::DEVT: + return DataDevType(msg); // information on device types + case Q::DEVI: + return DataDevice(msg); // device name-address mappings + case Q::DEVE: + return DataDeviceExternal(msg); // devices with an external connection + case Q::EXTN: + return DataExternal(msg); // external device name-address mappings + case Q::SUPV: + return DataSupervisor(msg); // supervisor address-MPI rank mappings + default: + Post(700,uint2str(msg->Key()),int2str(Urank)); + return 0; + } +} + +//============================================================================== +/* + OnDump - dump out this SBase's database info, probably to a file +*/ +//============================================================================== +unsigned SBase::OnDump(PMsg_p *msg, unsigned comm) +{ + if (msg->L(2) == Q::LIST) return DumpSummary(msg, comm); // just a task summary + if (msg->L(2) != Q::TASK) + { + Post(700,uint2str(msg->Key()),int2str(Urank)); + return 0; + } + switch(msg->L(3)) + { + case Q::ALL: + return DumpAll(msg, comm); // comprehensive info on all tasks + case Q::NM: + return DumpTask(msg, comm); // info only on a named task + default: + Post(700,uint2str(msg->Key()),int2str(Urank)); + return 0; + } +} + +//============================================================================== +/* + OnQuery - respond to name services queries (most of the functionality is here) +*/ +//============================================================================== +unsigned SBase::OnQuery(PMsg_p *msg, unsigned comm) +{ + switch(msg->L(2)) // messages have several subkeys for queries + { + case Q::DEVI: // queries relating to devices + switch(msg->L(3)) + { + case Q::NM: // a single device + case Q::NGRP: // devices in the same group as a device + case Q::NSUP: // supervisor of device + return QueryDevIByName(msg, comm); // query by name + case Q::ID: // a single device + case Q::IGRP: // devices in the same group as a device + case Q::ISUP: // supervisor of device + return QueryDevIByID(msg, comm); // query by device ID + case Q::ALL: // just get all (normal) devices + return QueryDevIAll(msg, comm); + default: + Post(702,uint2str(static_cast(msg->L(3))),int2str(Urank)); + return 0; + } + case Q::SUPV: // query supervisors + return QuerySupv(msg, comm); + case Q::EXTN: // query externals + return QueryExtn(msg, comm); + case Q::DEVT: // query devices by a named type + return QueryDevT(msg, comm); + case Q::ATTR: // query devices by a labelled attribute + return QueryAttr(msg, comm); + case Q::LIST: // get a basic list of tasks + return QueryList(msg, comm); + case Q::TASK: // get summary info on a task or all tasks + return QueryTask(msg, comm); + default: + Post(700,uint2str(msg->Key()),int2str(Urank)); + return 0; + } +} + +//============================================================================== +/* + OnReply - receive a name services response. SBase just immediately returns, + because we expect this to be sent FROM an SBase TO some querying process. +*/ +//============================================================================== +unsigned SBase::OnReply(PMsg_p *msg, unsigned comm) +{ + Post(703,uint2str(msg->Key()),int2str(Urank)); + return 0; +} + +//============================================================================== +/* + OnSend - forward packets to some set of devices. Not implemented to devices yet. +*/ +//============================================================================== +unsigned SBase::OnSend(PMsg_p *msg, unsigned comm) +{ + // OnSend uses the same subtree as OnQuery, except we don't need to handle a + // Q::TASK subkey. It will in practice execute a query and then forward + // the message to a Mothership for further handling. + switch(msg->L(2)) + { + case Q::DEVI: + switch(msg->L(3)) + { + case Q::NM: + case Q::NGRP: + case Q::NSUP: + return SendDevIByName(msg, comm); + case Q::ID: + case Q::IGRP: + case Q::ISUP: + return SendDevIByID(msg, comm); + case Q::ALL: + return SendDevIAll(msg, comm); + default: + Post(702,uint2str(static_cast(msg->L(3))),int2str(Urank)); + return 0; + } + case Q::SUPV: + return SendSupv(msg, comm); + case Q::EXTN: + return SendExtn(msg, comm); + case Q::DEVT: + return SendDevT(msg, comm); + case Q::ATTR: + return SendAttr(msg, comm); + default: + Post(700,uint2str(msg->Key()),int2str(Urank)); + return 0; + } +} + +//============================================================================== +/* + Connect - set up new function tables for handling messages from another MPI + universe, such as, e.g. might happen if a group of Motherships were started + and connected to the main Orchestrator universe as a service. +*/ +//============================================================================== +unsigned SBase::Connect(string svc) +{ + unsigned connErr = MPI_SUCCESS; + if ((connErr = CommonBase::Connect(svc)) != MPI_SUCCESS) return connErr; + FnMapx.push_back(new FnMap_t); // add another function table in the nameserver base class + int fIdx=FnMapx.size()-1; + // Load event handler map for the new comm + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::SEND,Q::DEVI,Q::NM )] = &SBase::OnSend; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::SEND,Q::DEVI,Q::ID )] = &SBase::OnSend; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::SEND,Q::DEVI,Q::ALL )] = &SBase::OnSend; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::SEND,Q::DEVI,Q::NGRP)] = &SBase::OnSend; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::SEND,Q::DEVI,Q::IGRP)] = &SBase::OnSend; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::SEND,Q::DEVI,Q::NSUP)] = &SBase::OnSend; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::SEND,Q::DEVI,Q::ISUP)] = &SBase::OnSend; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::SEND,Q::SUPV )] = &SBase::OnSend; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::SEND,Q::EXTN )] = &SBase::OnSend; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::SEND,Q::DEVT,Q::NM )] = &SBase::OnSend; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::SEND,Q::DEVT,Q::IN )] = &SBase::OnSend; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::SEND,Q::DEVT,Q::OUT )] = &SBase::OnSend; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::SEND,Q::ATTR )] = &SBase::OnSend; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::QRY,Q::DEVI,Q::NM )] = &SBase::OnQuery; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::QRY,Q::DEVI,Q::ID )] = &SBase::OnQuery; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::QRY,Q::DEVI,Q::ALL )] = &SBase::OnQuery; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::QRY,Q::DEVI,Q::NGRP )] = &SBase::OnQuery; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::QRY,Q::DEVI,Q::IGRP )] = &SBase::OnQuery; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::QRY,Q::DEVI,Q::NSUP )] = &SBase::OnQuery; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::QRY,Q::DEVI,Q::ISUP )] = &SBase::OnQuery; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::QRY,Q::LIST )] = &SBase::OnQuery; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::QRY,Q::TASK )] = &SBase::OnQuery; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::QRY,Q::SUPV )] = &SBase::OnQuery; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::QRY,Q::EXTN )] = &SBase::OnQuery; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::QRY,Q::DEVT,Q::NM )] = &SBase::OnQuery; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::QRY,Q::DEVT,Q::IN )] = &SBase::OnQuery; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::QRY,Q::DEVT,Q::OUT )] = &SBase::OnQuery; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::QRY,Q::ATTR )] = &SBase::OnQuery; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::CFG,Q::DIST )] = &SBase::OnCfg; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::CFG,Q::TDIR )] = &SBase::OnCfg; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::CFG,Q::BLD )] = &SBase::OnCfg; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::CFG,Q::RECL )] = &SBase::OnCfg; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::CFG,Q::DEL )] = &SBase::OnCfg; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::CFG,Q::STATE )] = &SBase::OnCfg; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::RPLY,Q::DEVI )] = &SBase::OnReply; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::RPLY,Q::DEVI,Q::NF )] = &SBase::OnReply; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::RPLY,Q::DEVI,Q::TNF )] = &SBase::OnReply; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::RPLY,Q::SUPV )] = &SBase::OnReply; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::RPLY,Q::SUPV,Q::TNF )] = &SBase::OnReply; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::RPLY,Q::EXTN )] = &SBase::OnReply; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::RPLY,Q::EXTN,Q::TNF )] = &SBase::OnReply; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::RPLY,Q::DEVT )] = &SBase::OnReply; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::RPLY,Q::DEVT,Q::TNF )] = &SBase::OnReply; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::RPLY,Q::ATTR )] = &SBase::OnReply; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::RPLY,Q::ATTR,Q::TNF )] = &SBase::OnReply; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::RPLY,Q::LIST )] = &SBase::OnReply; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::RPLY,Q::TASK )] = &SBase::OnReply; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::RPLY,Q::TASK,Q::TNF )] = &SBase::OnReply; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::DATA,Q::TASK )] = &SBase::OnData; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::DATA,Q::DEVT )] = &SBase::OnData; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::DATA,Q::DEVI )] = &SBase::OnData; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::DATA,Q::DEVE )] = &SBase::OnData; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::DATA,Q::EXTN )] = &SBase::OnData; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::DATA,Q::SUPV )] = &SBase::OnData; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::CMDC,Q::MONI,Q::ON )] = &SBase::OnCmd; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::CMDC,Q::MONI,Q::OFF )] = &SBase::OnCmd; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::CMDC,Q::LOGN,Q::ON )] = &SBase::OnCmd; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::CMDC,Q::LOGN,Q::OFF )] = &SBase::OnCmd; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::DUMP,Q::TASK,Q::ALL )] = &SBase::OnDump; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::DUMP,Q::TASK,Q::NM )] = &SBase::OnDump; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::DUMP,Q::LIST )] = &SBase::OnDump; + return MPI_SUCCESS; +} + +//============================================================================== +/* + CmdIntegrity - handle an integrity check request. Not implemented yet. +*/ +//============================================================================== +unsigned SBase::CmdIntegrity(PMsg_p *msg) +{ + return 0; +} + +//============================================================================== +/* + ConfigBuild - rebuild SBase, probably because device data has changed +*/ +//============================================================================== +unsigned SBase::ConfigBuild(PMsg_p *msg, unsigned comm) +{ + string taskName = msg->Zname(0); // get the task to be rebuilt + vector tasks; + unsigned err = SUCCESS; + if (taskName.empty()) // No task name => rebuild everything + { + if ((err = ListTask(tasks))) // Some tasks exist to rebuild? + { + Post(721,"rebuild","list",int2str(Urank)); // No. Warn the user. + return err; + } + } + else tasks.push_back(taskName); // a named task. Only rebuild that one. + vector::iterator task; + // traverse the found task list, + for (task = tasks.begin(); task != tasks.end(); task++) + { + if (!(err = RebuildTask(*task))) // try to rebuild both the task + { + if (!(err = BuildMaps(*task))) // and its crosslinks + err = BuildLink(*task); + } + if (err) // something went wrong in the rebuild + { + Post(721,"rebuild",*task,int2str(Urank)); // so warn the user. + return err; + } + } + return err; +} + +//============================================================================== +/* + ConfigDelete - remove a task from this SBase +*/ +//============================================================================== +unsigned SBase::ConfigDelete(PMsg_p *msg, unsigned comm) +{ + string taskName = msg->Zname(0); // get task name + vector tasks; + unsigned err = SUCCESS; + if (taskName.empty()) // no task name => delete all tasks + { + if ((err = ListTask(tasks))) // so get all tasks that have been loaded + { + Post(721,"gett","list",int2str(Urank)); // warning if there are none + return err; + } + } + else tasks.push_back(taskName); // otherwise delete only the named task + vector::iterator task; + for (task = tasks.begin(); task != tasks.end(); task++) + { + if ((err = DelTask(*task))) // try to delete all tasks we asked for + { + Post(721,"delet",*task,int2str(Urank)); // warning on failure + return err; + } + } + return err; +} + +//============================================================================== +/* + ConfigDistribute - send core mappings to Motherships. Normally this will + be handled from Root so SBase itself needs to do very little, only make + sure the task state is changed to Deployed to reflect the new state of + affairs. +*/ +//============================================================================== +unsigned SBase::ConfigDistribute(PMsg_p *msg, unsigned comm) +{ + string taskName = msg->Zname(0); + if (TaskState(taskName,TaskState_t(Deployed))) + { + Post(724,taskName,int2str(Urank)); + return ERR_TASK_NOT_FOUND; + } + return SUCCESS; +} + +//============================================================================== +/* + ConfigDir - send task binary directory path (mostly to Motherships). SBase + keeps a copy of this, though, so this function does update it +*/ +//============================================================================== +unsigned SBase::ConfigDir(PMsg_p *msg, unsigned comm) +{ + string taskName = msg->Zname(0); // first static name string is the task name + string taskPath = msg->Zname(1); // and second name string is the directory path + if (taskPath.empty()) Post(723,int2str(Urank)); + if (TaskExecPath(taskName,taskPath)) + { + Post(724,taskName,int2str(Urank)); + return ERR_TASK_NOT_FOUND; + } + return SUCCESS; +} + +//============================================================================== +/* + ConfigRecall - remove knowledge of a task from a Mothership. This does not + entirely delete the task from SBase or name services, but it does mean that] + a task should be redeployed if it is to be run again. +*/ +//============================================================================== +unsigned SBase::ConfigRecall(PMsg_p *msg, unsigned comm) +{ + string taskName = msg->Zname(0); // as usual, get the task name + vector tasks; + unsigned err = SUCCESS; + if (taskName.empty()) // where no name => recall all tasks + { + if ((err = ListTask(tasks))) // which we should get if they exist + { + Post(721,"gett","list",int2str(Urank)); // and warn if not + return err; + } + } + else tasks.push_back(taskName); + vector::iterator task; + for (task = tasks.begin(); task != tasks.end(); task++) + { + if ((err = ClearTask(*task))) // easiest way to reset the task on SBase + { // is to clear it and reset it + Post(721,"clear",*task,int2str(Urank)); + return err; + } + if ((err = TaskState(*task,Unknown))) // a recalled task is in unknown state + { // until we redeploy or rebuild + Post(725,*task,int2str(Urank)); + return err; + } + } + return err; +} + +//============================================================================== +/* + ConfigState - SBases maintain copies of the task run state. After a task has + been deployed, this corresponds to the Mothership's state for that task. + Before it has been deployed, it corresponds to the stage in the build + process that has been reached at Root. +*/ +//============================================================================== +unsigned SBase::ConfigState(PMsg_p *msg, unsigned comm) +{ + string taskName = msg->Zname(0); + // State is an enum, which makes it more reliable to send it as a string. + // Otherwise there is no easy way to be certain the value isn't out of range + // (e.g. what if the enum is given non-contiguous values?) + string newStateStr = msg->Zname(1); + TaskState_t newState(Unknown); + // Thus after creating a default unknown state we test each viable possibility + if (newStateStr == "Loaded") newState = Loaded; + else if (newStateStr == "Linked") newState = Linked; + else if (newStateStr == "Built") newState = Built; + else if (newStateStr == "Deployed") newState = Deployed; + else if (newStateStr == "Init") newState = Init; + else if (newStateStr == "Running") newState = Running; + else if (newStateStr == "Finished") newState = Finished; + if (TaskState(taskName,newState)) + { + Post(724,taskName,int2str(Urank)); + return ERR_NONFATAL; // + // return ERR_TASK_NOT_FOUND; + } + return SUCCESS; +} + +//============================================================================== +/* + DataTask - Load this SBase with basic task data. A task is loaded from a + PMsg_p containing its name, path, XML filename, a vector of + message types (strings), a vector of attribute types (also strings), and + a pair of counts (how many devices/externals there are for the ENTIRE task). +*/ +//============================================================================== +unsigned SBase::DataTask(PMsg_p *msg) +{ + TaskData_t task; + task.Name = msg->Zname(0); // extract all the basic information + task.Path = msg->Zname(1); + task.XML = msg->Zname(2); + if (task.Name == "") // tasks must have a name + { + Post(704, int2str(Urank)); + return AddressBook::ERR_INVALID_TASK; + } + msg->GetX(0, task.MessageTypes); // then get the message and attribute types + msg->GetX(1, task.AttributeTypes); + vector counts; + msg->Get(2, counts); // and the device counts + if (counts.size() != 2) Post(720,uint2str(counts.size()),"2",task.Name,int2str(Urank)); + else + { + task.DeviceCount = counts[0]; + task.ExternalCount = counts[1]; + } + return AddTask(task.Name, task); // add the task (through AddressBook) +} + +//============================================================================== +/* + DataDevType - Load this task with DeviceType info. This is done as a + separate message to the rest of the basic task data because a DeviceType + has more information about pins and messages than a simple string, so it's + easier to separate it into its own message rather than serialising the + complexity into the basic task info message. Message types for in and out + pins are sent as indices into the message types which were received in the + previous task message, and which should also match the order of declaration + in the XML. +*/ +//============================================================================== +unsigned SBase::DataDevType(PMsg_p *msg) +{ + DevTypeRecord_t deviceType; + string taskName = msg->Zname(0); // first static field is the task + deviceType.Name = msg->Zname(2); // and third is the device type + if (taskName == "") // no task name is an error + { + Post(705, int2str(Urank)); + return AddressBook::ERR_INVALID_TASK; + } + if (deviceType.Name == "") // similarly for no device type + { + Post(706, "load", taskName, int2str(Urank)); + return AddressBook::ERR_INVALID_DEVTYPE; + } + msg->Get(0, deviceType.InMsgs); // otherwise get the message type indices + msg->Get(1, deviceType.OuMsgs); + return AddDeviceType(taskName, deviceType); +} + +//============================================================================== +/* + DataDevice - Load this task with Device info. This is the main function to + populate the database. Device records may be sent in several such messages, + although it is standard to send them all in one message. +*/ +//============================================================================== +unsigned SBase::DataDevice(PMsg_p *msg) +{ + // Post(715,int2str(Urank),int2str(msg->Src()),msg->Zname(0)); + string taskName = msg->Zname(0); // as usual, first static field is the task + if (taskName == "") + { + Post(705, int2str(Urank)); + return AddressBook::ERR_INVALID_TASK; + } + TaskData_t task; + if (GetTask(taskName, task)) // does the task exist? + { + Post(708,"records",taskName,int2str(Urank)); // no. Abort. + return AddressBook::ERR_TASK_NOT_FOUND; + } + string devTypName = msg->Zname(2); // device type name is in the third field + DTypeIdx devIdx = 0; + // search through loaded task info for the device type + while ((task.DeviceTypes[devIdx].Name != devTypName) && + (++devIdx < task.DeviceTypes.size())); + if (devIdx >= task.DeviceTypes.size()) // this device type was never loaded + { + Post(718,taskName,int2str(Urank),devTypName); // so error. + return AddressBook::ERR_INVALID_DEVTYPE; + } + vector devNames; // names and addresses will be in packed vectors + vector devAddrs; + int numSupers = 1; // get method expects a *reference* to the count + // supervisor of this device is in the fourth dynamic field + SymAddr_t* devSuper = msg->Get(3,numSupers); + if (!devSuper) + { + // no supervisor found + Post(718,taskName,int2str(Urank)); + return ERR_INVALID_SUPERVISOR; + } + vector devAttrs; + msg->GetX(0, devNames); // grab device names and addresses out of the first + msg->Get(1, devAddrs); // and second dynamic fields. + if (devNames.size() != devAddrs.size()) // mismatching counts is an error + { + Post(716,int2str(Urank),uint2str(devNames.size()),uint2str(devAddrs.size()),"addresse"); + return ERR_DEVICE_DATA_MISMATCH; + } + msg->Get(2, devAttrs); // get the device attributes + // for which there should be one for each device + if (devNames.size() != devAttrs.size()) + { + Post(716,int2str(Urank),uint2str(devNames.size()),uint2str(devAttrs.size()),"attribute"); + return ERR_DEVICE_DATA_MISMATCH; + } + unsigned err = AddressBook::SUCCESS; + // everything checks out. Build device objects. + for (unsigned d = 0; d < devNames.size(); d++) + { + Record_t device(devNames[d], + devAddrs[d], + *devSuper, + devIdx, + RecordType_t(Device), + devAttrs[d]); + // and add them to the database + if ((err = AddDevice(taskName, device)) != AddressBook::SUCCESS) + { + // try to clean out the task if a device couldn't be inserted + if (ClearTask(taskName) != AddressBook::SUCCESS) + { + Post(708, device.Name, taskName, int2str(Urank)); + return err; + } + Post(707, device.Name, taskName, int2str(Urank)); + return err; + } + } + if (TaskState(taskName) < Linked) // adding the devices auto-links the records + { + if ((err = TaskState(taskName,Linked))) + { + Post(724,taskName,int2str(Urank)); + return err; + } + } + return AddressBook::SUCCESS; +} + +//============================================================================== +/* + DataDeviceExternal - Load this task with any devices having External + connections. Not implemented for now, awaiting definition of Externals. +*/ +//============================================================================== +unsigned SBase::DataDeviceExternal(PMsg_p *msg) +{ + return 0; +} + + +//============================================================================== +/* + DataExternal - Load this task with External device info. Not implemented for + now. +*/ +//============================================================================== +unsigned SBase::DataExternal(PMsg_p *msg) +{ + return 0; +} + + +//============================================================================== +/* + DataSupervisor - Load this task with Supervisor information. Supervisors have + both an address and an MPI rank. +*/ +//============================================================================== +unsigned SBase::DataSupervisor(PMsg_p *msg) +{ + // Post(715,int2str(Urank),int2str(msg->Src()),msg->Zname(0)); + string taskName = msg->Zname(0); // first static field is the task + if (taskName == "") // abort if there was no task name + { + Post(705, int2str(Urank)); + return AddressBook::ERR_INVALID_TASK; + } + TaskData_t task; + if (GetTask(taskName, task)) // also abort if the task wasn't loaded + { + Post(708,"records",taskName,int2str(Urank)); + return AddressBook::ERR_TASK_NOT_FOUND; + } + string devTypName = msg->Zname(2); // third static field is the Supervisor type + DTypeIdx devIdx = 0; + // Supervisors should exist in the database as a 'normal' device type + while ((task.DeviceTypes[devIdx].Name != devTypName) && + (++devIdx < task.DeviceTypes.size())); + if (devIdx >= task.DeviceTypes.size()) // nonexistent Supervisor type + { + Post(718,taskName,int2str(Urank),devTypName); + return AddressBook::ERR_INVALID_DEVTYPE; + } + vector devNames; // containers for all the Supervisors in + vector devAddrs; // the message + vector devRanks; + vector devAttrs; + msg->GetX(0, devNames); // first dynamic field is the name + // msg->Get(1, devData); + msg->Get(1, devAddrs); // second is the (POETS) address + if (devNames.size() != devAddrs.size()) // name-address counts must match + { + Post(716,int2str(Urank),uint2str(devNames.size()),uint2str(devAddrs.size()),"address"); + return ERR_DEVICE_DATA_MISMATCH; + } + msg->Get(2, devAttrs); // third is the Supervisor's attributes + if (devNames.size() != devAttrs.size()) // which also has to match counts. + { + Post(716,int2str(Urank),uint2str(devNames.size()),uint2str(devAttrs.size()),"attribute"); + return ERR_DEVICE_DATA_MISMATCH; + } + // and fourth is the MPI rank. FIXME: this only works with a single comm. + // If supervisors are on multiple comms we will need to have some way of + // identifying the comm as well. + msg->Get(3, devRanks); + if (devNames.size() != devRanks.size()) + { + Post(716,int2str(Urank),uint2str(devNames.size()),uint2str(devRanks.size()),"rank"); + return ERR_DEVICE_DATA_MISMATCH; + } + // if everything was OK, load the supervisors in + for (unsigned d = 0; d < devNames.size(); d++) + { + // by building a record for each one + Record_t device(devNames[d], + devAddrs[d], + static_cast(devRanks[d]), + devIdx, + RecordType_t(Supervisor), + devAttrs[d]); + unsigned err = AddressBook::SUCCESS; + // and adding it to the database + if ((err = AddDevice(taskName, device)) != AddressBook::SUCCESS) + { + /* + if (ClearTask(taskName) != AddressBook::SUCCESS) + { + Post(708, device.Name, taskName, int2str(Urank)); + return err; + } + */ + Post(707, device.Name, taskName, int2str(Urank)); + // return err; + } + } + // fclose(dbgSBase); + return AddressBook::SUCCESS; +} + +//============================================================================== +/* + DumpAll - Output a comprehensive dump of all tasks loaded to this SBase, + including device info. +*/ +//============================================================================== +unsigned SBase::DumpAll(PMsg_p *msg, unsigned comm) +{ + /* Second static name string is the file name to dump to. The first static + string should be empty. We use the second one so that the first field is + used in a consistent way - for a task name. Filename must be treated as an + absolute literal, because tasks could in + general have different paths, therefore there is no way to infer what + additional path string might be appended to the file name given. + */ + string filename = msg->Zname(1); + FILE* dumpFile; + if (filename.empty()) dumpFile = stdout; // empty filename => dump to console + else dumpFile = fopen(filename.c_str(), "a"); + vector tasks; + unsigned err = AddressBook::SUCCESS; + if ((err = ListTask(tasks))) // get all the tasks + { + Post(710, "Dump", int2str(Urank)); // error: no tasks were found + if (dumpFile != stdout) fclose(dumpFile); + return AddressBook::ERR_NONFATAL; // so we don't need to do anything + } + WALKVECTOR(string, tasks, task) Dump(dumpFile, *task); // dump it all out + return err; +} + +//============================================================================== +/* + DumpAll - Output only task summary info to a file. +*/ +//============================================================================== +unsigned SBase::DumpSummary(PMsg_p *msg, unsigned comm) +{ + string filename = msg->Zname(1); // second static string - file to dump to + FILE* dumpFile; + if (filename.empty()) dumpFile = stdout; // no name: dump to console + else dumpFile = fopen(filename.c_str(), "a"); + Dump(dumpFile); // dump without a task emits a summary + if (dumpFile != stdout) fclose(dumpFile); + return AddressBook::SUCCESS; +} + +//============================================================================== +/* + DumpTask - Output complete information about a given task to a file. +*/ +//============================================================================== +unsigned SBase::DumpTask(PMsg_p *msg, unsigned comm) +{ + string taskName = msg->Zname(0); // first static string is the task to dump + if (taskName.empty()) // no name is an obvious error + { + Post(710, "Dump", int2str(Urank)); + return AddressBook::ERR_INVALID_TASK; + } + string filename = msg->Zname(1); // second static string is the file to dump to + FILE* dumpFile; + if (filename.empty()) dumpFile = stdout; // if file is empty dump to console + else dumpFile = fopen(filename.c_str(), "a"); + Dump(dumpFile, taskName); // only dump info about this task + if (dumpFile != stdout) fclose(dumpFile); + return AddressBook::SUCCESS; +} + +//============================================================================== +/* + QueryAttr - Get all device name-address pairs where the device has the + attribute specified. Currently, the attribute is asked for by name. The + name can be a vector, in which case the query matches devices with any of + the matched names (OR matching). A bit-field representation of an attribute + (rather than a string) would also allow AND matching (extension for the + future?). + + The Query group of commands to SBase will be an external request for device + information. We do not usually expect such commands to come from internal + Orchestrator processes, although it's conceivable a Supervisor device could + ask for an internal query (which should be fulfilled locally by the SBase + of the Mothership to which the supervisor belongs). All Queries will generate + a reply to the querying process. This reply will either be a device list + (of some form) a NOTFOUND response (no devices matched the query criteria) or + a TASKNOTFOUND response (the task being queried doesn't exist in the database) +*/ +//============================================================================== +unsigned SBase::QueryAttr(PMsg_p *msg, unsigned comm) +{ + string taskName = msg->Zname(0); // first static string is the task to query + if (taskName.empty()) // no task is an obvious error + { + Post(710, "QueryByAttribute", int2str(Urank)); + return AddressBook::ERR_INVALID_TASK; + } + unsigned err = SUCCESS; + // query asks for attributes by name (should this be by attribute ID for + // compactness?) + vector attrs; + msg->GetX(2,attrs); // third dynamic field is the attribute set + int srcProc = msg->Src(); // need the querying source to send a reply + msg->comm = Comms[comm]; + msg->Src(msg->Tgt()); // build the reply message + msg->L(1,Q::RPLY); + vector devAddrs; + vector devNames; + vector devAttrs; + // now grab all the devices having one or more of the attributes asked for + WALKVECTOR(string, attrs, a) + { + const RecordVect_t* devRecords; + if ((err = FindByAttribute(taskName, *a, devRecords))) + { + // filter out nonexistent task/nonexistent device queries. An + // ERR_INVALID_MAP indicates the task has a dirty device table, and + // should be rebuilt before asking for devices. + if (err == ERR_TASK_NOT_FOUND || err == ERR_INVALID_MAP) + { + // Q::TNF => task not found; Q::NF => no matching records found + msg->L(3, err == ERR_TASK_NOT_FOUND ? Q::TNF : Q::NF); + msg->Send(srcProc); // send not found response + return SUCCESS; + } + continue; // no devices with this attribute were found + } + devAttrs.push_back(*a); // send the actual attributes matched in the reply + for (vector::const_iterator device = devRecords->begin(); + device != devRecords->end(); device++) + { + devAddrs.push_back((*device)->Address); + devNames.push_back((*device)->Name); + } + } + if (!devAddrs.size()) msg->L(3, Q::NF); // no attributes matched + else + { + msg->Put(0,&devAddrs); // otherwise reply with the matching + msg->PutX(1,&devNames); // devices' name-address pairs + msg->PutX(2,&devAttrs); + } + msg->Send(srcProc); + return SUCCESS; +} + +//============================================================================== +/* + QueryDevIAll - Gets all devices for a task. We return every device in a single + PMsg_p. This could get VERY big, but since there are no certainties the + receiving device knows how many devices it's expecting, chunking the reply + into bite-sized messages makes it impossible for the receiver to know when it + can stop receiving. An alternative would be to insert the device count in the + message, along with possibly a sequence number, but for a first revision this + is just adding complication for possibly little benefit. To be tested. +*/ +//============================================================================== +unsigned SBase::QueryDevIAll(PMsg_p *msg, unsigned comm) +{ + string taskName = msg->Zname(0); // as usual, first static field is the task + if (taskName.empty()) // also as usual, no task is an error. + { + Post(710, "QueryAllDevices", int2str(Urank)); + return AddressBook::ERR_INVALID_TASK; + } + unsigned err = SUCCESS; + int srcProc = msg->Src(); // set up the reply to the querying process + msg->comm = Comms[comm]; + msg->Src(msg->Tgt()); + msg->L(1,Q::RPLY); + vector devAddrs; + vector devNames; + const vector* devRecords; + if ((err = GetDevices(taskName, devRecords))) // grab all the devices + { + // as long as any exist + msg->L(3, err == ERR_TASK_NOT_FOUND ? Q::TNF : Q::NF); + msg->Send(srcProc); + return SUCCESS; + } + // reply message gives all the device name-address pairs. + for (vector::const_iterator device = devRecords->begin(); + device != devRecords->end(); device++) + { + devAddrs.push_back(device->Address); + devNames.push_back(device->Name); + } + // no devices. Should have been caught above, but just in case... + if (!devAddrs.size()) msg->L(3, Q::NF); + else + { + msg->Put(0,&devAddrs); // addresses and names are both + msg->PutX(1,&devNames); // packed as vectors. + } + msg->Send(srcProc); + return SUCCESS; +} + +//============================================================================== +/* + QueryDevIByName - This gets device addresses from a device name. We handle + both the case of a simple query for a device address given a name, and the + more complex cases where we want all devices in the same group as a named + device, or where we want the supervisor of the named device. The latter will + return only the address, in the first dynamic field, the former will return a + vector of device address-name pairs, in the first and second dynamic fields. +*/ +//============================================================================== +unsigned SBase::QueryDevIByName(PMsg_p *msg, unsigned comm) +{ + // check that the key type is valid - if not the query is unrecognised. + if (!((msg->L(3) == Q::NGRP) || (msg->L(3) == Q::NM) || (msg->L(3) == Q::NSUP))) + { + Post(702,uint2str(msg->Key()),int2str(Urank)); + return ERR_NONFATAL; + } + string taskName = msg->Zname(0); // first static field is the task name + if (taskName.empty()) // which, as usual, must exist. + { + Post(710, "QueryDeviceByName", int2str(Urank)); + return AddressBook::ERR_INVALID_TASK; + } + string deviceName = msg->Zname(1); // second static field is the device name + if (deviceName.empty()) // which also must exist or it is an error. + { + Post(731, int2str(Urank)); + return ERR_INVALID_DEVICE; + } + unsigned err = SUCCESS; + int srcProc = msg->Src(); // query is valid - set up a reply. + msg->comm = Comms[comm]; + msg->Src(msg->Tgt()); + msg->L(1,Q::RPLY); + const Record_t* device; + if ((err = FindDevice(taskName, deviceName, device))) // get the base device + { + // if the device or task doesn't exist, we can reply immediately + msg->L(3, err == ERR_TASK_NOT_FOUND ? Q::TNF : Q::NF); + msg->Send(srcProc); + return SUCCESS; + } + if (msg->L(3) == Q::NGRP) // asking for all devices in the same group. + { + vector devAddrs; + vector devNames; + const RecordVect_t* devRecords; + // same group => devices share a supervisor. Look up by supervisor of + // named device. + if ((err = FindBySuper(taskName, device->Supervisor, devRecords)) || + (devRecords->size() == 0)) + { + // given that at least the lookup device must belong to its + // Supervisor, finding no devices attached to the Supervisor in + // question would be a serious error! + Post(732,int2str(Urank),uint2str(device->Supervisor),device->Name, + err == ERR_TASK_NOT_FOUND ? "task" : "devices"); + msg->L(3, err == ERR_TASK_NOT_FOUND ? Q::TNF : Q::NF); + msg->Send(srcProc); + return ERR_DEVICE_DATA_MISMATCH; + } + // bundle up all the device address-name pairs and send them as the reply. + for (vector::const_iterator deviceIt = devRecords->begin(); + deviceIt != devRecords->end(); deviceIt++) + { + devAddrs.push_back((*deviceIt)->Address); + devNames.push_back((*deviceIt)->Name); + } + msg->Put(0,&devAddrs); + msg->PutX(1,&devNames); + } + else // expecting only to respond with an address - either a supervisor or device + { + // these have to be copied into a temporary (and then recopied into the message) + // because Put expects a type match, while the iterator returns constant records, + // and Put itself must overwrite the actual value in its own data structure (not + // the original) - but if its internal type is , it can't overwrite. + SymAddr_t nonConstDevAddr = device->Address; + SymAddr_t nonConstSuperAddr = device->Supervisor; + msg->Put(0,&nonConstDevAddr); + if (msg->L(3) == Q::NSUP) msg->Put(1,&nonConstSuperAddr); + } + msg->Send(srcProc); + return SUCCESS; +} + +//============================================================================== +/* + QueryDevIByID - Gets device names (and possibly addresses from a device + address. This mirrors (almost) the functionality of QueryDevIByName. The only + difference is that if a Supervisor is being queried, only the Supervisor ID + is returned (it makes no sense to ask for a Supervisor name, since from the + application POV, there is only one Supervisor device, so its name is + automatically known). +*/ +//============================================================================== +unsigned SBase::QueryDevIByID(PMsg_p *msg, unsigned comm) +{ + // just like the ByName case, validate the query type + if (!((msg->L(3) == Q::IGRP) || (msg->L(3) == Q::ID) || (msg->L(3) == Q::ISUP))) + { + Post(702,uint2str(msg->Key()),int2str(Urank)); + return ERR_NONFATAL; + } + string taskName = msg->Zname(0); // as always, first static field is the task + if (taskName.empty()) // and an empty task is an error. + { + Post(710, "QueryDeviceByID", int2str(Urank)); + return AddressBook::ERR_INVALID_TASK; + } + int devCount = 0; + // first dynamic field is the device address to query on. This must exist, + // so if the count of values in the first field is 0, it's an error. + SymAddr_t* deviceAddr = msg->Get(0,devCount); + if (!devCount) + { + Post(731, int2str(Urank)); + return ERR_INVALID_DEVICE; + } + unsigned err = SUCCESS; + int srcProc = msg->Src(); // query is valid, so set up the reply. + msg->comm = Comms[comm]; + msg->Src(msg->Tgt()); + msg->L(1,Q::RPLY); + const Record_t* device; + // find the device being queried by ID. + if ((err = FindDevice(taskName, *deviceAddr, device))) + { + // no such device, or no such task. + msg->L(3, err == ERR_TASK_NOT_FOUND ? Q::TNF : Q::NF); + msg->Send(srcProc); + return SUCCESS; + } + if (msg->L(3) == Q::IGRP) // we want all devices in the same group + { + vector devAddrs; + vector devNames; + const RecordVect_t* devRecords; + // just like the ByName case, look up by supervisor of the query device + if ((err = FindBySuper(taskName, device->Supervisor, devRecords)) || + (devRecords->size() == 0)) + { + // given that at least the lookup device must belong to its Supervisor, + // finding no devices attached to the Supervisor in question would be + // a serious error! + Post(732,int2str(Urank),uint2str(device->Supervisor),device->Name, + err == ERR_TASK_NOT_FOUND ? "task" : "devices"); + msg->L(3, err == ERR_TASK_NOT_FOUND ? Q::TNF : Q::NF); + msg->Send(srcProc); + return ERR_DEVICE_DATA_MISMATCH; + } + // and in the same way, as long as devices are found, return their + // address-name pairs (which should include the queried device) + for (vector::const_iterator deviceIt = devRecords->begin(); + deviceIt != devRecords->end(); deviceIt++) + { + devAddrs.push_back((*deviceIt)->Address); + devNames.push_back((*deviceIt)->Name); + } + msg->Put(0,&devAddrs); + msg->PutX(1,&devNames); + } + else + { + // for a simple device, return the name in the first static field + msg->Zname(1,device->Name); + if (msg->L(3) == Q::ISUP) // supervisors return their address + { + SymAddr_t nonConstSuperAddr = device->Supervisor; + msg->Put(1,&nonConstSuperAddr); + } + } + msg->Send(srcProc); + return SUCCESS; +} + +//============================================================================== +/* + QueryDevT - Gets device name-address pairs with a given type. There are 3 + flavours of this query: NM - matches on the type, IN - matches on all devices + sharing an input with the same message type, OUT - matches on all devices + sharing an output with the same message type. +*/ +//============================================================================== +unsigned SBase::QueryDevT(PMsg_p *msg, unsigned comm) +{ + // validate the query flavour + if (!((msg->L(3) == Q::IN) || (msg->L(3) == Q::NM) || (msg->L(3) == Q::OUT))) + { + Post(702,uint2str(msg->Key()),int2str(Urank)); + return ERR_NONFATAL; + } + string taskName = msg->Zname(0); // first static field is the task name + if (taskName.empty()) // no task name is an error as always + { + Post(710, "QueryDeviceByName", int2str(Urank)); + return AddressBook::ERR_INVALID_TASK; + } + string lookupType = msg->Zname(2); // third static field is the type name + if (lookupType.empty()) + { + // no type name is an error - different error class depending on the + // query flavour + if (msg->L(3) == Q::NM) + { + Post(706,"find",taskName,int2str(Urank)); + return ERR_INVALID_DEVTYPE; + } + else + { + Post(733,int2str(Urank)); + return ERR_INVALID_MESSAGE_TYPE; + } + + } + unsigned err = SUCCESS; + int srcProc = msg->Src(); // query is valid; set up the reply + msg->comm = Comms[comm]; + msg->Src(msg->Tgt()); + msg->L(1,Q::RPLY); + vector devAddrs; + vector devNames; + const RecordVect_t* devRecords; + // do the lookup for the 3 different query flavours: whichever is + // specified, we will return name-address pairs. + if (msg->L(3) == Q::NM) err = FindByType(taskName,lookupType,devRecords); + else if (msg->L(3) == Q::IN) err = FindByInMsg(taskName,lookupType,devRecords); + else err = FindByOuMsg(taskName,lookupType,devRecords); + if (err) + { + // no such devices found or no such task found: immediate reply + msg->L(3, err == ERR_TASK_NOT_FOUND ? Q::TNF : Q::NF); + msg->Send(srcProc); + return SUCCESS; + } + // otherwise pack the reply vectors with addresses and names + for (vector::const_iterator device = devRecords->begin(); + device != devRecords->end(); device++) + { + devAddrs.push_back((*device)->Address); + devNames.push_back((*device)->Name); + } + if (!devAddrs.size()) msg->L(3, Q::NF); // should have been caught above + else + { + msg->Put(0,&devAddrs); + msg->PutX(1,&devNames); + } + msg->Send(srcProc); + return SUCCESS; + +} + +//============================================================================== +/* + QueryExtn - Gets all external device name-address pairs. An External is a + virtual device not hosted in the POETS system but that can interact with + other devices as if it were. Since we currently don't have any support for + externals in the rest of the system, for the moment this query won't do + very much (and it can't be practicably tested), but the machinery looks + just like a QueryDevIAll so it can be implemented in principle. +*/ +//============================================================================== +unsigned SBase::QueryExtn(PMsg_p *msg, unsigned comm) +{ + string taskName = msg->Zname(0); // as usual, first static field is the task + if (taskName.empty()) // also as usual, no task is an error. + { + Post(710, "QueryAllExternals", int2str(Urank)); + return AddressBook::ERR_INVALID_TASK; + } + unsigned err = SUCCESS; + int srcProc = msg->Src(); // set up the reply to the querying process + msg->comm = Comms[comm]; + msg->Src(msg->Tgt()); + msg->L(1,Q::RPLY); + vector devAddrs; + vector devNames; + const vector* devRecords; + if ((err = GetExternals(taskName, devRecords))) // grab all the externals + { + // as long as any exist + msg->L(3, err == ERR_TASK_NOT_FOUND ? Q::TNF : Q::NF); + msg->Send(srcProc); + return SUCCESS; + } + // reply message gives all the external name-address pairs. + for (vector::const_iterator device = devRecords->begin(); + device != devRecords->end(); device++) + { + devAddrs.push_back(device->Address); + devNames.push_back(device->Name); + } + // no externals. Should have been caught above, but just in case... + if (!devAddrs.size()) msg->L(3, Q::NF); + else + { + msg->Put(0,&devAddrs); // addresses and names are both + msg->PutX(1,&devNames); // packed as vectors. + } + msg->Send(srcProc); + return SUCCESS; +} + +//============================================================================== +/* + QueryList - Ask for a list of tasks on this SBase. Can't fail: either we have + tasks or not so we just return a list if we have them, a not found message if + not. +*/ +//============================================================================== +unsigned SBase::QueryList(PMsg_p *msg, unsigned comm) +{ + unsigned err = SUCCESS; + int srcProc = msg->Src(); // query is automatically valid; set up a reply + msg->comm = Comms[comm]; + msg->Src(msg->Tgt()); + msg->L(1,Q::RPLY); + vector taskNames; + // if the list produces no tasks, return with task not found + if ((err = ListTask(taskNames)) || (taskNames.size() == 0)) msg->L(3,Q::TNF); + else msg->PutX(1,&taskNames); // otherwise return the vector of task names + msg->Send(srcProc); + return SUCCESS; +} + +//============================================================================== +/* + QuerySupv - Get all supervisors for a task. We return one more list in + addition to the address and the name: the list of ranks, because the + requesting process may wish to identify a Supervisor by MPI rank rather than + POETS address. Otherwise this looks like the other 'all'-type queries. +*/ +//============================================================================== +unsigned SBase::QuerySupv(PMsg_p *msg, unsigned comm) +{ + string taskName = msg->Zname(0); // as usual, first static field is the task + if (taskName.empty()) // also as usual, no task is an error. + { + Post(710, "QueryAllSupervisors", int2str(Urank)); + return AddressBook::ERR_INVALID_TASK; + } + unsigned err = SUCCESS; + int srcProc = msg->Src(); // set up the reply to the querying process + msg->comm = Comms[comm]; + msg->Src(msg->Tgt()); + msg->L(1,Q::RPLY); + vector devAddrs; + vector devNames; + vector devRanks; + const vector* devRecords; + if ((err = GetSupervisors(taskName, devRecords))) // grab all the supervisors + { + // as long as any exist + msg->L(3, err == ERR_TASK_NOT_FOUND ? Q::TNF : Q::NF); + msg->Send(srcProc); + return SUCCESS; + } + // reply message gives name-address-rank triplets for each Supervisor + for (vector::const_iterator device = devRecords->begin(); + device != devRecords->end(); device++) + { + devAddrs.push_back(device->Address); + devNames.push_back(device->Name); + devRanks.push_back(device->Rank); + } + // no supervisors. Should have been caught above, but just in case... + if (!devAddrs.size()) msg->L(3, Q::NF); + else + { + msg->Put(0,&devAddrs); // addresses, + msg->PutX(1,&devNames); // names, and + msg->Put(2,&devRanks); // ranks are all packed as vectors + } + msg->Send(srcProc); + return SUCCESS; +} + +//============================================================================== +/* + QueryTask - A task query might end up being some set of requests with further + internal details in future, but for the moment this simply returns summary + task info, like a dump, but to a remote querying process. +*/ +//============================================================================== +unsigned SBase::QueryTask(PMsg_p *msg, unsigned comm) +{ + string taskName = msg->Zname(0); // first static field is the task, as always + if (taskName.empty()) // and no task name is an error. + { + Post(710, "QueryTask", int2str(Urank)); + return AddressBook::ERR_NONFATAL; + } + unsigned err = SUCCESS; + int srcProc = msg->Src(); // set up the reply + msg->comm = Comms[comm]; + msg->Src(msg->Tgt()); + msg->L(1,Q::RPLY); + TaskData_t task; + if ((err = GetTask(taskName, task))) // get the task info + { + msg->L(3,Q::TNF); // there is no such task + msg->Send(srcProc); + return SUCCESS; + } + // turn the state into a string for easier reading at the querying process + msg->Zname(1,State2Str(task.State)); + if (msg->Zname(1) == "Unknown") + Post(734,taskName,int2str(Urank)); // some strange unknown task state + /* + switch (task.State) + { + case Loaded: + msg->Zname(1,"Loaded"); + break; + case Linked: + msg->Zname(1,"Linked"); + break; + case Built: + msg->Zname(1,"Built"); + break; + case Deployed: + msg->Zname(1,"Deployed"); + break; + case Init: + msg->Zname(1,"Init"); + break; + case Running: + msg->Zname(1,"Running"); + break; + case Finished: + msg->Zname(1,"Finished"); + break; + default: + Post(734,taskName,int2str(Urank)); + msg->Zname(1,"Unknown"); + } + */ + msg->Zname(2,task.Path); // dump all other available info + msg->Zname(3,task.XML); // into the reply message + msg->PutX(0,&task.MessageTypes); + msg->PutX(1,&task.AttributeTypes); + vector counts; // put device counts in a vector + counts.push_back(task.DeviceCount); // (just so we have fewer fields) + counts.push_back(task.ExternalCount); + counts.push_back(task.SupervisorCount); + msg->Put(2,&counts); + msg->Send(srcProc); + return SUCCESS; +} + +//============================================================================== +/* + The Send group of commands. In general, these forward on pre-packed messages + containing POETS packets to some subset of devices in a POETS system. These + are all virtual in SBase, because what a given process actually does with a + Send depends on what type of process it is. A Nameserver, for example, needs + to detect which Supervisors to talk to and relay the message on to them. A + Supervisor needs either to unpack the message and send it over HostLink, or + identify where to forward the message, if it doesn't have the devices being + targetted. The functions here are not pure-virtual, though, because one can + conceive of SBase processes which simply don't implement Send functionality. + So we provide the not-implemented stubs here. +*/ +//============================================================================== +unsigned SBase::SendAttr(PMsg_p *msg, unsigned comm) +{ + return 0; +} + +unsigned SBase::SendDevIAll(PMsg_p *msg, unsigned comm) +{ + return 0; +} + +unsigned SBase::SendDevIByName(PMsg_p *msg, unsigned comm) +{ + return 0; +} + +unsigned SBase::SendDevIByID(PMsg_p *msg, unsigned comm) +{ + return 0; +} + +unsigned SBase::SendDevT(PMsg_p *msg, unsigned comm) +{ + return 0; +} + +unsigned SBase::SendExtn(PMsg_p *msg, unsigned comm) +{ + return 0; +} + +unsigned SBase::SendSupv(PMsg_p *msg, unsigned comm) +{ + return 0; +} + +//============================================================================== +/* + State2Str - a static method to transform a state enum into a readable string +*/ +//============================================================================== +string SBase::State2Str(TaskState_t state) +{ + map::const_iterator s = StateStrs.find(state); + if (s == StateStrs.end()) return "Unknown"; + else return s->second; +} + +//============================================================================== +/* + Str2State - a static method to transform a state string into a state enum +*/ +//============================================================================== +TaskState_t SBase::Str2State(const string& state) +{ + map::const_iterator s = StateVals.find(state); + if (s == StateVals.end()) return Unknown; + else return s->second; +} diff --git a/Source/NameServer/SBase.h b/Source/NameServer/SBase.h new file mode 100644 index 00000000..cadd884b --- /dev/null +++ b/Source/NameServer/SBase.h @@ -0,0 +1,110 @@ +#ifndef __SBASE_H__ +#define __SBASE_H__ + +#include "CommonBase.h" +#include "AddressBook.hpp" +using namespace AddressBookNS; + +//============================================================================== +/* + +SBase is the common abstraction layer to provide name services to the +Orchestrator. Its main function is to service Q::NAME messages coming from +other processes in the system. The Q::NAME message space is itself +divided into Q::SEND (forward to devices), Q::QRY (ask name services for device +into), Q::RPLY (send a response to a query to some other process), Q::DATA +(load NameServer data into the database), Q::CFG (configure Motherships at +startup of a task), Q::CMDC (miscellaneous low-level control functions) and +Q::DUMP (output NameServer data to a file or on-screen) + +SBase is in fact a wrapper for 2 classes, CommonBase (containing the MPI +messaging layer and all its associated machinery) and AddressBook (which +provides the Name database and query functions. As such it derives from +both classes. Essentially all the methods of SBase deal with responding +to queries or building the database. +*/ +//------------------------------------------------------------------------------ +class SBase : public CommonBase, public AddressBook +{ + +public: + SBase(int,char **,string,string); +virtual ~ SBase(); + +inline virtual void Dump(FILE * f = stdout, string s = "") {AddressBook::Dump(f, s);}; + +// top-level message dispatchers that handle the main subtypes of message +unsigned OnCfg (PMsg_p *, unsigned); +unsigned OnCmd (PMsg_p *, unsigned); +unsigned OnData (PMsg_p *, unsigned); +unsigned OnDump (PMsg_p *, unsigned); +unsigned OnQuery (PMsg_p *, unsigned); +unsigned OnReply (PMsg_p *, unsigned); +unsigned OnSend (PMsg_p *, unsigned); + +static string State2Str (TaskState_t); +static TaskState_t Str2State (const string&); + +protected: + +// overloaded Connect for multi-universe support +virtual unsigned Connect (string=""); + +// subcommands to set up Motherships +virtual unsigned ConfigBuild (PMsg_p *, unsigned); +virtual unsigned ConfigDelete (PMsg_p *, unsigned); +virtual unsigned ConfigDir (PMsg_p *, unsigned); +virtual unsigned ConfigDistribute (PMsg_p *, unsigned); +virtual unsigned ConfigRecall (PMsg_p *, unsigned); +virtual unsigned ConfigState (PMsg_p *, unsigned); +// various types of dumps +virtual unsigned DumpAll (PMsg_p *, unsigned); +virtual unsigned DumpSummary (PMsg_p *, unsigned); +virtual unsigned DumpTask (PMsg_p *, unsigned); +// queries, organised by general type (device, supervisor) +virtual unsigned QueryDevIAll (PMsg_p *, unsigned); +virtual unsigned QueryDevIByName (PMsg_p *, unsigned); +virtual unsigned QueryDevIByID (PMsg_p *, unsigned); +virtual unsigned QuerySupv (PMsg_p *, unsigned); +// forwarded messages containing POETS packets. Most of +// these should do nothing in SBase because the behaviour +// will depend upon the process class +virtual unsigned SendAttr (PMsg_p *, unsigned); +virtual unsigned SendDevIAll (PMsg_p *, unsigned); +virtual unsigned SendDevIByName (PMsg_p *, unsigned); +virtual unsigned SendDevIByID (PMsg_p *, unsigned); +virtual unsigned SendDevT (PMsg_p *, unsigned); +virtual unsigned SendExtn (PMsg_p *, unsigned); +virtual unsigned SendSupv (PMsg_p *, unsigned); + +// structures needed for CommonBase to register callbacks +typedef unsigned (SBase::*pMeth)(PMsg_p*, unsigned); +typedef map FnMap_t; +vector FnMapx; + +private: + +// low-level commands - usually called from a subcommand, that +// handle the nuts and bolts of the operations. +unsigned CmdIntegrity (PMsg_p *); +unsigned DataTask (PMsg_p *); +unsigned DataDevType (PMsg_p *); +unsigned DataDevice (PMsg_p *); +unsigned DataDeviceExternal (PMsg_p *); +unsigned DataExternal (PMsg_p *); +unsigned DataSupervisor (PMsg_p *); +unsigned QueryAttr (PMsg_p *, unsigned); +unsigned QueryDevT (PMsg_p *, unsigned); +unsigned QueryExtn (PMsg_p *, unsigned); +unsigned QueryList (PMsg_p *, unsigned); +unsigned QueryTask (PMsg_p *, unsigned); + +static const map StateVals; +static const map StateStrs; + +}; + +//============================================================================== + +#endif + diff --git a/Source/OrchBase/CMsg_p.cpp b/Source/OrchBase/CMsg_p.cpp index fc03a9b9..f87cf402 100644 --- a/Source/OrchBase/CMsg_p.cpp +++ b/Source/OrchBase/CMsg_p.cpp @@ -6,6 +6,21 @@ CMsg_p::CMsg_p(CMsg_p & r):PMsg_p(r){} CMsg_p::CMsg_p(PMsg_p & r):PMsg_p(r){} CMsg_p::~CMsg_p(void){} +void CMsg_p::Dump(FILE * fp) +{ + fprintf(fp,"--------------------------------------------------------------------------------\n"); + fprintf(fp," Distribution Message Dump\n\n"); + fprintf(fp,"Task: %s\n",Zname(0).c_str()); + fprintf(fp,"Binary path: %s\n\n",Zname(1).c_str()); + fprintf(fp,"---------------- Core Map -----------------\n"); + fprintf(fp,"Virtual core number Physical core\n\n"); + vector > coreMap; + Get(coreMap); + for (vector >::iterator core = coreMap.begin(); core != coreMap.end(); core++) + fprintf(fp, "%19d,Box:%d, Board:%d, Mailbox:%d, Core:%d\n",core->first,core->second.A_box,core->second.A_board,core->second.A_mailbox,core->second.A_core); + fprintf(fp,"________________________________________________________________________________\n"); + fflush(fp); +} template <> void CMsg_p::Dump >(FILE * fp) { diff --git a/Source/OrchBase/CMsg_p.h b/Source/OrchBase/CMsg_p.h index 267573e3..9aea69c9 100644 --- a/Source/OrchBase/CMsg_p.h +++ b/Source/OrchBase/CMsg_p.h @@ -14,20 +14,21 @@ class CMsg_p : public PMsg_p virtual ~CMsg_p(); + void Dump(FILE * = stdout); template void Dump(FILE * = stdout); - template inline T * Get(int k,int & cnt) {return (T*)0;} + template inline T * Get(int k,int & cnt) {return k ? (T*)0 : PMsg_p::Get(k,cnt);} pair * Get(int &); - inline void Get(int k,string & s) {if (!k) PMsg_p::Get(k,s);}; + inline void Get(int k,string & s) {return;} template inline void Get(int k,vector & vT) {return;} void Get(vector > &); - inline void GetX(int k,vector & vs) {return;}; + inline void GetX(int k,vector & vs) {return;} template inline int Put() {return -1;} - template void Put(int k,T * data,int cnt=1) {return;} + template void Put(int k,T * data,int cnt=1) {if (!k) PMsg_p::Put(k,data,cnt);} void Put(pair *,int=1); - inline void Put(int k,string * data) {if (!k) PMsg_p::Put(k,data);}; + inline void Put(int k,string * data) {return;} template inline void Put(int k,vector * data) {return;} void Put(vector > *); - inline void PutX(int k,vector * data) {return;}; + inline void PutX(int k,vector * data) {return;} }; diff --git a/Source/OrchBase/OrchBase.cpp b/Source/OrchBase/OrchBase.cpp index cff6faca..2a32ae16 100644 --- a/Source/OrchBase/OrchBase.cpp +++ b/Source/OrchBase/OrchBase.cpp @@ -14,8 +14,13 @@ #include "SimpleDeployer.h" #include "Dialect1Deployer.h" #include "MultiSimpleDeployer.h" -#include "Ns_el.h" +// #include "Ns_el.h" #include "P_super.h" +#include "poets_msg.h" // brings in constants to set hardware addresses. This should be rationalised. +#include "ABDefs.hpp" // TODO: integrate 11 July 2019 ADR +#include "ABRecord.hpp" +#include // debug +using namespace AddressBookNS; //============================================================================== // This class is chopped up into multiple .cpp files, because it's so diff --git a/Source/OrchBase/OrchBase.h b/Source/OrchBase/OrchBase.h index e01b89a6..8dac0e07 100644 --- a/Source/OrchBase/OrchBase.h +++ b/Source/OrchBase/OrchBase.h @@ -18,6 +18,7 @@ class Dialect1Deployer; #include "Environment.h" #include "CommonBase.h" #include "P_owner.h" +// include "Ns_el.h" using namespace std; #define TASK_DEPLOY_DIR ".orchestrator/task_binaries" // Relative to home. @@ -72,7 +73,7 @@ unsigned CmLink(Cli *); void LinkCons(Cli::Cl_t); void LinkDump(Cli::Cl_t); void LinkLink(Cli::Cl_t); -void LinkNser(Cli::Cl_t); +// void LinkNser(Cli::Cl_t); void LinkPath(Cli::Cl_t); void LinkPlac(Cli::Cl_t); void LinkUnli(Cli::Cl_t); diff --git a/Source/OrchBase/OrchBaseLink.cpp b/Source/OrchBase/OrchBaseLink.cpp index 26334cbb..c07e6d36 100644 --- a/Source/OrchBase/OrchBaseLink.cpp +++ b/Source/OrchBase/OrchBaseLink.cpp @@ -12,7 +12,7 @@ WALKVECTOR(Cli::Cl_t,pC->Cl_v,i) { if (strcmp(cs,"cons")==0) { LinkCons(*i); continue; } if (strcmp(cs,"dump")==0) { LinkDump(*i); continue; } if (strcmp(cs,"link")==0) { LinkLink(*i); continue; } - if (strcmp(cs,"nser")==0) { LinkNser(*i); continue; } + // if (strcmp(cs,"nser")==0) { LinkNser(*i); continue; } if (strcmp(cs,"path")==0) { LinkPath(*i); continue; } if (strcmp(cs,"plac")==0) { LinkPlac(*i); continue; } if (strcmp(cs,"unli")==0) { LinkUnli(*i); continue; } @@ -144,7 +144,164 @@ if (pT->linked) { // Task already linked? */ // Place and set the link flag if the placement was successful. -if (!pPlace->Place(pT)) pT->LinkFlag(); +// if (!pPlace->Place(pT)) pT->LinkFlag(); +if (!pPlace->Place(pT)) +{ + // send data to NameServer. Once the P_builder is threaded, this + // should be moved to the build command. + int nsRank = Q::NAP; + MPI_Comm nsComm = MPI_COMM_NULL; + vector >superRanks; + for (unsigned comm = 0; comm < Comms.size(); comm++) + { + WALKVECTOR(int, pPmap[comm]->U.Mothership, m) + superRanks.push_back(pair(comm, *m)); + if ((nsRank = pPmap[comm]->U.NameServer) != Q::NAP) + { + nsComm = Comms[comm]; + break; + } + } + if (nsRank == Q::NAP) + { + Post(711); // No nameserver. This should be fatal + return; // (need to return with a value from TaskLoad) + } + if (nsComm == MPI_COMM_NULL) + { + Post(712); // Unknown comm for nameserver. VERY fatal + return; // (should abort) + } + PMsg_p deviceData(nsComm); + vector supervisorNames; + vector supervisorAddrs; + vector supervisorRanks; + string supervisorType = pT->pSup->pP_devtyp->Name(); + vector supervisorAttrs; + vector >::iterator currSuperRank = superRanks.begin(); + deviceData.Src(Urank); + deviceData.Tgt(nsRank); + deviceData.Zname(0,pT->Name()); + WALKMAP(AddressComponent,P_box*,pE->P_boxm,box) + { + // find and set up supervisor devices + vector::iterator superIter = box->second->P_superv.begin(); + while (((*superIter) != pT->pSup) && (++superIter != box->second->P_superv.end())); + if (superIter == box->second->P_superv.end()) + { + Post(168,pT->Name()); + pT->LinkFlag(); // have to set the task as linked in order + Unlink(pT->Name()); // to unlink it, which needs to be done to clear + return; // the thread device list. + } + if (!((*superIter)->pP_devtyp)) + { + Post(169,(*superIter)->Name()); + pT->LinkFlag(); + Unlink(pT->Name()); + return; + } + P_addr sAddr = (*superIter)->addr; + supervisorNames.push_back((*superIter)->Name()); + // temporarily addresses must use old format. Later this should + // be changed to the definitive reference standard format. + supervisorAddrs.push_back((sAddr.A_box << P_BOX_OS) | + (sAddr.A_board << P_BOARD_OS) | + (sAddr.A_mailbox << P_MAILBOX_OS) | + (sAddr.A_core << P_CORE_OS) | + (sAddr.A_thread << P_THREAD_OS) | + (sAddr.A_device << P_DEVICE_OS)); + supervisorAttrs.push_back(0); + // For testing without Motherships, we will give the Supervisor + // a 'bad' rank. When running with a system that includes Motherships the + // commented code below can be selected. + if (currSuperRank == superRanks.end()) + supervisorRanks.push_back(0xFFFFFFFF); + /* { + Post(709,(*superIter)->Name(),box->first); + } + */ + else + { + // naïvely, we will assume that the MPI ranks are in box-address order. + // later, this should be fixed. + // TODO: extend the rank in a Record_t to include the comm. + supervisorRanks.push_back(currSuperRank->second); + ++currSuperRank; + } + // and then find all the 'normal' devices + WALKVECTOR(P_board*,box->second->P_boardv,board) + { + + for (map::node>::iterator mailbox = (*board)->G.index_n.begin();mailbox != (*board)->G.index_n.end();mailbox++) + { + WALKMAP(unsigned,P_core*,mailbox->second.data->P_corem,core) + { + WALKMAP(unsigned,P_thread*,core->second->P_threadm,thread) + { + if (thread->second->P_devicel.size() && (thread->second->P_devicel.front()->par->par == pT)) + { + vector devNames; + vector devAddrs; + // SymAddr_t devSuper; + // any given message will only contain one device type, corresponding + // to the one used in a particular thread. + string devTyp = thread->second->P_devicel.front()->pP_devtyp->Name(); + vector devAttrs; + // vector devices; + // RecordData_t deviceRecord; + // DTypeIdx devType = 0; + // deviceRecord.Supervisor = supervisorRecord.Address; + // deviceRecord.RecordType = Device; + // deviceRecord.DeviceType = devType; + WALKLIST(P_device*,thread->second->P_devicel,device) + { + P_addr dAddr = (*device)->addr; + // temporarily addresses must use old format. Later this should + // be changed to the definitive reference standard format. + // deviceRecord.Address = (dAddr.A_box << P_BOX_OS) | + // (dAddr.A_board << P_BOARD_OS) | + // (dAddr.A_core << P_CORE_OS) | + // (dAddr.A_thread << P_THREAD_OS) | + // (dAddr.A_device << P_DEVICE_OS); + devAddrs.push_back((dAddr.A_box << P_BOX_OS) | + (dAddr.A_board << P_BOARD_OS) | + (dAddr.A_mailbox << P_MAILBOX_OS) | + (dAddr.A_core << P_CORE_OS) | + (dAddr.A_thread << P_THREAD_OS) | + (dAddr.A_device << P_DEVICE_OS)); + devNames.push_back((*device)->Name()); + // devices.push_back(deviceRecord); + devAttrs.push_back(0); + } + deviceData.Key(Q::NAME,Q::DATA,Q::DEVI); + deviceData.Zname(2,devTyp); + deviceData.PutX(0,&devNames); + // deviceData.Put(1,&devices); + deviceData.Put(1,&devAddrs); + deviceData.Put(2,&devAttrs); + deviceData.Put(3,&supervisorAddrs.back()); + // ___________________________________________________________________ + // Post(713,int2str(deviceData.Tgt()),int2str(deviceData.Src()),thread->second->Name()); + deviceData.Send(); + } + } + } + } + } + } + deviceData.Key(Q::NAME,Q::DATA,Q::SUPV); + deviceData.Zname(2,supervisorType); + deviceData.PutX(0,&supervisorNames); + // deviceData.Put(1,&supervisorDevices); + deviceData.Put(1,&supervisorAddrs); + deviceData.Put(2,&supervisorAttrs); + deviceData.Put(3,&supervisorRanks); + // Post(714,int2str(deviceData.Tgt()),int2str(deviceData.Src()),pT->Name()); + deviceData.Send(); + pT->LinkFlag(); + pT->state = Linked; +} /* @@ -160,7 +317,7 @@ for(unsigned i=0;i || "*") 13 July 2019 ADR +string ufo = Cl.GetO(1); // Look for summary operator "-" +string ufn = Cl.GetP(1); +if (!(ufo.empty() || ufo == "-")) // reject other operators +{ + Post(25, ufo+ufs, "task /dump"); + return; +} +bool summary = false; +bool dumpNs = false; +if (!ufn.empty()) // look for dump qualifiers +{ + if (ufo == "-") // expect a summary with a "-" sign + { + if (ufn != "s") + { + Post(25, ufo+ufn, "task /dump"); // other switches are invalid + return; + } + summary = true; + ufn = "*"; // summary always outputs all tasks + } + ufo = Cl.GetO(2); + if (!(ufo.empty() || ufo == "-")) // reject other operators + { + Post(25, ufo+Cl.GetP(2), "task /dump"); + return; + } + if (ufo == "-") // expect a NameServer dump with a "-" sign + { + if (Cl.GetP(2) != "n") // look for the "-n" + { + Post(25, ufo+Cl.GetP(2), "task /dump"); // other switches are invalid + return; + } + dumpNs = true; + } + +} +else ufn = "*"; // default to dumping all tasks. +map::iterator task = P_taskm.begin(); // may need to find a specific task + // Just the task structures if (P_taskm.empty()) fprintf(fp,"Task map empty\n"); -else WALKMAP(string,P_task *,P_taskm,i) (*i).second->Dump(fp); -if (P_typdclm.empty()) fprintf(fp,"Type declaration map empty\n"); -else WALKMAP(string,P_typdcl *,P_typdclm,i) (*i).second->Dump(fp); -if (fp!=stdout) fclose(fp); +else +{ + if (ufn == "*") // all tasks + WALKMAP(string,P_task *,P_taskm,i) i->second->Dump(fp); + else // a named task given by parameter 1 + { + task = P_taskm.find(ufn); + if (task == P_taskm.end()) + { + Post(105,ufn); + fprintf(fp,"No such task: %s\n", ufn.c_str()); + } + else task->second->Dump(fp); + } +} +if (!summary) // print out type declares if not simply doing a summary +{ + if (P_typdclm.empty()) fprintf(fp,"Type declaration map empty\n"); + else + { + if (ufn == "*") // all type declarations + WALKMAP(string,P_typdcl *,P_typdclm,i) (*i).second->Dump(fp); + else if (task != P_taskm.end()) // just the type declaration for a named task + { + if (!task->second->pP_typdcl) // if it exists! + { + Post(113,ufn); + fprintf(fp,"Task %s has no type declarations\n", ufn.c_str()); + } + else task->second->pP_typdcl->Dump(fp); + } + } +} +// close the file BEFORE sending to NameServer, so that if it happens to be on the +// same machine, the dumpfile won't have multiple open handles. +if (fp!=stdout) fclose(fp); +if (dumpNs) // also dump NameServer info? +{ + int nsRank = Q::NAP; // search for NameServer + MPI_Comm nsComm = MPI_COMM_NULL; + for (unsigned comm = 0; comm < Comms.size(); comm++) + { + // find NameServer's comm + if ((nsRank = pPmap[comm]->U.NameServer) != Q::NAP) + { + nsComm = Comms[comm]; + break; + } + } + if (nsRank == Q::NAP) + { + Post(711); // No nameserver. This should be fatal + return; // (need to return with a value from TaskDump) + } + if (nsComm == MPI_COMM_NULL) + { + Post(712); // Unknown comm for nameserver. VERY fatal + return; // (should abort) + } + PMsg_p nsDump(nsComm); // set up a dump message + nsDump.Src(Urank); + nsDump.Tgt(nsRank); + // of the appropriate type (summary, all, or a named task) + if (summary) nsDump.Key(Q::NAME,Q::DUMP,Q::LIST); + if (ufn == "*") nsDump.Key(Q::NAME,Q::DUMP,Q::TASK,Q::ALL); + else + { + nsDump.Key(Q::NAME,Q::DUMP,Q::TASK,Q::NM); + nsDump.Zname(0,ufn); + } + nsDump.Zname(1,ufs); // NameServer should open the appropriate dumpfile + nsDump.Send(); +} } //------------------------------------------------------------------------------ @@ -312,6 +424,93 @@ if (Fn.Err()) Post(104,file); // If not, warn the monkey // pB->Build(pT); // Build the thing pB->Load(file); // Parse the xml description of the task //pB->Build(); // Build the thing + +// additions for NameServer/SBase 11 July 2019 ADR +int nsRank = Q::NAP; +MPI_Comm nsComm = MPI_COMM_NULL; +for (unsigned comm = 0; comm < Comms.size(); comm++) +{ + if ((nsRank = pPmap[comm]->U.NameServer) != Q::NAP) + { + nsComm = Comms[comm]; + break; + } +} +if (nsRank == Q::NAP) +{ + Post(711); // No nameserver. This should be fatal + return; // (need to return with a value from TaskLoad) +} +if (nsComm == MPI_COMM_NULL) +{ + Post(712); // Unknown comm for nameserver. VERY fatal + return; // (should abort) +} +PMsg_p nsTask(nsComm); +nsTask.Src(Urank); +WALKMAP(string, P_task*, P_taskm, task) +{ + if (task->second->state == Unknown) + { + nsTask.Key(Q::NAME,Q::DATA,Q::TASK); + nsTask.Zname(0,task->first); + nsTask.Zname(1,taskpath); + nsTask.Zname(2,file); + vector msgs; + WALKVECTOR(P_message*,task->second->pP_typdcl->P_messagev,msg) msgs.push_back((*msg)->Name()); + nsTask.PutX(0, &msgs); + // in future should place attributes here. + vector attrs; + attrs.push_back("_NO_ATTRIBUTE_"); + nsTask.PutX(1,&attrs); + vector counts; + unsigned long deviceCount = 0; + // tediously, we have to walk the graph because supervisors and externals + // must be disambiguated. + for (map::node>::iterator dn = task->second->pD->G.index_n.begin(); dn != task->second->pD->G.index_n.end(); dn++) + if (dn->second.data != task->second->pSup) ++deviceCount; + counts.push_back(deviceCount); + // it would be so much easier if we could do something like the following! + // counts.push_back(task->second->pD->G.SizeNodes()); + counts.push_back(0); // number of externals. In future this will need to be distinguished. + deviceCount = 0; + nsTask.Put(2,&counts); + nsTask.Send(nsRank); + nsTask.Clear(); + nsTask.Src(Urank); + nsTask.Key(Q::NAME,Q::DATA,Q::DEVT); + nsTask.Zname(0,task->first); + WALKVECTOR(P_devtyp*,task->second->pP_typdcl->P_devtypv,devType) + { + nsTask.Zname(2,(*devType)->Name()); + vectorinMsgs; + vectoroutMsgs; + for (unsigned m = 0; m < task->second->pP_typdcl->P_messagev.size(); m++) + { + WALKVECTOR(P_pintyp*,(*devType)->P_pintypIv,iPin) + { + if ((*iPin)->pMsg == task->second->pP_typdcl->P_messagev[m]) + { + inMsgs.push_back(m); + break; + } + } + WALKVECTOR(P_pintyp*,(*devType)->P_pintypOv,oPin) + { + if ((*oPin)->pMsg == task->second->pP_typdcl->P_messagev[m]) + { + outMsgs.push_back(m); + break; + } + } + } + nsTask.Put(0,&inMsgs); + nsTask.Put(1,&outMsgs); + nsTask.Send(nsRank); + } + task->second->state = Loaded; + } +} } //------------------------------------------------------------------------------ @@ -379,15 +578,12 @@ vector > coreVec; // core map container to send vector::iterator currBox = pPmap[cIdx]->vPmap.begin(); // process map for the Mothership being deployed to -CMsg_p PktC; -PMsg_p PktD; -//PMsg_p PktC, PktD; // messages to send to each participating box -PktC.Key(Q::NAME,Q::DIST); // distributing the core mappings -PktD.Key(Q::NAME,Q::TDIR); // and the data directory +CMsg_p PktC; // message to send to each participating box +PktC.Key(Q::NAME,Q::CFG,Q::DIST); // distributing the core mappings and the data directory PktC.Src(Urank); string taskname = task->first; -PktC.Put(0, &taskname); // first field in the packet is the task name -PktD.Put(0, &taskname); +PktC.Zname(0, taskname); // first string field in the packet is the task name +//PktC.Put(0, &taskname); // first field in the packet is the task name // duplicate P_builder's iteration through the task P_core* thisCore; // Core available during iteration. P_thread* firstThread; // The "first" thread in thisCore. "first" is arbitrary, because cores are stored in a map. @@ -470,7 +666,7 @@ std::vector commands; std::string target = string("/home/") + currBox->P_user + "/" + TASK_DEPLOY_DIR + "/" + task->first; std::string sourceBins = taskpath + task->first + "/" + BIN_PATH + "/*"; -PktD.Put(1,&target); +PktC.Zname(1,target); // second string field is the task binary directory if (RootProcMapI->P_proc == currBox->P_proc) { // then copy locally (inefficient, wasteful, using the files in place would be better but this would require @@ -507,19 +703,28 @@ WALKVECTOR(std::string, commands, command) return; } } - -PktD.comm = PktC.comm = Comms[cIdx]; // Packet will go on the communicator it was found on -printf("Sending a distribution message to mothership with %lu cores\n", coreVec.size()); -PktC.Put(&coreVec); // place the core map in the packet to this Mothership -/* -printf("Sending a single-core distribution message to mothership cores\n"); -PktC.Put(1,&(coreVec[0].first),1); -PktC.Put(2,&(coreVec[0].second),1); -*/ -PktC.Send(currBox->P_rank); // Send to the target Mothership -PktD.Send(currBox->P_rank); +PktC.comm = Comms[cIdx]; // Packet will go on the communicator it was found on +Post(726,int2str(currBox->P_rank),uint2str(coreVec.size())); +PktC.Put(0,&currBox->P_rank); // first field is the Mothership's address (rank or symbolic address) +PktC.Put(&coreVec); // place the core map in the packet to this Mothership +// PktC.Send(currBox->P_rank); // Send to the target Mothership +unsigned nsComm = 0; +for (; nsComm < pPmap.size(); nsComm++) // find the NameServer +{ + if (pPmap[nsComm]->U.NameServer != Q::NAP) + { + PktC.comm = Comms[nsComm]; // send the distribution message + PktC.Send(pPmap[nsComm]->U.NameServer); // to the NameServer + break; + } +} +if (nsComm >= pPmap.size()) // No NameServer. A severe error. +{ + Post(711); + return; +} } -} // Next Mothership (box) +} // Next Mothership } //------------------------------------------------------------------------------ diff --git a/Source/OrchBase/P_super.cpp b/Source/OrchBase/P_super.cpp index 3ae09bf1..5bd5b69d 100644 --- a/Source/OrchBase/P_super.cpp +++ b/Source/OrchBase/P_super.cpp @@ -74,6 +74,11 @@ void P_super::Attach(P_board* targetBoard) // Store the index in the board, so the board can access the supervisor by // querying its parent box. targetBoard->sup_offv.push_back(supervisorIndex); + // append the box address to the supervisor if it's not already there. + // added 12 July 2019 ADR + unsigned boxAddr; + if (!addr.GetBox(boxAddr)) + addr.SetBox(parentBox->get_hardware_address()->get_box()); } //------------------------------------------------------------------------------ diff --git a/Source/OrchBase/P_task.cpp b/Source/OrchBase/P_task.cpp index 72d3a73e..1851efa1 100644 --- a/Source/OrchBase/P_task.cpp +++ b/Source/OrchBase/P_task.cpp @@ -10,6 +10,7 @@ P_task::P_task(OrchBase * _p,string _s):par(_p),pSup(0),pD(0),pP_typdcl(0) Name(_s); // Save name Npar(_p); // Namebase parent linked = false; // Not linked yet +state = AddressBookNS::Unknown; // Don't know state yet (ADR 11 July 2019) pOwn = 0; // No owner yet pD = new D_graph(this,"D_graph"); } diff --git a/Source/OrchBase/P_task.h b/Source/OrchBase/P_task.h index 9fb7fbc3..b8d89023 100644 --- a/Source/OrchBase/P_task.h +++ b/Source/OrchBase/P_task.h @@ -4,6 +4,7 @@ #include #include "NameBase.h" #include "OrchBase.h" +#include "ABDefs.hpp" // TODO: take TaskState_t and refactor into a common .h class P_owner; //#include "P_owner.h" @@ -27,6 +28,7 @@ P_typdcl * pP_typdcl; // Declare block string filename; // XML source from HbD-land P_owner * pOwn; // Task owner bool linked; +AddressBookNS::TaskState_t state; // Task state (TODO: integrate this) struct PoL_t { public: diff --git a/Source/OrchBase/Placement.cpp b/Source/OrchBase/Placement.cpp index bd6a0274..325e1a3a 100644 --- a/Source/OrchBase/Placement.cpp +++ b/Source/OrchBase/Placement.cpp @@ -132,9 +132,9 @@ return false; void Placement::Xlink(P_device * pDe,P_thread * pTh) // Actually link a real device to a real thread { -printf("XLinking device %s with id %d to thread %s\n", +DebugPrint("XLinking device %s with id %d to thread %s\n", pDe->FullName().c_str(), pDe->addr.A_device, pTh->FullName().c_str()); -fflush(stdout); +// fflush(stdout); pDe->pP_thread = pTh; // Device to thread pTh->P_devicel.push_back(pDe); // Thread to device diff --git a/Source/OrchestratorMessages.txt b/Source/OrchestratorMessages.txt index 348aa1f6..e51cbb9b 100644 --- a/Source/OrchestratorMessages.txt +++ b/Source/OrchestratorMessages.txt @@ -76,6 +76,8 @@ 165(E) : "System command '%s' ran unsuccessfully. Aborting deployment." 166(E) : "Not enough mothership processes exist to deploy task %s. Is a mothership running?" 167(E) : "System command '%s' ran unsuccessfully with message '%s'. Aborting deployment." +168(E) : "Task %s's supervisor does not exist in the box supervisor list" +169(E) : "Supervisor %s does not have a device type - cannot configure NameServer" 508(U) : "Task %s in an unrecognised state. Reboot the mothership" 509(W) : "Task %s could not be loaded because task %s was already active in state %s" @@ -86,7 +88,7 @@ 515(W) : "Task %s not mapped to this mothership (%s)" 516(E) : "Boot thread could not be created for board %s for task %s" 517(W) : "Task %s could not be stopped: state is %s" -518(E) : "Unable to boot task completely: %s cores loaded, %s cores expected - aborting" +518(E) : "Unable to boot task %s completely: %s cores loaded, %s cores expected - aborting" 520(W) : "Hardware command ||%s|| to mothership %s failed" 520(W) : "Unrecognised Mothership system command %s" 530(E) : "Tinsel packet not received successfully on mothership %s" @@ -95,11 +97,54 @@ 533(E) : "Bad symbol %s in the Supervisor library on Mothership %s: check XML definition. Error was %s" 534(E) : "Supervisor library could not be successfully unloaded on Mothership %s. Future attempts to run tasks may have strange I/O behaviour" 540(I) : "Application Supervisor being started on Mothership %s" +550(E) : "Unexpected name services state configuration received on Mothership %s. Asked for state %s, actual state is %s" +560(I) : "Task %s booting..." +561(I) : "Task %s ready to be run" +562(I) : "Loading core %s on box %s, board %s" +563(I) : "%s of %s cores loaded. Loading board %s of %s" 600(I) : "%s" 601(I) : "Core %s: (part %s): %s" 607(W) : "Attempted to recall a task %s that was not deployed to the Mothership" +700(W) : "Unrecognised command %s to SBase process rank %s" +701(U) : "Bad command switch in SBase process rank %s" +702(W) : "Unrecognised query subtype %s to SBase process rank %s" +703(E) : "Unexpected reply to query: %s received at SBase process rank %s. Circular routing loop?" +704(E) : "Tried to load task into SBase at rank %s with no name" +705(E) : "Error while loading SBase data at rank %s: no task name" +706(E) : "Error %sing device type data for task %s in SBase at rank %s: no device type name" +707(E) : "Error loading device %s for task %s into SBase at rank %s" +708(E) : "Error loading device %s for task %s into SBase at rank %s: no such task" +709(W) : "Unable to register supervisor %s on box %s into SBase: not found in the rank list. May need to connect" +710(W) : "Empty task for SBase %s request at rank %s" +711(S) : "Error: no nameserver found" +712(U) : "Fatal error: nameserver on nonexistent comm" +713(I) : "Sending information to nameserver rank %s from root rank %s on devices on thread %s" +714(I) : "Sending information to nameserver rank %s from root rank %s on supervisors for task %s" +715(I) : "SBase rank %s received information from rank %s on devices for task %s" +716(E) : "Mismatch in device name-data info to SBase at rank %s: %s device names, %s %ss received" +717(E) : "Error sending device data to SBase at rank %s for device %s: invalid record type %s" +718(E) : "Error: no supervisor found for device records for task %s on SBase at rank %s" +719(E) : "Error loading device records for task %s on SBase at rank %s: no such device type %s" +720(W) : "Mismatch in number of device counts sent: %s, expected %s for task %s to SBase at rank %s" +721(E) : "Error %sing task %s on SBase at rank %s" +723(W) : "Task binary directory path to SBase at rank %s is empty. Current working directory will be used" +724(E) : "No task %s on SBase at rank %s" +725(E) : "Error attempting to set state for task %s on SBase at rank %s" +726(I) : "Deploying data to Mothership at rank %s: %s cores distributed" +727(W) : "SBase map on rank %s is invalid. Rebuild before performing tasks on this SBase" +728(E) : "Error: SBase at rank %s has a record for Supervisor rank %s, but no such Supervisor exists" +729(E) : "Supervisor %s registered as a device for Supervisor %s, but only one Supervisor can exist" +730(W) : "Invalid command %s to SBase rank %s: missing value %s" +731(E) : "Query made for a nameless or nonexistent device on SBase rank %s" +732(S) : "SBase at rank %s shows a Supervisor %s for device %s, but no associated %s" +733(E) : "Error looking up by message type on SBase at rank %s: no message type given" +734(W) : "Task %s is in an unknown state according to SBase at rank %s" +735(E) : "No device address received for device name %s to give to process at rank %s" +736(E) : "No associated supervisor address located for device %s to give to process at rank %s" +737(E) : "Mismatch in supervisor data received at rank %s from SBase: %s addresses, %s names, %s ranks" + 801(D) : "P_builder::Add(name=%s,file=%s)" 802(D) : "Removing task %s from the Orchestrator" 803(F) : "P_builder:out of memory to create a QCoreApplication object" diff --git a/Source/Softswitch/Makefile b/Source/Softswitch/Makefile index 34ebb494..7841194f 100644 --- a/Source/Softswitch/Makefile +++ b/Source/Softswitch/Makefile @@ -15,6 +15,7 @@ SOFTSWSRC = $(realpath $(SOFTSW_ROOT)/src) PCOMMONINC = $(realpath $(ORCH_ROOT)/Source/Common) GENERICSINC = $(realpath $(ORCH_ROOT)/Generics) +NAMESRVINC = $(realpath $(ORCH_ROOT)/Source/NameServer) ifndef RISCV_PATH export RISCV_PATH := /usr/local/riscv @@ -81,14 +82,15 @@ vpath mpi%.h $(MPICHINC) vpath opa_%.h $(MPICHINC) vpath PMsg_p.hpp $(PCOMMONINC) vpath OSFixes.hpp $(PCOMMONINC) +vpath ABDefs.hpp $(NAMESRVINC) vpath Supervisor.% $(realpath ../$(DEFAULT_GEN_DIR)) -VPATH := $(SOFTSWSRC):$(SOFTSWINC):$(TINSELLIB):$(TINSELINC):$(GENERICSINC) +VPATH := $(SOFTSWSRC):$(SOFTSWINC):$(TINSELLIB):$(TINSELINC):$(GENERICSINC):$(NAMESRVINC) # Local compiler flags CFLAGS = $(RV_CFLAGS) $(LH_CFLAGS) -O2 -I $(TINSELINC) -I $(SOFTSWINC) -I $(GENINC) -I $(PCOMMONINC) LDFLAGS = -melf32lriscv -G 0 -L$(RV_LIBS) -L$(RV_GCC_LIBS) -lgcc -lc -MPICFLAGS = -I$(SOFTSWINC) -I$(TINSELINC) -I$(MPICHINC) -I$(PCOMMONINC) -I$(GENERICSINC) -std=c++11 -fPIC -pipe -Wall +MPICFLAGS = -I$(SOFTSWINC) -I$(TINSELINC) -I$(MPICHINC) -I$(PCOMMONINC) -I$(GENERICSINC) -I$(NAMESRVINC) -std=c++11 -fPIC -pipe -Wall MPILDFLAGS = -shared -L$(MPICHLIB) SECEXPCPPS := $(GENSRC)/%.cpp diff --git a/Source/Softswitch/inc/poets_msg.h b/Source/Softswitch/inc/poets_msg.h index 1040ba63..c140f161 100644 --- a/Source/Softswitch/inc/poets_msg.h +++ b/Source/Softswitch/inc/poets_msg.h @@ -17,6 +17,7 @@ #define P_DEVICE_OS 0 #define P_THREAD_OS (LOG_DEVICES_PER_THREAD) #define P_CORE_OS (LOG_THREADS_PER_CORE+P_THREAD_OS) +#define P_MAILBOX_OS (TinselLogCoresPerMailbox+P_CORE_OS) #define P_BOARD_OS (LOG_CORES_PER_BOARD+P_CORE_OS) #define P_BOX_OS (LOG_BOARDS_PER_BOX+P_BOARD_OS) #define P_SUP_OS 31