source: trunk/FACT++/src/DimWriteStatistics.cc@ 11944

Last change on this file since 11944 was 11893, checked in by tbretz, 14 years ago
Implemented a new determination fo run-number after noon in the fadctrl; created the possibility to add the date to the raw-data path; allow to set the path as program option; for all this moved some code from the datalogger to DimWriteStatistics
File size: 8.6 KB
Line 
1//*************************************************************************************
2/** @class DimWriteStatistics
3
4 @brief provides a statistics service telling the free space on disk and the total size written so far
5
6*/
7//*************************************************************************************
8#include "DimWriteStatistics.h"
9
10#include <sys/statvfs.h> //for getting disk free space
11#include <sys/stat.h> //for getting files sizes
12
13#include <boost/filesystem.hpp>
14
15#include "Time.h"
16
17using namespace std;
18using namespace boost::posix_time;
19
20// --------------------------------------------------------------------------
21//
22//! Constructor with correct service name. The state machine using this object should give it
23//! its own name as a parameter
24//! @param serverName the name of the server which created this object
25//
26DimWriteStatistics::DimWriteStatistics(const string& server, MessageImp &log) :
27 fLog(log),
28 fDimService(server + "/STATS", "X:4", "Statistics about size written"),
29 fCurrentFolder("."),
30 fUpdateInterval(1000),
31 fBaseSize(0),
32 fDebug(false)
33{
34 fThread = boost::thread(boost::bind(&DimWriteStatistics::UpdateService, this));
35}
36
37// --------------------------------------------------------------------------
38//
39//! Destructor. Stop thread by setting fUpdateInterval to 0 and join the
40//! thread.
41//
42DimWriteStatistics::~DimWriteStatistics()
43{
44 fUpdateInterval = 0;
45
46 // This blocks for fPeriod duration, but maybe canceling the thread
47 // could be more dangerous leaving Dim in an undefined state.
48 fThread.interrupt();
49}
50
51int DimWriteStatistics::Write(const Time &t, const string &txt, int qos)
52{
53 return fLog.Write(t, txt, qos);
54}
55
56// --------------------------------------------------------------------------
57//
58//! Retrieves the free space of the current base path
59//! @return the available free space on disk, in bytes
60//
61int64_t DimWriteStatistics::GetFreeSpace()
62{
63 struct statvfs vfs;
64 if (statvfs(fCurrentFolder.c_str(), &vfs))
65 return -1;
66
67 return vfs.f_bsize*vfs.f_bavail;
68}
69
70// --------------------------------------------------------------------------
71//
72//! Retrieves the size on disk of a given file, in bytes
73//! @param file the filename for which the size should be retrieved
74//! @return the size of the file, in bytes
75//
76int64_t DimWriteStatistics::GetFileSizeOnDisk(const string& file, MessageImp &log)
77{
78 errno = 0;
79 struct stat st;
80 if (!stat(file.c_str(), &st))
81 return st.st_size;
82
83 //ignoring error #2: no such file or directory is not an error for new files
84 if (errno == 0 || errno == 2)
85 return 0;
86
87 ostringstream str;
88 str << "stat() failed for '" << file << "': " << strerror(errno) << " [errno=" << errno << "]";
89 log.Error(str);
90
91 return -1;
92}
93
94// --------------------------------------------------------------------------
95//
96//! Check if a given path exists
97//! @param path the path to be checked
98//! @return whether or not the given path exists
99//
100bool DimWriteStatistics::DoesPathExist(const string &path, MessageImp &log)
101{
102 namespace fs = boost::filesystem;
103
104 const fs::path fullPath = fs::system_complete(fs::path(path));
105
106 if (!fs::exists(fullPath))
107 return false;
108
109 if (!fs::is_directory(fullPath))
110 {
111 log.Error("Path given for checking '" + path + "' designate a file name. Please provide a path name only");
112 return false;
113 }
114
115 if (access(path.c_str(), R_OK|W_OK|X_OK) != 0)
116 {
117 log.Error("Missing read, write or execute permissions on directory '" + path + "'");
118 return false;
119 }
120
121 return true;
122}
123
124// --------------------------------------------------------------------------
125//
126//! Check if a given path exists
127//! @param path the path to be checked
128//! @return whether or not the creation has been successfull
129//
130void DimWriteStatistics::CreateDirectory(string path)
131{
132 namespace fs = boost::filesystem;
133
134 //remove last '/', if present
135 if (path[path.size()-1] == '/')
136 path = path.substr(0, path.size()-1);
137
138 //create boost path
139 const fs::path fullPath = fs::system_complete(fs::path(path));
140
141 //if path does not exist, check if upper levels exist already
142 if (fs::exists(fullPath))
143 {
144 //if path already exist, make sure it does not designate a file (filenames cannot be checked if they do not exist)
145 if (fs::is_directory(fullPath))
146 return;
147
148 throw runtime_error("Path to be created is a file '" + path + "'");
149 }
150
151 // we're hitting "/", which SHOULD have existed...
152 if (path.size() <= 1)
153 throw runtime_error("Path to be created looks suspicious '"+path+"'");
154
155 CreateDirectory(path.substr(0, path.find_last_of('/')));
156
157 //path does not exist, and upper level have been created already by recusrion.
158 const mode_t rightsMask = S_IRWXU | S_IXGRP | S_IRGRP | S_IXOTH | S_IROTH; //everybody read, owner writes
159
160 if (mkdir(path.c_str(), rightsMask)==0)
161 return;
162
163 ostringstream str;
164 str << "mkdir() failed for '" << path << "': " << strerror(errno) << " [errno=" << errno << "]";
165 throw runtime_error(str.str());
166}
167
168// --------------------------------------------------------------------------
169//
170//! Sets the current folder
171//! @param folder the path to the folder
172//
173bool DimWriteStatistics::SetCurrentFolder(const string& folder)
174{
175 if (GetFreeSpace() == -1)
176 {
177 fLog.Error("statvfs() failed for '"+folder+"'... ignoring it.");
178 return false;
179 }
180
181 fCurrentFolder = folder;
182 return true;
183}
184
185// --------------------------------------------------------------------------
186//
187//! Updates the service. This is the function executed by the thread
188//
189void DimWriteStatistics::UpdateService()
190{
191 Time previousTime;
192 uint64_t previousSize = 0;
193
194 while (1)
195 {
196 if (fUpdateInterval==0)
197 {
198 boost::this_thread::interruption_point();
199 boost::this_thread::yield();
200 continue;
201 }
202
203 Stats data;
204
205 for (set<string>::const_iterator it = fOpenedFiles.begin(); it != fOpenedFiles.end(); it++)
206 data.sizeWritten += GetFileSizeOnDisk(*it);
207 data.sizeWritten -= fBaseSize;
208
209 const Time cTime = Time();
210
211 data.freeSpace = GetFreeSpace();
212 data.rateWritten = data.sizeWritten-previousSize;
213 data.timeElapsed = (cTime - previousTime).total_milliseconds();
214
215 previousSize = data.sizeWritten;
216 previousTime = cTime;
217
218 fDimService.Update(data);
219
220 fStats = data;
221
222 if (fDebug)
223 {
224 ostringstream str;
225 str << "Written: " << fStats.sizeWritten/1000 << " kB; writing rate: ";
226 str << fStats.rateWritten/fStats.timeElapsed << " kB/s; free space: ";
227 str << fStats.freeSpace/1000000 << " MB";
228 fLog.Debug(str);
229 }
230
231 boost::this_thread::sleep(milliseconds(fUpdateInterval));
232 }
233}
234// --------------------------------------------------------------------------
235//
236//! Let the object know that a new file has been opened
237//! @param fileName the full name of the file newly opened
238//! @return whether this file could be stated or not
239//
240bool DimWriteStatistics::FileOpened(const string& fileName)
241{
242 if (fOpenedFiles.find(fileName) != fOpenedFiles.end())
243 return false;
244
245 //Add a newly opened file, and remember its original size
246 const int64_t newSize = GetFileSizeOnDisk(fileName);
247 if (newSize == -1)
248 return false;
249
250 fBaseSize += newSize;
251 fOpenedFiles.insert(fileName);
252
253 return true;
254}
255// --------------------------------------------------------------------------
256//
257//! Set the debug mode on and off
258//! @param debug the new mode (true or false)
259//
260void DimWriteStatistics::SetDebugMode(bool debug)
261{
262 fDebug = debug;
263
264 if (fDebug)
265 fLog.Debug("Debug mode is now on.");
266}
267// --------------------------------------------------------------------------
268//
269//! Set the update of the service interval
270//! @param duration the duration between two services update, in second
271//
272void DimWriteStatistics::SetUpdateInterval(const int16_t duration)
273{
274 if (!finite(duration))
275 {
276 fLog.Error("Provided update interval is not a valid float... discarding.");
277 return;
278 }
279 if (uint16_t(duration) == fUpdateInterval)
280 {
281 fLog.Warn("Statistics update interval not modified. Supplied value already in use.");
282 return;
283 }
284
285 if (duration <= 0)
286 fLog.Message("Statistics are now OFF.");
287 else
288 {
289 ostringstream str;
290 str << "Statistics update interval is now " << duration << " seconds";
291 fLog.Message(str);
292 }
293
294 fUpdateInterval = duration<0 ? 0 : duration;
295}
Note: See TracBrowser for help on using the repository browser.