diff --git a/Common/Libraries/ZMQPublisher/include/PyZMQPublisher.hpp b/Common/Libraries/ZMQPublisher/include/PyZMQPublisher.hpp new file mode 100644 index 0000000000000000000000000000000000000000..02b849654e653c82d7679b76dc1617e2b82c66fb --- /dev/null +++ b/Common/Libraries/ZMQPublisher/include/PyZMQPublisher.hpp @@ -0,0 +1,50 @@ +#ifndef __PYZMQPUBLISHER_HPP__ +#define __PYZMQPUBLISHER_HPP__ + +#include "ZMQPublisher.hpp" +#include + +namespace py = boost::python; + + +/** + * Wrapper for the above ZMQPublisher class in order to be used with boost_python. + */ +class PyZMQPublisher : public ZMQPublisher +{ +public: + using ZMQPublisher::ZMQPublisher; + + /** + * Re-definition of the base protected publish method as public. + * We cannot use "using" because it won't work with boost_python. + * @param payload, a json-ified string. + */ + void publish(const std::string& payload) override; +}; + + +/** + * Python module definition. + */ +BOOST_PYTHON_MODULE(libPyZMQPublisher) +{ + /** + * Expose DEFAULT_ADDRESS and DEFAULT_PORT to the Python module. + */ + py::object scope = py::scope(); + scope.attr("DEFAULT_ADDRESS") = DEFAULT_ADDRESS; + scope.attr("DEFAULT_PORT") = DEFAULT_PORT; + + /** + * Python PyZMQPublisher class definition. It is a wrapper of the PyZMQPublisher class defined above. + * Unlike the PyZMQPublisher class, the "publish" method is exposed as a private method "__publish". + * The actual "publish" method is defined inside the actual python module in order to convert a python dict() to a json-ified string. + * Take a look there for more info. + */ + py::class_("PyZMQPublisher", py::init()) + .def("__publish", &PyZMQPublisher::publish) + .def_readonly("topic", &ZMQPublisher::topic); +} + +#endif /*__PYZMQPUBLISHER_HPP__*/ diff --git a/Common/Libraries/ZMQPublisher/include/ZMQDictionary.hpp b/Common/Libraries/ZMQPublisher/include/ZMQDictionary.hpp new file mode 100644 index 0000000000000000000000000000000000000000..baa14364df3d520669d3c3f122b38c078b009882 --- /dev/null +++ b/Common/Libraries/ZMQPublisher/include/ZMQDictionary.hpp @@ -0,0 +1,8 @@ +#ifndef __ZMQDICTIONARY_HPP__ +#define __ZMQDICTIONARY_HPP__ + +#include + +using ZMQDictionary = nlohmann::json; + +#endif /*__ZMQDICTIONARY_HPP__*/ diff --git a/Common/Libraries/ZMQPublisher/include/ZMQPublisher.hpp b/Common/Libraries/ZMQPublisher/include/ZMQPublisher.hpp new file mode 100644 index 0000000000000000000000000000000000000000..59380b6a61dde142aeddfcb15a25c9752cc8b8f1 --- /dev/null +++ b/Common/Libraries/ZMQPublisher/include/ZMQPublisher.hpp @@ -0,0 +1,69 @@ +#ifndef __ZMQPUBLISHER_HPP__ +#define __ZMQPUBLISHER_HPP__ + +#include +#include +#include "ZMQDictionary.hpp" + +#define DEFAULT_ADDRESS std::string("127.0.0.1") +#define DEFAULT_PORT 16001 + + +/** + * This class implements a publisher object over a ZeroMQ socket, single topic only. + * It exposes a publish method which sends a dictionary of key, value format over the ZMQ socket. + */ +class ZMQPublisher +{ +public: + /** + * Constructors. Initializes the ZMQPublisher object with the given topic, address and port. + */ + ZMQPublisher(const std::string& topic, const std::string address, const unsigned int port); + ZMQPublisher(const std::string& topic) : ZMQPublisher(topic, DEFAULT_ADDRESS, DEFAULT_PORT) {}; + ZMQPublisher(const std::string& topic, const std::string address) : ZMQPublisher(topic, address, DEFAULT_PORT) {}; + ZMQPublisher(const std::string& topic, const unsigned int port) : ZMQPublisher(topic, DEFAULT_ADDRESS, port) {}; + + /** + * Destructor. + */ + ~ZMQPublisher(); + + /** + * Public publisher method. This method accepts a ZMQDictionary object reference, + * it converts it to a json-ified string and calls the protected publisher method. + * @param dictionary, a json-like dictionary containing tuples of key, value format. + */ + virtual void publish(const ZMQDictionary& dictionary); + + /** + * Name of the topic on which the messages will be sent. + */ + const std::string topic; + +protected: + /** + * Protected publisher method. This method sends a tuple (topic, payload) over the ZMQ socket. + * @param payload, a json-ified string. + */ + virtual void publish(const std::string& payload); + +private: + /** + * ZMQ constant buffer which references the topic name. + */ + const zmq::const_buffer m_topic; + + /** + * ZMQ context shared pointer. We use a pointer since the context must not be destroyed for communications to work properly. + */ + std::shared_ptr m_context; + + /** + * ZMQ socket shared pointer. We use a pointer since the socket must not be destroyed for communications to work properly. + */ + std::shared_ptr m_socket; +}; + + +#endif /*__ZMQPUBLISHER_HPP__*/ diff --git a/Common/Libraries/ZMQPublisher/src/Makefile b/Common/Libraries/ZMQPublisher/src/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..af0c2d51959a72c33866087ca1c4132436d8e15f --- /dev/null +++ b/Common/Libraries/ZMQPublisher/src/Makefile @@ -0,0 +1,95 @@ +#******************************************************************************* +# This Makefile follows VLT Standards (see Makefile(5) for more). +#******************************************************************************* +# REMARKS +# None +#------------------------------------------------------------------------ + +# +# user definable C-compilation flags +#USER_CFLAGS = + +# +# additional include and library search paths +#USER_INC = +USER_LIB = /usr/local/lib64/libzmq.so + +# +# MODULE CODE DESCRIPTION: +# ------------------------ +# As a general rule: public file are "cleaned" and "installed" +# local (_L) are not "installed". + +# +# C programs (public and local) +# ----------------------------- +EXECUTABLES = +EXECUTABLES_L = + + +PY_PACKAGES = ZMQPublisher +#PY_SCRIPTS = + + +# +# + + +# +# Includes (.h) files (public only) +# --------------------------------- +INCLUDES = ZMQDictionary.hpp ZMQPublisher.hpp PyZMQPublisher.hpp + +# +# Libraries (public and local) +# ---------------------------- +LIBRARIES = ZMQPublisher PyZMQPublisher +LIBRARIES_L = + +ZMQPublisher_OBJECTS = ZMQPublisher + +PyZMQPublisher_OBJECTS = PyZMQPublisher +PyZMQPublisher_LIBS = ZMQPublisher boost_python3 + + +# +# list of all possible C-sources (used to create automatic dependencies) +# ------------------------------ +CSOURCENAMES = \ + $(foreach exe, $(EXECUTABLES) $(EXECUTABLES_L), $($(exe)_OBJECTS)) \ + $(foreach rtos, $(RTAI_MODULES) , $($(rtos)_OBJECTS)) \ + $(foreach lib, $(LIBRARIES) $(LIBRARIES_L), $($(lib)_OBJECTS)) + +# +#>>>>> END OF standard rules + +# +# INCLUDE STANDARDS +# ----------------- + +MAKEDIRTMP := $(shell searchFile include/acsMakefile) +ifneq ($(MAKEDIRTMP),\#error\#) + MAKEDIR := $(MAKEDIRTMP)/include + include $(MAKEDIR)/acsMakefile +endif + +# +# TARGETS +# ------- +all: do_all + @echo " . . . 'all' done" + +clean : clean_all + @echo " . . . clean done" + +clean_dist : clean_all clean_dist_all + @echo " . . . clean_dist done" + +man : do_man + @echo " . . . man page(s) done" + +install : install_all + @echo " . . . installation done" + + +#___oOo___ diff --git a/Common/Libraries/ZMQPublisher/src/PyZMQPublisher.cpp b/Common/Libraries/ZMQPublisher/src/PyZMQPublisher.cpp new file mode 100644 index 0000000000000000000000000000000000000000..cad5451901a12a489888403c900bd4844ed95f26 --- /dev/null +++ b/Common/Libraries/ZMQPublisher/src/PyZMQPublisher.cpp @@ -0,0 +1,6 @@ +#include "PyZMQPublisher.hpp" + +void PyZMQPublisher::publish(const std::string& payload) +{ + ZMQPublisher::publish(payload); +} diff --git a/Common/Libraries/ZMQPublisher/src/ZMQPublisher.cpp b/Common/Libraries/ZMQPublisher/src/ZMQPublisher.cpp new file mode 100644 index 0000000000000000000000000000000000000000..8ec757f02ede3098a5f94b590ced77a3fb0e438b --- /dev/null +++ b/Common/Libraries/ZMQPublisher/src/ZMQPublisher.cpp @@ -0,0 +1,29 @@ +#include "ZMQPublisher.hpp" + +ZMQPublisher::ZMQPublisher(const std::string& topic, const std::string address, const unsigned int port) : + topic(topic), + m_topic(this->topic.data(), this->topic.size()), + m_context(std::make_shared()), + m_socket(std::make_shared(*m_context, zmq::socket_type::pub)) +{ + // Limit the outbound messages buffer to 2 (1 topic and 1 payload). This will assure we only send the last message after establishing a delayed connection. + m_socket->set(zmq::sockopt::conflate, true); + m_socket->connect("tcp://" + address + ":" + std::to_string(port)); +} + +ZMQPublisher::~ZMQPublisher() +{ + m_socket->close(); +} + +void ZMQPublisher::publish(const ZMQDictionary& dictionary) +{ + publish(dictionary.dump()); +} + +void ZMQPublisher::publish(const std::string& payload) +{ + std::string message = topic + " " + payload; + zmq::message_t zmq_message(message.data(), message.size()); + m_socket->send(zmq_message, zmq::send_flags::none); +} diff --git a/Common/Libraries/ZMQPublisher/src/ZMQPublisher/__init__.py b/Common/Libraries/ZMQPublisher/src/ZMQPublisher/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..4fe972da6c09c827c4f3fde7be3aeb566b844448 --- /dev/null +++ b/Common/Libraries/ZMQPublisher/src/ZMQPublisher/__init__.py @@ -0,0 +1,35 @@ +# The following lines will look for the libZMQPublisher in the +# $INTROOT/lib path instead of looking in the $INTROOT/lib/python/site-packages +# path. This is a workaround needed since the said library is a C++ shared +# library and it does not get automatically installed in the correct folder. +# By adding this workaround we're also able to rename the library with the +# module name ZMQPublisher, trimming the 'lib' header. I +# suggest to use this approach whenever a Python wrapper is needed. +import os +import sys +sys.path.append(os.path.join(os.environ['INTROOT'], 'lib')) +from libPyZMQPublisher import PyZMQPublisher, DEFAULT_ADDRESS, DEFAULT_PORT +sys.path.remove(os.path.join(os.environ['INTROOT'], 'lib')) +del os +del sys + +# Add the actual 'publish' public method to the ZMQPublisher class. +import json +def publish(self, dictionary: dict): + self.__publish(json.dumps(dictionary)) + +PyZMQPublisher.publish = publish + + +# This class acts as a class factory. This is a workaround in order to make use +# of Python optional arguments for a constructor method (or a class factory +# method, like '__new__'), since this is not possible to achieve with boost_python +# The '__new__' method simply returns an object of type PyZMQPublisher, +# boost_python wrapper of the actual c++ ZMQPublisher class. +class ZMQPublisher: + + def __new__(cls, topic: str, address: str=DEFAULT_ADDRESS, port: int=DEFAULT_PORT): + return PyZMQPublisher(topic, address, port) + + +__all__ = ['ZMQPublisher'] diff --git a/Common/Libraries/ZMQPublisher/tests/Makefile b/Common/Libraries/ZMQPublisher/tests/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..7d7b7bfcbfb1f22df1048c78790fd4f6e4bb2a6b --- /dev/null +++ b/Common/Libraries/ZMQPublisher/tests/Makefile @@ -0,0 +1,89 @@ +# CPP UNIT TESTING SETUP +#-------------- +GTEST_HOME=/usr/local/include/gtest +GMOCK_HOME=/usr/local/include/gmock +GTEST_LIBS=gtest gtest_main + +USER_INC=-I$(GTEST_HOME) -I$(GMOCK_HOME) +# END OF CPP UNIT TESTING SETUP +#--------------------- + +# DEFINE YOUR CPP UNIT TEST EXECUTABLES HERE as: +# +# EXECTUABLES_L = unittest +# unittest_OBJECTS = unittest +# unittest_LIBS = $(GTEST_LIBS) + +EXECUTABLES_L = example + +example_OBJECTS = example +example_LIBS = ZMQPublisher + +# END OF CUSTOMIZATION +# do not edit below this line +#---------------------------- + +CSOURCENAMES = \ + $(foreach exe, $(EXECUTABLES) $(EXECUTABLES_L), $($(exe)_OBJECTS)) \ + $(foreach rtos, $(RTAI_MODULES) , $($(rtos)_OBJECTS)) \ + $(foreach lib, $(LIBRARIES) $(LIBRARIES_L), $($(lib)_OBJECTS)) + +MAKEDIRTMP := $(shell searchFile include/acsMakefile) +ifneq ($(MAKEDIRTMP),\#error\#) + MAKEDIR := $(MAKEDIRTMP)/include + include $(MAKEDIR)/acsMakefile +endif + +# TEST TARGETS +#TODO: unittest(2) discover pyunit + +do_unit: all + @echo "running cpp unit tests" + ../bin/example --gtest_output=xml:results/cppexample.xml + +do_pyunit: + @echo "running python unit tests" + python -m unittest pyunit + +do_functional: + @echo "running python functional tests" + python -m unittest functional + +do_external: + @echo "running python external tests" + python -m unittest external + +clean_test: + rm -f results/*.xml + rm -f functional/*.pyc + rm -f pyunit/*.pyc + rm -f external/*.pyc + +unit: do_unit + @echo " . . . 'unit' done" + +pyunit: do_pyunit + @echo " . . . 'pyunit' done" + +functional: do_functional + @echo " . . . 'functional' done" + +external: do_external + @echo " . . . 'external' done" + +# TARGETS +# ------- +all: do_all + @echo " . . . 'all' done" + +clean : clean_all clean_test + @echo " . . . clean done" + +clean_dist : clean_all clean_dist_all clean_test + @echo " . . . clean_dist done" + +man : do_man + @echo " . . . man page(s) done" + +install : install_all + @echo " . . . installation done" diff --git a/Common/Libraries/ZMQPublisher/tests/example.cpp b/Common/Libraries/ZMQPublisher/tests/example.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e1290c8c1e6480266373442d57a905da3054e4e9 --- /dev/null +++ b/Common/Libraries/ZMQPublisher/tests/example.cpp @@ -0,0 +1,26 @@ +#include "ZMQPublisher.hpp" +#include +#include +#include + +int main() +{ + ZMQPublisher publisher("cpp"); + + std::cout << "Started cpp ZMQPublisher on topic '" << publisher.topic << "'" << std::endl; + + while(true) + { + ZMQDictionary dictionary; + dictionary["string"] = std::string("string value"); + char* c = strdup("char value"); + dictionary["charstring"] = c; + dictionary["integer"] = 5; + // This is a double + dictionary["timestamp"] = std::chrono::duration(std::chrono::system_clock::now().time_since_epoch()).count(); + + publisher.publish(dictionary); + + std::this_thread::sleep_for(std::chrono::seconds(1)); + } +} diff --git a/Common/Libraries/ZMQPublisher/tests/pyunit/example.py b/Common/Libraries/ZMQPublisher/tests/pyunit/example.py new file mode 100755 index 0000000000000000000000000000000000000000..5e5904c2577780a3683fc29f5c7067dcc50a8ca6 --- /dev/null +++ b/Common/Libraries/ZMQPublisher/tests/pyunit/example.py @@ -0,0 +1,20 @@ +#!/usr/bin/env python +import time +from ZMQPublisher import ZMQPublisher + +pub = ZMQPublisher("python") +#pub = ZMQPublisher("python", '127.0.0.1', 16001) + +print(f"Started Python ZMQPublisher on topic '{pub.topic}'") + +try: + while True: + dictionary = { + 'string': 'string value', + 'integer': 10, + 'timestamp': time.time() + } + pub.publish(dictionary) + time.sleep(1) +except KeyboardInterrupt: + print() diff --git a/SRT/CDB/alma/ANTENNA/Mount/Mount.xml b/SRT/CDB/alma/ANTENNA/Mount/Mount.xml index 3fba00d25ab72c97eef61a6d16d16dbfce8e7698..437ebf362da49ca37471c505801b8cc2d8cdd589 100644 --- a/SRT/CDB/alma/ANTENNA/Mount/Mount.xml +++ b/SRT/CDB/alma/ANTENNA/Mount/Mount.xml @@ -16,7 +16,7 @@ RepetitionCacheTime="7000000" RepetitionExpireTime="10000000" StatusSocketTimeout="250000" - StatusSocketDutyCycle="30000" + StatusSocketDutyCycle="5000" ControlSocketResponseTime="1000000" ControlSocketDutyCycle="30000" TimeSource="EXT"> diff --git a/SRT/Servers/SRTMount/include/ACUProtocol.h b/SRT/Servers/SRTMount/include/ACUProtocol.h index f7550a39d0a8ae42bdc1845bf26796a360111d1c..2142a3276cea3857a97c6f62a5f93aae85848b59 100644 --- a/SRT/Servers/SRTMount/include/ACUProtocol.h +++ b/SRT/Servers/SRTMount/include/ACUProtocol.h @@ -404,10 +404,11 @@ public: /**********************************************************/ inline TINT32 encoderElevation() const { return CACUProtocol::readStatusField(m_buffer,m_disp+28); } /***********************************************************/ - ACS::Time actualTime() const { + double actualMJD() const { return (double)CACUProtocol::readStatusField(m_buffer,m_disp+57); } + ACS::Time actualACSTime() const { IRA::CDateTime time; TIMEVALUE ret; - double mjd=(double)CACUProtocol::readStatusField(m_buffer,m_disp+57); - time.setMJD(mjd); time.getDateTime(ret); + //double mjd=(double)CACUProtocol::readStatusField(m_buffer,m_disp+57); + time.setMJD(actualMJD()); time.getDateTime(ret); return ret.value().value; } double actualTimeOffset() const { @@ -431,6 +432,9 @@ public: inline TUINT16 hour() const { return CACUProtocol::readStatusField(m_buffer,m_disp+81); } inline TUINT16 minute() const { return CACUProtocol::readStatusField(m_buffer,m_disp+83); } inline TUINT16 second() const { return CACUProtocol::readStatusField(m_buffer,m_disp+85); } + //inline double ptAzimuth() const { return CACUProtocol::readStatusField(m_buffer,m_disp+87)/1000000.0; } + //inline double ptElevation() const { return CACUProtocol::readStatusField(m_buffer,m_disp+91)/1000000.0; } + // Displacement 95, program track state, UINT16 /***************************************************************/ inline TErrorsTracking pointingErrors() const { return TErrorsTracking((TWORD)CACUProtocol::readStatusField(m_buffer,m_disp+97)); } inline TINT32 pTTTimeOffset() const { return CACUProtocol::readStatusField(m_buffer,m_disp+99); } diff --git a/SRT/Servers/SRTMount/include/CommonData.h b/SRT/Servers/SRTMount/include/CommonData.h index 7e44512d34aa5a13540be063c2722d6110ee4c07..ddcac2117b3db7043117b19b1282e2ce867ac90f 100644 --- a/SRT/Servers/SRTMount/include/CommonData.h +++ b/SRT/Servers/SRTMount/include/CommonData.h @@ -2,18 +2,20 @@ #define COMMONDATA_H_ /* **************************************************************************************************** */ -/* IRA Istituto di Radioastronomia */ -/* $Id: CommonData.h,v 1.9 2011-06-03 18:02:49 a.orlati Exp $ */ -/* */ -/* This code is under GNU General Public Licence (GPL). */ -/* */ -/* Who when What */ -/* Andrea Orlati(aorlati@ira.inaf.it) 30/09/2009 Creation */ +/* IRA Istituto di Radioastronomia */ +/* $Id: CommonData.h,v 1.9 2011-06-03 18:02:49 a.orlati Exp $ */ +/* */ +/* This code is under GNU General Public Licence (GPL). */ +/* */ +/* Who when What */ +/* Andrea Orlati(aorlati@ira.inaf.it) 30/09/2009 Creation */ +/* Giuseppe Carboni(giuseppe.carboni@inaf.it) 09/05/2024 Added ZMQPublisher */ #include #include #include #include "ACUProtocol.h" +#include "ZMQPublisher.hpp" using namespace IRA; @@ -292,6 +294,8 @@ public: inline const DWORD& getElevationStatusWord() const { return m_elevationStatusWord; } inline const Management::TSystemStatus& getMountStatus() const { return m_mountStatus; } + + ZMQDictionary getZMQDictionary(); private: /** diff --git a/SRT/Servers/SRTMount/include/DevIOs.h b/SRT/Servers/SRTMount/include/DevIOs.h index 2446e31829b729febd289f4ec2473f4662e3664a..9bce19af1299ef151c4c741560c045690d5d96b1 100644 --- a/SRT/Servers/SRTMount/include/DevIOs.h +++ b/SRT/Servers/SRTMount/include/DevIOs.h @@ -275,7 +275,7 @@ public: break; } case TIME : { - m_value=(T)data->pointingStatus()->actualTime(); + m_value=(T)data->pointingStatus()->actualACSTime(); break; } case STATUSLINESTATUS : { diff --git a/SRT/Servers/SRTMount/include/StatusSocket.h b/SRT/Servers/SRTMount/include/StatusSocket.h index 17cb3fbec7c9298390079e4599001e3972a92cff..67219f1f010bcb6dea05b15bcc2b2cd70574144b 100644 --- a/SRT/Servers/SRTMount/include/StatusSocket.h +++ b/SRT/Servers/SRTMount/include/StatusSocket.h @@ -19,6 +19,7 @@ #include "Configuration.h" #include "ACUProtocol.h" #include +#include "ZMQPublisher.hpp" /** * This class is inherited from the IRA::CSocket class. It takes charge of the management of the socket used by the component in order to get from remote interface of the SRT ACU the status messages.. @@ -39,7 +40,7 @@ public: virtual ~CStatusSocket(); /** - * This member function is used to enstablish and configure the communication channel to the ACU. + * This member function is used to enstablish and configure the communication channel to the ACU. * This must be the first call before using any other function of this class. * @param config pointer to the component configuration data structure * @param data pointer to the ACU data structure @@ -99,6 +100,8 @@ private: * Creates and setup the socket in charge of receiving status data from the ACU */ void createSocket() throw (ComponentErrors::SocketErrorExImpl); + + ZMQPublisher m_zmqPublisher; }; #endif /*STATUSSOCKET_H_*/ diff --git a/SRT/Servers/SRTMount/src/CommonData.cpp b/SRT/Servers/SRTMount/src/CommonData.cpp index 0035d6db90dfff77f351c7805f2463b7eddfb8fc..facbba115e030e24389b55c1d184f0f440d9b3ff 100644 --- a/SRT/Servers/SRTMount/src/CommonData.cpp +++ b/SRT/Servers/SRTMount/src/CommonData.cpp @@ -7,7 +7,9 @@ using namespace IRA; _IRA_LOGFILTER_IMPORT; -CCommonData::CCommonData() : m_statusBuffer(NULL), m_trackStack(CACUProtocol::PROGRAMTRACK_STACK_POSITIONS,false) +CCommonData::CCommonData() : + m_statusBuffer(NULL), + m_trackStack(CACUProtocol::PROGRAMTRACK_STACK_POSITIONS,false) { DWORD size=CACUProtocol::MESSAGE_FRAME_START_BYTE; @@ -230,7 +232,7 @@ void CCommonData::getAntennaErrors(double& azErr,double& elErr,ACS::Time& time) { azErr=azimuthStatus()->commandedPosition()-azimuthStatus()->actualPosition(); elErr=elevationStatus()->commandedPosition()-elevationStatus()->actualPosition(); - time=pointingStatus()->actualTime(); + time=pointingStatus()->actualACSTime(); } double CCommonData::getHWAzimuth(double destination,const Antenna::TSections& commandedSection,double azLowerLimit,double azUpperLimit,double azCwLimit) @@ -252,7 +254,7 @@ void CCommonData::getEncodersCoordinates(double& az,double& el,double& azOff,dou el=elevationStatus()->actualPosition(); azOff=azimuthStatus()->positionOffset(); elOff=elevationStatus()->positionOffset(); - time=pointingStatus()->actualTime(); + time=pointingStatus()->actualACSTime(); section=pointingStatus()->azimuthSector(); } @@ -331,6 +333,8 @@ void CCommonData::reBind() else { clearStatusWord(PROGRAM_TRACK_DATA_ERROR); } + + // bind the azimuth status word setAzimuthStatusWord(PRELIMIT_UP,m_azimuthStatus->warnings().preLimitUp()); setAzimuthStatusWord(PRELIMIT_DOWN,m_azimuthStatus->warnings().preLimitDown()); @@ -345,7 +349,8 @@ void CCommonData::reBind() setAzimuthStatusWord(ACTIVE,m_azimuthStatus->axisState()==CACUProtocol::STATE_ACTIVE); setAzimuthStatusWord(LOW_POWER_MODE,m_azimuthStatus->lowPowerMode()); setAzimuthStatusWord(STOWED,m_azimuthStatus->stowed()); - // bind the azimuth status word + + // bind the elevation status word setElevationStatusWord(PRELIMIT_UP,m_elevationStatus->warnings().preLimitUp()); setElevationStatusWord(PRELIMIT_DOWN,m_elevationStatus->warnings().preLimitDown()); setElevationStatusWord(FINAL_LIMIT_UP,m_elevationStatus->warnings().finLimitUp()); @@ -359,6 +364,7 @@ void CCommonData::reBind() setElevationStatusWord(ACTIVE,m_elevationStatus->axisState()==CACUProtocol::STATE_ACTIVE); setElevationStatusWord(LOW_POWER_MODE,m_elevationStatus->lowPowerMode()); setElevationStatusWord(STOWED,m_elevationStatus->stowed()); + //bind motors status word TWORD motorSelection,brakesOpen,powerModuleOk; motorSelection=m_azimuthStatus->motorSelection(); @@ -374,6 +380,7 @@ void CCommonData::reBind() setMotorsStatusWord(i,BUS_ERROR,m_azimuthMotors[i]->busError()); //setMotorsStatusWord(i,POSITION_ERROR,m_azimuthMotors[i]->positionError()); } + motorSelection=m_elevationStatus->motorSelection(); brakesOpen=m_elevationStatus->brakesOpen(); powerModuleOk=m_elevationStatus->powerModuleOk(); @@ -397,6 +404,7 @@ void CCommonData::reBind() setMotorsStatusWord(CACUProtocol::AZIMUTH_MOTORS+CACUProtocol::ELEVATION_MOTORS,MOTOR_SERVO_ERROR,m_cableWrapMotor->servoError()); setMotorsStatusWord(CACUProtocol::AZIMUTH_MOTORS+CACUProtocol::ELEVATION_MOTORS,SENSOR_ERROR,m_cableWrapMotor->sensorError()); setMotorsStatusWord(CACUProtocol::AZIMUTH_MOTORS+CACUProtocol::ELEVATION_MOTORS,BUS_ERROR,m_cableWrapMotor->busError()); + //setMotorsStatusWord(CACUProtocol::AZIMUTH_MOTORS+CACUProtocol::ELEVATION_MOTORS,POSITION_ERROR,m_cableWrapMotor->positionError()); //now let's compose the overall status..get it from : // m_statusSocketState; @@ -520,3 +528,176 @@ void CCommonData::reBind() _IRA_LOGFILTER_LOG(LM_CRITICAL,"CCommonData::reBind()","ELEVATION_SERVO_FAILURE"); } } + +ZMQDictionary CCommonData::getZMQDictionary() +{ + ZMQDictionary dictionary; + dictionary["timestamp"] = pointingStatus()->actualMJD(); + + dictionary["messageSyncError"] = getStatusWord() & (1 << STATUS_MESSAGE_SYNC_ERROR); + dictionary["statusLineError"] = getStatusWord() & (1 << STATUS_LINE_ERROR); + dictionary["controlLineError"] = getStatusWord() & (1 << CONTROL_LINE_ERROR); + dictionary["remoteControlDisabled"] = getStatusWord() & (1 << REMOTE_CONTROL_DISABLED); + dictionary["emergencyStop"] = getStatusWord() & (1 << EMERGENCY_STOP); + dictionary["mainPowerError"] = getStatusWord() & (1 << MAIN_POWER_ERROR); + dictionary["timeError"] = getStatusWord() & (1 << TIME_ERROR); + dictionary["programTrackError"] = getStatusWord() & (1 << PROGRAM_TRACK_DATA_ERROR); + dictionary["remoteCommandError"] = getStatusWord() & (1 << REMOTE_COMMAND_ERROR); + dictionary["statusSocketConnected"] = getStatusLineState() == Antenna::ACU_CNTD; + dictionary["controlSocketConnected"] = getControlLineState() == Antenna::ACU_CNTD; + dictionary["componentStatus"] = getMountStatus() == Management::MNG_OK ? "OK" : getMountStatus() == Management::MNG_WARNING ? "WARNING" : "FAILURE"; + dictionary["remainingTrackingPoints"] = pointingStatus()->pTTLength() - pointingStatus()->pTTCurrentIndex(); + dictionary["MJD"] = pointingStatus()->actualMJD(); + dictionary["MJDOffset"] = pointingStatus()->actualTimeOffset(); + dictionary["azimuthSector"] = pointingStatus()->azimuthSector() == Antenna::ACU_CW ? "CW" : "CCW"; + + dictionary["azimuth"] = ZMQDictionary(); + dictionary["azimuth"]["currentPosition"] = azimuthStatus()->actualPosition();; + dictionary["azimuth"]["currentRate"] = azimuthStatus()->actualVelocity(); + dictionary["azimuth"]["currentOffset"] = azimuthStatus()->positionOffset(); + dictionary["azimuth"]["currentPositionError"] = azimuthStatus()->commandedPosition() - azimuthStatus()->actualPosition(); + dictionary["azimuth"]["currentTrackingError"] = azimuthStatus()->positionError(); + dictionary["azimuth"]["commandedPosition"] = azimuthStatus()->commandedPosition(); + dictionary["azimuth"]["commandedRate"] = azimuthStatus()->commandedVelocity(); + dictionary["azimuth"]["commandedOffset"] = m_commandedAzOff; + dictionary["azimuth"]["preLimitUp"] = azimuthStatus()->warnings().preLimitUp(); + dictionary["azimuth"]["preLimitDown"] = azimuthStatus()->warnings().preLimitDown();; + dictionary["azimuth"]["finalLimitUp"] = azimuthStatus()->warnings().finLimitUp(); + dictionary["azimuth"]["finalLimitDown"] = azimuthStatus()->warnings().finLimitDown(); + dictionary["azimuth"]["rateLimit"] = azimuthStatus()->warnings().rateLimit(); + dictionary["azimuth"]["stowPinExtracted"] = azimuthStatus()->warnings().stowPinExtracted(); + dictionary["azimuth"]["encoderFailure"] = azimuthStatus()->errors().encoderFailure(); + dictionary["azimuth"]["brakeError"] = azimuthStatus()->errors().brakeError(); + dictionary["azimuth"]["servoError"] = azimuthStatus()->errors().servoError(); + dictionary["azimuth"]["ready"] = azimuthStatus()->axisReady(); + dictionary["azimuth"]["active"] = azimuthStatus()->axisState() == CACUProtocol::STATE_ACTIVE; + dictionary["azimuth"]["lowPowerMode"] = azimuthStatus()->lowPowerMode(); + dictionary["azimuth"]["stowed"] = azimuthStatus()->stowed(); + dictionary["azimuth"]["motors"] = std::vector(); + + TWORD motorSelection = azimuthStatus()->motorSelection(); + TWORD brakesOpen = azimuthStatus()->brakesOpen(); + TWORD powerModuleOk = azimuthStatus()->powerModuleOk(); + for(WORD i = 0; i < CACUProtocol::AZIMUTH_MOTORS; i++) + { + ZMQDictionary motorStatus; + motorStatus["id"] = i + 1; + motorStatus["position"] = m_azimuthMotors[i]->actualPosition(); + motorStatus["rpm"] = m_azimuthMotors[i]->actualVelocity(); + motorStatus["torque"] = m_azimuthMotors[i]->actualTorque(); + motorStatus["usage"] = m_azimuthMotors[i]->utilization(); + motorStatus["selected"] = (motorSelection & (1 << i)) != 0; + motorStatus["brakesOpen"] = (brakesOpen & (1 << i)) != 0; + motorStatus["powerModuleOk"] = (powerModuleOk & (1 << i)) == 0; + motorStatus["active"] = m_azimuthMotors[i]->active(); + motorStatus["servoError"] = m_azimuthMotors[i]->servoError(); + motorStatus["sensorError"] = m_azimuthMotors[i]->sensorError(); + motorStatus["bus"] = m_azimuthMotors[i]->busError(); + dictionary["azimuth"]["motors"].push_back(motorStatus); + } + + dictionary["elevation"] = ZMQDictionary(); + dictionary["elevation"]["currentPosition"] = elevationStatus()->actualPosition();; + dictionary["elevation"]["currentRate"] = elevationStatus()->actualVelocity(); + dictionary["elevation"]["currentOffset"] = elevationStatus()->positionOffset(); + dictionary["elevation"]["currentPositionError"] = elevationStatus()->commandedPosition() - elevationStatus()->actualPosition(); + dictionary["elevation"]["currentTrackingError"] = elevationStatus()->positionError(); + dictionary["elevation"]["commandedPosition"] = elevationStatus()->commandedPosition(); + dictionary["elevation"]["commandedRate"] = elevationStatus()->commandedVelocity(); + dictionary["elevation"]["commandedOffset"] = m_commandedElOff; + dictionary["elevation"]["preLimitUp"] = elevationStatus()->warnings().preLimitUp(); + dictionary["elevation"]["preLimitDown"] = elevationStatus()->warnings().preLimitDown(); + dictionary["elevation"]["finalLimitUp"] = elevationStatus()->warnings().finLimitUp(); + dictionary["elevation"]["finalLimitDown"] = elevationStatus()->warnings().finLimitDown(); + dictionary["elevation"]["rateLimit"] = elevationStatus()->warnings().rateLimit(); + dictionary["elevation"]["stowPinExtracted"] = elevationStatus()->warnings().stowPinExtracted(); + dictionary["elevation"]["encoderFailure"] = elevationStatus()->errors().encoderFailure(); + dictionary["elevation"]["brakeError"] = elevationStatus()->errors().brakeError(); + dictionary["elevation"]["servoError"] = elevationStatus()->errors().servoError(); + dictionary["elevation"]["ready"] = elevationStatus()->axisReady(); + dictionary["elevation"]["active"] = elevationStatus()->axisState() == CACUProtocol::STATE_ACTIVE; + dictionary["elevation"]["lowPowerMode"] = elevationStatus()->lowPowerMode(); + dictionary["elevation"]["stowed"] = elevationStatus()->stowed(); + dictionary["elevation"]["motors"] = std::vector(); + + motorSelection = elevationStatus()->motorSelection(); + brakesOpen = elevationStatus()->brakesOpen(); + powerModuleOk = elevationStatus()->powerModuleOk(); + for(WORD i = 0; i < CACUProtocol::ELEVATION_MOTORS; i++) + { + ZMQDictionary motorStatus; + motorStatus["id"] = i + 1; + motorStatus["position"] = m_elevationMotors[i]->actualPosition(); + motorStatus["rpm"] = m_elevationMotors[i]->actualVelocity(); + motorStatus["torque"] = m_elevationMotors[i]->actualTorque(); + motorStatus["usage"] = m_elevationMotors[i]->utilization(); + motorStatus["selected"] = (motorSelection & (1 << i)) != 0; + motorStatus["brakesOpen"] = (brakesOpen & (1 << i)) != 0; + motorStatus["powerModuleOk"] = (powerModuleOk & (1 << i)) == 0; + motorStatus["active"] = m_elevationMotors[i]->active(); + motorStatus["servoError"] = m_elevationMotors[i]->servoError(); + motorStatus["sensorError"] = m_elevationMotors[i]->sensorError(); + motorStatus["bus"] = m_elevationMotors[i]->busError(); + dictionary["elevation"]["motors"].push_back(motorStatus); + } + + std::vector axesModes(2); + getActualMode(axesModes[0], axesModes[1]); + for(size_t i = 0; i < axesModes.size(); i++) + { + std::string currentMode; + switch(axesModes[i]) + { + case Antenna::ACU_STANDBY: + currentMode = "STANDBY"; + break; + case Antenna::ACU_STOP: + currentMode = "STOP"; + break; + case Antenna::ACU_PRESET: + currentMode = "PRESET"; + break; + case Antenna::ACU_PROGRAMTRACK: + currentMode = "PROGRAMTRACK"; + break; + case Antenna::ACU_RATE: + currentMode = "RATE"; + break; + case Antenna::ACU_STOW: + currentMode = "STOW"; + break; + case Antenna::ACU_UNSTOW: + currentMode = "UNSTOW"; + break; + case Antenna::ACU_UNKNOWN: + currentMode = "UNKNOWN"; + break; + } + dictionary[i == 0 ? "azimuth" : "elevation"]["currentMode"] = currentMode; + } + + dictionary["cableWrap"] = ZMQDictionary(); + dictionary["cableWrap"]["currentPosition"] = cableWrapStatus()->actualPosition(); + dictionary["cableWrap"]["currentRate"] = cableWrapStatus()->actualVelocity(); + dictionary["cableWrap"]["currentTrackingError"] = cableWrapStatus()->positionError(); + + motorSelection = cableWrapStatus()->motorSelection(); + brakesOpen = cableWrapStatus()->brakesOpen(); + powerModuleOk = cableWrapStatus()->powerModuleOk(); + ZMQDictionary CWMotorStatus; + CWMotorStatus["id"] = 1; + CWMotorStatus["position"] = m_cableWrapMotor->actualPosition(); + CWMotorStatus["rpm"] = m_cableWrapMotor->actualVelocity(); + CWMotorStatus["torque"] = m_cableWrapMotor->actualTorque(); + CWMotorStatus["usage"] = m_cableWrapMotor->utilization(); + CWMotorStatus["selected"] = (motorSelection & (1 << 0)) != 0; + CWMotorStatus["brakesOpen"] = (brakesOpen & (1 << 0)) != 0; + CWMotorStatus["powerModuleOk"] = (powerModuleOk & (1 << 0)) == 0; + CWMotorStatus["active"] = m_cableWrapMotor->active(); + CWMotorStatus["servoError"] = m_cableWrapMotor->servoError(); + CWMotorStatus["sensorError"] = m_cableWrapMotor->sensorError(); + CWMotorStatus["bus"] = m_cableWrapMotor->busError(); + dictionary["cableWrap"]["motors"] = std::vector{CWMotorStatus}; + + return dictionary; +} diff --git a/SRT/Servers/SRTMount/src/Makefile b/SRT/Servers/SRTMount/src/Makefile index aa3f94a4825657d6e2b951d980cca3d8bb783d32..ca2eee6356c74ecd1d2a614044e8f03e9a2cc697 100644 --- a/SRT/Servers/SRTMount/src/Makefile +++ b/SRT/Servers/SRTMount/src/Makefile @@ -41,10 +41,10 @@ EXECUTABLES = EXECUTABLES_L = testACU statusLineTest testACU_OBJECTS = ACUProtocol CommandSocket StatusSocket Configuration CommonData testACU -testACU_LIBS = IRALibrary ComponentErrors AntennaErrors +testACU_LIBS = IRALibrary ComponentErrors AntennaErrors ZMQPublisher statusLineTest_OBJECTS = ACUProtocol StatusSocket Configuration CommonData statusLineTest -statusLineTest_LIBS = IRALibrary ComponentErrors +statusLineTest_LIBS = IRALibrary ComponentErrors ZMQPublisher # @@ -62,7 +62,7 @@ INCLUDES = LIBRARIES = SRTMountImpl #LIBRARIES_L = SRTMountImpl_OBJECTS = ACUProtocol CommandSocket StatusSocket Configuration CommonData SRTMountImpl WatchDog -SRTMountImpl_LIBS = SRTMountStubs MountStubs IRALibrary AntennaDefinitionsStubs ComponentErrors AntennaErrors ManagementErrors ManagmentDefinitionsStubs +SRTMountImpl_LIBS = SRTMountStubs MountStubs IRALibrary AntennaDefinitionsStubs ComponentErrors AntennaErrors ManagementErrors ManagmentDefinitionsStubs ZMQPublisher # # Configuration Database Files diff --git a/SRT/Servers/SRTMount/src/StatusSocket.cpp b/SRT/Servers/SRTMount/src/StatusSocket.cpp index a46bc0b5a2cfc968d232a5b200003c74ae5fcfa4..f7a9177874cb31d6f200fc1ffaf289abf3167520 100644 --- a/SRT/Servers/SRTMount/src/StatusSocket.cpp +++ b/SRT/Servers/SRTMount/src/StatusSocket.cpp @@ -8,7 +8,7 @@ using namespace ComponentErrors; _IRA_LOGFILTER_IMPORT; -CStatusSocket::CStatusSocket() +CStatusSocket::CStatusSocket() : m_zmqPublisher("mount") { m_pConfiguration=NULL; m_pData=NULL; @@ -48,11 +48,13 @@ void CStatusSocket::onReceive(DWORD Counter,bool &Boost) data->clearStatusWord(CCommonData::STATUS_LINE_ERROR); if (!m_btransferStarted) m_btransferStarted=true; m_wtimeOuts=0; + if ((len=m_protocol.syncBuffer(buffer,res,data->getStatusBuffer()))>0) { data->clearStatusWord(CCommonData::STATUS_MESSAGE_SYNC_ERROR); //Boost=true; if (m_customBind==NULL) { data->reBind(); + m_zmqPublisher.publish(data->getZMQDictionary()); } else { m_customBind((const CCommonData *)data); diff --git a/SRT/Servers/SRTMount/src/statusLineTest.cpp b/SRT/Servers/SRTMount/src/statusLineTest.cpp index 34939c2d6f4c4a5a5bc4c2b858ac8d0000d99550..c8c89102c0e8e460bb161b1d78b8790b84cca688 100644 --- a/SRT/Servers/SRTMount/src/statusLineTest.cpp +++ b/SRT/Servers/SRTMount/src/statusLineTest.cpp @@ -21,7 +21,7 @@ void binder(const CCommonData *data) CACUProtocol::TAxisStatus *azimuthStatus=data->azimuthStatus(); CACUProtocol::TAxisStatus *elevationStatus=data->elevationStatus(); CACUProtocol::TPointingStatus * pointingStatus=data->pointingStatus(); - currentTime=pointingStatus->actualTime(); + currentTime=pointingStatus->actualACSTime(); IRA::CIRATools::timeToStr(currentTime,strTime); printf("Time: %s\n",(const char *)strTime); if (pastTime!=0) {