+// vi: ts=4 sw=4 noet:
+/*
+==================================================================================
+ Copyright (c) 2020 Nokia
+ Copyright (c) 2020 AT&T Intellectual Property.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+==================================================================================
+*/
+
+/*
+ Mnemonic: metrics.cpp
+ Abstract: Impementation of the metrics functions. (see metrics.hpp)
+
+
+ void Set_source( std::string new_source );
+ void Set_reporter( std::string new_reporter );
+ void Push_data( std::string key, double value );
+
+ Date: 20 July 2020
+ Author: E. Scott Daniels
+*/
+
+#include <string.h>
+#include <unistd.h>
+#include <time.h>
+
+#include <rmr/RIC_message_types.h>
+#ifndef RIC_METRICS
+ #define RIC_METRICS 120
+#endif
+
+#include <iostream>
+
+#include "msg_component.hpp"
+#include "message.hpp"
+#include "metrics.hpp"
+
+extern char* __progname; // runtime lib supplied since we don't get argv[0]
+
+
+
+namespace xapp {
+
+
+
+// ------ private ----------------------------------------------
+
+
+/*
+ Return the current time in milliseconds past the UNIX epoch.
+*/
+static long long now( void ) {
+ struct timespec ts;
+ long long now = 0;
+
+ clock_gettime( CLOCK_REALTIME, &ts );
+ now = (ts.tv_sec * 1000000) + (ts.tv_nsec/1000000); // convert nano to milli and bang onto seconds
+
+ return now;
+}
+
+
+/*
+ Build the payload that we'll send.
+ Accepts a pointer to the payload message component in the RMR message that we are
+ to fill in, along with the maxmimum length of the payload. Returns the length of
+ the payload that was acutally used.
+
+ Currently, the munchkin expects:
+ {
+ "reporter": "<string>",
+ "generator" "<string>",
+ "timestamp": double,
+ "data": [
+ {
+ "id": "<string>",
+ "type": "<string>",
+ "value": double
+ },...
+ ]
+ }
+*/
+int xapp::Metrics::build_payload( xapp::Msg_component payload, int max_len ) {
+ int used; // actual size of payload created
+ std::string generator;
+
+
+ if( data.compare( "" ) == 0 ) { // xAPP never pushed any data
+ return 0; // don't build, just bail
+ }
+
+ generator = source.compare( "" ) == 0 ? reporter : source;
+
+ used = snprintf( (char *) payload.get(), max_len,
+ "{ "
+ "\"reporter\": \"%s\", "
+ "\"generator\": \"%s\", "
+ "\"timestamp\": %lld, "
+ "\"data\": [ %s ] "
+ " }",
+ reporter.c_str(),
+ generator.c_str(),
+ now(),
+ data.c_str()
+ );
+
+ return used;
+}
+
+
+// --------------- builders/operators -------------------------------------
+
+/*
+ Create a new metrics "message" with a provided (empty) RMR message.
+*/
+xapp::Metrics::Metrics( std::shared_ptr<Message> msg ) :
+ msg( msg ),
+ reporter( __progname ),
+ source( "" ),
+ data( "" )
+{ /* empty body */ }
+
+/*
+ Build a metrics for a value source other than the calling xAPP
+*/
+xapp::Metrics::Metrics( std::shared_ptr<Message> msg, std::string msource ) :
+ msg( msg ),
+ reporter( __progname ),
+ source( msource ),
+ data( "" )
+{ /* empty body */ }
+
+/*
+ Build a metrics object that allows the xAPP to set it's reporter name
+ rather than assuming the programme name and the source.
+
+ The xAPP can pass "" as the msource if the intent is just to supply
+ an alternate programme name which is also the source.
+*/
+xapp::Metrics::Metrics( std::shared_ptr<Message> msg, std::string reporter, std::string msource ) :
+ msg( msg ),
+ reporter( reporter ),
+ source( msource ),
+ data( "" )
+{ /* empty body */ }
+
+// ------------------ copy and move support ---------------------------------
+
+/*
+ Copy builder. Given a source object instance (soi), create a copy.
+ Creating a copy should be avoided as it can be SLOW!
+*/
+xapp::Metrics::Metrics( const Metrics& soi ) {
+ msg = soi.msg;
+ data = soi.data;
+ source = soi.source;
+ reporter = soi.reporter;
+}
+
+/*
+ Assignment operator. Simiolar to the copycat, but "this" object exists and
+ may have data that needs to be released prior to making a copy of the soi.
+*/
+Metrics& xapp::Metrics::operator=( const Metrics& soi ) {
+ if( this != &soi ) { // cannot do self assignment
+ // anything that must be freed from 'this' must be done here
+
+ msg = soi.msg;
+ data = soi.data;
+ source = soi.source;
+ reporter = soi.reporter;
+ }
+
+ return *this;
+}
+
+/*
+ Move builder. Given a source object instance (soi), move the information from
+ the soi ensuring that the destriction of the soi doesn't trash things from
+ under us.
+*/
+xapp::Metrics::Metrics( Metrics&& soi ) {
+ msg = soi.msg; // capture pointers and copy data before setting soruce things to nil
+ data = soi.data;
+ source = soi.source;
+ reporter = soi.reporter;
+
+ soi.msg = NULL; // prevent closing of RMR stuff on soi destroy
+}
+
+/*
+ Move Assignment operator. Move the message data to the existing object
+ ensure the object reference is cleaned up, and ensuring that the source
+ object references are removed.
+*/
+Metrics& xapp::Metrics::operator=( Metrics&& soi ) {
+ if( this != &soi ) { // cannot do self assignment
+ // anything that needs to be freed/delted from 'this', must be done here
+
+ msg = soi.msg; // move pointers and values
+ data = soi.data;
+ source = soi.source;
+ reporter = soi.reporter;
+
+ soi.msg = NULL; // prevent bad things when source is destroyed
+ }
+
+ return *this;
+}
+
+/*
+ Destroyer.
+*/
+xapp::Metrics::~Metrics() {
+ msg = NULL;
+}
+
+
+// ---- setters -------------------------------------------------
+
+void xapp::Metrics::Set_source( std::string new_source ) {
+ source = new_source;
+}
+
+void xapp::Metrics::Set_reporter( std::string new_reporter ) {
+ reporter = new_reporter;
+}
+
+/*
+ Pushes the key/value pair onto the current data list.
+ This could be more efficent, but for now it is simple.
+*/
+void xapp::Metrics::Push_data( std::string key, double value ) {
+ char wbuf[1024];
+ char* sep = (char *) " ";
+
+ if( data.compare( "" ) != 0 ) { // first on the list, no need for preceeding comma
+ sep = (char *) ", ";
+ }
+
+ snprintf( wbuf, sizeof( wbuf ), "%s{ \"id\": \"%s\", \"value\": %.5f }", sep, key.c_str(), value );
+
+ data += std::string( wbuf );
+}
+
+// ------------------- getters ------------------------------------
+
+
+// ------- message sending ---------------------------------------
+
+/*
+ Send the message by building the payload and passing to RMR.
+ Returns the state of the send (true == good). If the xapp did not
+ push any data, no send is actually invoked, and true is reported.
+*/
+bool xapp::Metrics::Send( ) {
+ int used = 0; // actual num bytes used in the payload
+ bool state = true;
+
+ used = build_payload( msg->Get_payload(), msg->Get_available_size() - 1 );
+ if( used > 0 ) {
+ //fprintf( stderr, ">>>> sending payload: %s\n", (char *) msg->Get_payload().get() );
+ state = msg->Send_msg( RIC_METRICS, RMR_VOID_SUBID, used+1, NULL );
+ }
+
+ data = ""; // clear data after the send
+
+ return state;
+}
+
+
+
+} // namespace