blob: 1c273551fc524bc859867ce949e4cfc3ccd5a1dd [file] [log] [blame]
// 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