001 /*
002 * Copyright (C) 2001 Ciaran Treanor <ciaran@codeloop.com>
003 *
004 * Distributable under GPL license.
005 * See terms of license at gnu.org.
006 *
007 * $Id: Archive.java,v 1.3 2006/12/21 18:02:42 tarus Exp $
008 */
009 package org.jrobin.core.jrrd;
010
011 import java.io.IOException;
012 import java.io.PrintStream;
013 import java.text.DecimalFormat;
014 import java.text.NumberFormat;
015 import java.text.SimpleDateFormat;
016 import java.util.ArrayList;
017 import java.util.Calendar;
018 import java.util.Date;
019 import java.util.Iterator;
020
021 /**
022 * Instances of this class model an archive section of an RRD file.
023 *
024 * @author <a href="mailto:ciaran@codeloop.com">Ciaran Treanor</a>
025 * @version $Revision: 1.3 $
026 */
027 public class Archive {
028
029 RRDatabase db;
030 long offset;
031 long dataOffset;
032 long size;
033 ConsolidationFunctionType type;
034 int rowCount;
035 int pdpCount;
036 double xff;
037 ArrayList<CDPStatusBlock> cdpStatusBlocks;
038 int currentRow;
039
040 private double[][] values;
041
042 Archive(RRDatabase db) throws IOException {
043
044 this.db = db;
045
046 RRDFile file = db.rrdFile;
047
048 offset = file.getFilePointer();
049 type =
050 ConsolidationFunctionType.get(file.readString(Constants.CF_NAM_SIZE));
051 rowCount = file.readInt();
052 pdpCount = file.readInt();
053
054 file.align();
055
056 xff = file.readDouble();
057
058 // Skip rest of rra_def_t.par[]
059 file.align();
060 file.skipBytes(72);
061
062 size = file.getFilePointer() - offset;
063 }
064
065 /**
066 * Returns the type of function used to calculate the consolidated data point.
067 *
068 * @return the type of function used to calculate the consolidated data point.
069 */
070 public ConsolidationFunctionType getType() {
071 return type;
072 }
073
074 void loadCDPStatusBlocks(RRDFile file, int numBlocks) throws IOException {
075
076 cdpStatusBlocks = new ArrayList<CDPStatusBlock>();
077
078 for (int i = 0; i < numBlocks; i++) {
079 cdpStatusBlocks.add(new CDPStatusBlock(file));
080 }
081 }
082
083 /**
084 * Returns the <code>CDPStatusBlock</code> at the specified position in this archive.
085 *
086 * @param index index of <code>CDPStatusBlock</code> to return.
087 * @return the <code>CDPStatusBlock</code> at the specified position in this archive.
088 */
089 public CDPStatusBlock getCDPStatusBlock(int index) {
090 return cdpStatusBlocks.get(index);
091 }
092
093 /**
094 * Returns an iterator over the CDP status blocks in this archive in proper sequence.
095 *
096 * @return an iterator over the CDP status blocks in this archive in proper sequence.
097 * @see CDPStatusBlock
098 */
099 public Iterator<CDPStatusBlock> getCDPStatusBlocks() {
100 return cdpStatusBlocks.iterator();
101 }
102
103 void loadCurrentRow(RRDFile file) throws IOException {
104 currentRow = file.readInt();
105 }
106
107 void loadData(RRDFile file, int dsCount) throws IOException {
108
109 dataOffset = file.getFilePointer();
110
111 // Skip over the data to position ourselves at the start of the next archive
112 file.skipBytes(8 * rowCount * dsCount);
113 }
114
115 DataChunk loadData(DataChunk chunk) throws IOException {
116
117 Calendar end = Calendar.getInstance();
118 Calendar start = (Calendar) end.clone();
119
120 start.add(Calendar.DATE, -1);
121
122 loadData(chunk, start.getTime().getTime() / 1000,
123 end.getTime().getTime() / 1000);
124 return chunk;
125 }
126
127 void loadData(DataChunk chunk, long startTime, long endTime)
128 throws IOException {
129
130 long pointer;
131
132 if (chunk.start < 0) {
133 pointer = currentRow + 1;
134 }
135 else {
136 pointer = currentRow + chunk.start + 1;
137 }
138
139 db.rrdFile.ras.seek(dataOffset + (pointer * 8));
140 //cat.debug("Archive Base: " + dataOffset + " Archive Pointer: " + pointer);
141 //cat.debug("Start Offset: " + chunk.start + " End Offset: "
142 // + (rowCount - chunk.end));
143
144 double[][] data = chunk.data;
145
146 /*
147 * This is also terrible - cleanup - CT
148 */
149 int row = 0;
150 for (int i = chunk.start; i < rowCount - chunk.end; i++, row++) {
151 if (i < 0) { // no valid data yet
152 for (int ii = 0; ii < chunk.dsCount; ii++) {
153 data[row][ii] = Double.NaN;
154 }
155 }
156 else if (i >= rowCount) { // past valid data area
157 for (int ii = 0; ii < chunk.dsCount; ii++) {
158 data[row][ii] = Double.NaN;
159 }
160 }
161 else { // inside the valid are but the pointer has to be wrapped
162 if (pointer >= rowCount) {
163 pointer -= rowCount;
164
165 db.rrdFile.ras.seek(dataOffset + (pointer * 8));
166 }
167
168 for (int ii = 0; ii < chunk.dsCount; ii++) {
169 data[row][ii] = db.rrdFile.readDouble();
170 }
171
172 pointer++;
173 }
174 }
175 }
176
177 void printInfo(PrintStream s, NumberFormat numberFormat, int index) {
178
179 StringBuffer sb = new StringBuffer("rra[");
180
181 sb.append(index);
182 s.print(sb);
183 s.print("].cf = \"");
184 s.print(type);
185 s.println("\"");
186 s.print(sb);
187 s.print("].rows = ");
188 s.println(rowCount);
189 s.print(sb);
190 s.print("].pdp_per_row = ");
191 s.println(pdpCount);
192 s.print(sb);
193 s.print("].xff = ");
194 s.println(xff);
195 sb.append("].cdp_prep[");
196
197 int cdpIndex = 0;
198
199 for (Iterator<CDPStatusBlock> i = cdpStatusBlocks.iterator(); i.hasNext();) {
200 CDPStatusBlock cdp = i.next();
201
202 s.print(sb);
203 s.print(cdpIndex);
204 s.print("].value = ");
205
206 double value = cdp.value;
207
208 s.println(Double.isNaN(value)
209 ? "NaN"
210 : numberFormat.format(value));
211 s.print(sb);
212 s.print(cdpIndex++);
213 s.print("].unknown_datapoints = ");
214 s.println(cdp.unknownDatapoints);
215 }
216 }
217
218 void toXml(PrintStream s) {
219
220 try {
221 s.println("\t<rra>");
222 s.print("\t\t<cf> ");
223 s.print(type);
224 s.println(" </cf>");
225 s.print("\t\t<pdp_per_row> ");
226 s.print(pdpCount);
227 s.print(" </pdp_per_row> <!-- ");
228 s.print(db.header.pdpStep * pdpCount);
229 s.println(" seconds -->");
230 s.print("\t\t<xff> ");
231 s.print(xff);
232 s.println(" </xff>");
233 s.println();
234 s.println("\t\t<cdp_prep>");
235
236 for (int i = 0; i < cdpStatusBlocks.size(); i++) {
237 cdpStatusBlocks.get(i).toXml(s);
238 }
239
240 s.println("\t\t</cdp_prep>");
241 s.println("\t\t<database>");
242
243 long timer = -(rowCount - 1);
244 int counter = 0;
245 int row = currentRow;
246
247 db.rrdFile.ras.seek(dataOffset + (row + 1) * 16);
248
249 long lastUpdate = db.lastUpdate.getTime() / 1000;
250 int pdpStep = db.header.pdpStep;
251 NumberFormat numberFormat = new DecimalFormat("0.0000000000E0");
252 SimpleDateFormat dateFormat =
253 new SimpleDateFormat("yyyy-MM-dd HH:mm:ss z");
254
255 while (counter++ < rowCount) {
256 row++;
257
258 if (row == rowCount) {
259 row = 0;
260
261 db.rrdFile.ras.seek(dataOffset);
262 }
263
264 long now = (lastUpdate - lastUpdate % (pdpCount * pdpStep))
265 + (timer * pdpCount * pdpStep);
266
267 timer++;
268
269 s.print("\t\t\t<!-- ");
270 s.print(dateFormat.format(new Date(now * 1000)));
271 s.print(" / ");
272 s.print(now);
273 s.print(" --> ");
274
275 for (int col = 0; col < db.header.dsCount; col++) {
276 s.print("<v> ");
277
278 double value = db.rrdFile.readDouble();
279
280 // NumberFormat doesn't know how to handle NaN
281 if (Double.isNaN(value)) {
282 s.print("NaN");
283 }
284 else {
285 s.print(numberFormat.format(value));
286 }
287
288 s.print(" </v>");
289 }
290
291 s.println("</row>");
292 }
293
294 s.println("\t\t</database>");
295 s.println("\t</rra>");
296 }
297 catch (IOException e) { // Is the best thing to do here?
298 throw new RuntimeException(e.getMessage());
299 }
300 }
301
302 /*
303 // THIS IS THE ORIGINAL CODE: BUGGY! Replaced by Sasa Markovic with a new method
304 // Funny: the bug will appear only if dsCount != 2 :)
305 public double[][] getValuesOriginal() throws IOException {
306 if (values != null) {
307 return values;
308 }
309 values = new double[db.header.dsCount][rowCount];
310 int row = currentRow;
311 db.rrdFile.ras.seek(dataOffset + (row + 1) * 16); // <----- BUG (resolved below)
312 for (int counter = 0; counter < rowCount; counter++) {
313 row++;
314 if (row == rowCount) {
315 row = 0;
316 db.rrdFile.ras.seek(dataOffset);
317 }
318 for (int col = 0; col < db.header.dsCount; col++) {
319 double value = db.rrdFile.readDouble();
320 values[col][counter] = value;
321 }
322 }
323 return values;
324 }
325 */
326
327 // Resolved bug from the original method (see above)
328 public double[][] getValues() throws IOException {
329 // OK PART
330 if (values != null) {
331 return values;
332 }
333 values = new double[db.header.dsCount][rowCount];
334 int row = currentRow;
335 // HERE ARE THE DRAGONS!
336 db.rrdFile.ras.seek(dataOffset + (row + 1) * db.header.dsCount * 8);
337 // OK, TOO!
338 for (int counter = 0; counter < rowCount; counter++) {
339 row++;
340 if (row == rowCount) {
341 row = 0;
342 db.rrdFile.ras.seek(dataOffset);
343 }
344 for (int col = 0; col < db.header.dsCount; col++) {
345 double value = db.rrdFile.readDouble();
346 values[col][counter] = value;
347 }
348 }
349 return values;
350 }
351
352 /**
353 * Returns the number of primary data points required for a consolidated
354 * data point in this archive.
355 *
356 * @return the number of primary data points required for a consolidated
357 * data point in this archive.
358 */
359 public int getPdpCount() {
360 return pdpCount;
361 }
362
363 /**
364 * Returns the number of entries in this archive.
365 *
366 * @return the number of entries in this archive.
367 */
368 public int getRowCount() {
369 return rowCount;
370 }
371
372 /**
373 * Returns the X-Files Factor for this archive.
374 *
375 * @return the X-Files Factor for this archive.
376 */
377 public double getXff() {
378 return xff;
379 }
380
381 /**
382 * Returns a summary the contents of this archive.
383 *
384 * @return a summary of the information contained in this archive.
385 */
386 public String toString() {
387
388 StringBuffer sb = new StringBuffer("[Archive: OFFSET=0x");
389
390 sb.append(Long.toHexString(offset));
391 sb.append(", SIZE=0x");
392 sb.append(Long.toHexString(size));
393 sb.append(", type=");
394 sb.append(type);
395 sb.append(", rowCount=");
396 sb.append(rowCount);
397 sb.append(", pdpCount=");
398 sb.append(pdpCount);
399 sb.append(", xff=");
400 sb.append(xff);
401 sb.append(", currentRow=");
402 sb.append(currentRow);
403 sb.append("]");
404
405 for (Iterator<CDPStatusBlock> i = cdpStatusBlocks.iterator(); i.hasNext();) {
406 CDPStatusBlock cdp = i.next();
407
408 sb.append("\n\t\t");
409 sb.append(cdp.toString());
410 }
411
412 return sb.toString();
413 }
414 }