VideoTools
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
gvdatabase.cpp
1 //
2 // Copyright (C) 2017 Graeme Walker
3 //
4 // This program is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation, either version 3 of the License, or
7 // (at your option) any later version.
8 //
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
13 //
14 // You should have received a copy of the GNU General Public License
15 // along with this program. If not, see <http://www.gnu.org/licenses/>.
16 // ===
17 //
18 // gvdatabase.cpp
19 //
20 
21 #include "gdef.h"
22 #include "gvdatabase.h"
23 #include "gfile.h"
24 #include "gpath.h"
25 #include "glog.h"
26 #include "gassert.h"
27 #include <fstream>
28 #include <sstream>
29 #include <algorithm>
30 
31 namespace Gv
32 {
33  class DatabaseRrd ;
34  class DatabaseWrapper ;
35 }
36 
37 /// \class Gv::DatabaseWrapper
38 /// A concrete Database class that aggregates up to one second intervals
39 /// and then delegates to (eg.) DatabaseRrd.
40 ///
42 {
43 public:
44  explicit DatabaseWrapper( const std::string & path ) ;
45  virtual ~DatabaseWrapper() ;
46  virtual void add( time_t , unsigned int n , unsigned int dx , unsigned int dy , unsigned int squelch , unsigned int apathy ) override ;
47  virtual std::string graph( std::string & , time_t start_time , time_t end_time ) override ;
48 
49 private:
50  DatabaseRrd * m_imp ;
51  unsigned int m_items ;
52  unsigned int m_total ;
53  unsigned int m_max ;
54  time_t m_time ;
55 } ;
56 
57 /// \class Gv::DatabaseRrd
58 /// Manages an rrdtool database under the Gv::Database interface.
59 ///
61 {
62 public:
63  explicit DatabaseRrd( const std::string & ) ;
64  ~DatabaseRrd() ;
65  bool exists() const ;
66  void create() ;
67  std::string graph( std::string & , time_t start_time , time_t end_time ) ;
68  void add( time_t time , unsigned int total , unsigned int items , unsigned int max ,
69  unsigned int dx , unsigned int dy , unsigned int squelch , unsigned int apathy ) ;
70 
71 private:
72  static void run( const std::string & s ) ;
73  static std::string png( const std::string & rrd ) ;
74  void writeGraph( time_t start_time , time_t end_time ) ;
75  void readGraph( std::string & data ) ;
76 
77 private:
78  std::string m_rrd_filename ;
79  std::string m_png_filename ;
80 } ;
81 
82 Gv::Database * Gv::Database::create( const std::string & path )
83 {
84  return new DatabaseWrapper( path ) ;
85 }
86 
87 Gv::DatabaseWrapper::DatabaseWrapper( const std::string & path ) :
88  m_imp(nullptr) ,
89  m_items(0U) ,
90  m_total(0U) ,
91  m_max(0U) ,
92  m_time(0)
93 {
94  if( !path.empty() )
95  m_imp = new DatabaseRrd( path ) ;
96 }
97 
98 Gv::DatabaseWrapper::~DatabaseWrapper()
99 {
100  delete m_imp ;
101 }
102 
103 void Gv::DatabaseWrapper::add( time_t time , unsigned int n , unsigned int dx , unsigned int dy , unsigned int squelch , unsigned int apathy )
104 {
105  if( m_imp == nullptr )
106  return ;
107 
108  if( !m_imp->exists() )
109  m_imp->create() ;
110 
111  // we guarantee no more than one data point per second (as per rrd),
112  // so generate an average and a maximum
113  //
114  if( m_time == 0 || time == m_time )
115  {
116  m_items++ ;
117  m_total += n ;
118  m_max = std::max(m_max,n) ;
119  }
120  else
121  {
122  m_imp->add( time , m_total , m_items , m_max , dx , dy , squelch , apathy ) ;
123  m_items = 0U ;
124  m_total = 0U ;
125  m_max = 0U ;
126  }
127  m_time = time ;
128 }
129 
130 std::string Gv::DatabaseWrapper::graph( std::string & buffer , time_t start_time , time_t end_time )
131 {
132  if( m_imp == nullptr ) return std::string() ;
133  return m_imp->graph( buffer , start_time , end_time ) ;
134 }
135 
136 // ==
137 
138 Gv::DatabaseRrd::DatabaseRrd( const std::string & rrd_filename ) :
139  m_rrd_filename(rrd_filename) ,
140  m_png_filename(png(rrd_filename))
141 {
142  G_LOG( "Gv::DatabaseRrd::ctor: rrd=[" << m_rrd_filename << "] png=[" << m_png_filename << "]" ) ;
143 }
144 
145 Gv::DatabaseRrd::~DatabaseRrd()
146 {
147 }
148 
149 std::string Gv::DatabaseRrd::png( const std::string & rrd )
150 {
151  G_ASSERT( !rrd.empty() ) ;
152  G::Path path( rrd ) ;
153  return (path.dirname()+(path.withoutExtension().basename()+".png")).str() ;
154 }
155 
156 bool Gv::DatabaseRrd::exists() const
157 {
158  return G::File::exists( m_rrd_filename ) ;
159 }
160 
161 void Gv::DatabaseRrd::create()
162 {
163  G_ASSERT( !m_rrd_filename.empty() ) ;
164 
166  std::ostringstream ss ;
167  ss
168  << "rrdtool create " << m_rrd_filename << " "
169  << "--start " << now.s << " "
170  << "--step 1 " // populated every second
171  << "DS:items:GAUGE:3:U:U "
172  << "DS:total:GAUGE:3:U:U " // 3 => value is 'unknown' if no value for 3s, U => no min/max
173  << "DS:maximum:GAUGE:3:U:U "
174  << "DS:squelch:GAUGE:3:U:U "
175  << "DS:apathy:GAUGE:3:U:U "
176  << "DS:dx:GAUGE:3:U:U "
177  << "DS:dy:GAUGE:3:U:U "
178  << "RRA:AVERAGE:0:1:108000 "
179  << "RRA:MAX:0:1:108000 " ;
180  // << "RRA:AVERAGE:0.5:10s:1y "
181  // << "RRA:MAX:0.5:10s:1y " ;
182 
183  G_LOG( "Gv::DatabaseRrd::create: " << ss.str() ) ;
184  run( ss.str() ) ;
185  if( !exists() )
186  throw std::runtime_error( "failed to create rrd" ) ;
187 }
188 
189 void Gv::DatabaseRrd::run( const std::string & s )
190 {
191  int rc = system( s.c_str() ) ;
192  if( rc != 0 )
193  G_WARNING_ONCE( "Gv::DatabaseRrd::run: command failed: [" << s << "]" ) ;
194 }
195 
196 void Gv::DatabaseRrd::add( time_t time , unsigned int total , unsigned int items , unsigned int max ,
197  unsigned int dx , unsigned int dy , unsigned int squelch , unsigned int apathy )
198 {
199  std::ostringstream ss ;
200  ss
201  << "rrdtool update " << m_rrd_filename << " "
202  << time << ":"
203  << items << ":"
204  << total << ":"
205  << max << ":"
206  << squelch << ":"
207  << apathy << ":"
208  << dx << ":"
209  << dy ;
210 
211  G_LOG( "Gv::DatabaseRrd::emit: " << ss.str() ) ;
212  run( ss.str() ) ;
213 }
214 
215 std::string Gv::DatabaseRrd::graph( std::string & data , time_t start_time , time_t end_time )
216 {
217  writeGraph( start_time , end_time ) ;
218  readGraph( data ) ;
219  return "image/png" ;
220 }
221 
222 void Gv::DatabaseRrd::writeGraph( time_t start_time , time_t end_time )
223 {
224  std::ostringstream ss ;
225  ss
226  << "rrdtool graph " << m_png_filename << " "
227  << "--start " << start_time << " " ;
228  if( end_time ) ss
229  << "--end " << end_time << " " ; ss
230  << "DEF:c=" << m_rrd_filename << ":maximum:MAX "
231  << "DEF:a=" << m_rrd_filename << ":apathy:AVERAGE "
232  << "LINE2:c#802020 LINE2:a#208020" ;
233  G_LOG( "Gv::DatabaseRrd::emitDatum: " << ss.str() ) ;
234  run( ss.str() ) ;
235 }
236 
237 void Gv::DatabaseRrd::readGraph( std::string & data )
238 {
239  std::stringstream ss ;
240  std::ifstream png( m_png_filename.c_str() ) ;
241  ss << png.rdbuf() ;
242  data = ss.str() ;
243 }
244 
A subsecond-resolution timestamp based on a time_t.
Definition: gdatetime.h:39
virtual std::string graph(std::string &, time_t start_time, time_t end_time) override
Creates a graph image in the supplied string buffer and returns the image type.
Definition: gvdatabase.cpp:130
An abstract interface for a timeseries database that holds video analysis statistics.
Definition: gvdatabase.h:37
virtual void add(time_t, unsigned int n, unsigned int dx, unsigned int dy, unsigned int squelch, unsigned int apathy) override
Adds a data point for an analysis value 'n' at time 't', together with the analysis parameters (image...
Definition: gvdatabase.cpp:103
static EpochTime now()
Returns the current epoch time.
A concrete Database class that aggregates up to one second intervals and then delegates to (eg...
Definition: gvdatabase.cpp:41
static bool exists(const Path &file)
Returns true if the file (directory, device etc.) exists.
Definition: gfile.cpp:132
static Database * create(const std::string &path)
Factory function.
Definition: gvdatabase.cpp:82
A Path object represents a file system path.
Definition: gpath.h:72
Manages an rrdtool database under the Gv::Database interface.
Definition: gvdatabase.cpp:60