... | ... |
@@ -46,11 +46,11 @@ |
46 | 46 |
import os |
47 | 47 |
import sys |
48 | 48 |
import signal |
49 |
-import subprocess |
|
50 | 49 |
import multiprocessing |
51 | 50 |
import time |
52 | 51 |
import json |
53 | 52 |
from urllib.request import urlopen |
53 |
+import rrdbase |
|
54 | 54 |
|
55 | 55 |
### ENVIRONMENT ### |
56 | 56 |
|
... | ... |
@@ -59,10 +59,7 @@ _SERVER_MODE = "primary" |
59 | 59 |
|
60 | 60 |
### DEFAULT AREDN NODE URL ### |
61 | 61 |
|
62 |
-# set url of the aredn node |
|
63 |
- |
|
64 |
-_DEFAULT_AREDN_NODE_URL = \ |
|
65 |
- "{your node url}" |
|
62 |
+_DEFAULT_AREDN_NODE_URL = "http://localnode.local.mesh/cgi-bin/status" |
|
66 | 63 |
|
67 | 64 |
### FILE AND FOLDER LOCATIONS ### |
68 | 65 |
|
... | ... |
@@ -77,13 +74,19 @@ _RRD_FILE = "/home/%s/database/arednsigData.rrd" % _USER |
77 | 74 |
|
78 | 75 |
### GLOBAL CONSTANTS ### |
79 | 76 |
|
80 |
-# max number of failed data requests allowed |
|
77 |
+# maximum number of failed data requests allowed |
|
81 | 78 |
_MAX_FAILED_DATA_REQUESTS = 2 |
82 |
-# AREDN node data request interval in seconds |
|
79 |
+# maximum number of http request retries allowed |
|
80 |
+_MAX_HTTP_RETRIES = 3 |
|
81 |
+# delay time between http request retries |
|
82 |
+_HTTP_RETRY_DELAY = 1.1199 |
|
83 |
+# interval in seconds between data requests |
|
83 | 84 |
_DEFAULT_DATA_REQUEST_INTERVAL = 60 |
84 | 85 |
# number seconds to wait for a response to HTTP request |
85 |
-_HTTP_REQUEST_TIMEOUT = 5 |
|
86 |
+_HTTP_REQUEST_TIMEOUT = 3 |
|
86 | 87 |
|
88 |
+# interval in seconds between database updates |
|
89 |
+_DATABASE_UPDATE_INTERVAL = 60 |
|
87 | 90 |
# chart update interval in seconds |
88 | 91 |
_CHART_UPDATE_INTERVAL = 600 |
89 | 92 |
# standard chart width in pixels |
... | ... |
@@ -97,13 +100,14 @@ _CHART_HEIGHT = 150 |
97 | 100 |
# turn on or off of verbose debugging information |
98 | 101 |
verboseMode = False |
99 | 102 |
debugMode = False |
103 |
+reportUpdateFails = False |
|
100 | 104 |
|
101 | 105 |
# The following two items are used for detecting system faults |
102 | 106 |
# and aredn node online or offline status. |
103 | 107 |
|
104 | 108 |
# count of failed attempts to get data from aredn node |
105 | 109 |
failedUpdateCount = 0 |
106 |
-# detected status of aredn node device |
|
110 |
+httpRetries = 0 |
|
107 | 111 |
nodeOnline = False |
108 | 112 |
|
109 | 113 |
# ip address of aredn node |
... | ... |
@@ -113,6 +117,9 @@ dataRequestInterval = _DEFAULT_DATA_REQUEST_INTERVAL |
113 | 117 |
# chart update interval |
114 | 118 |
chartUpdateInterval = _CHART_UPDATE_INTERVAL |
115 | 119 |
|
120 |
+# rrdtool database interface handler instance |
|
121 |
+rrdb = None |
|
122 |
+ |
|
116 | 123 |
### PRIVATE METHODS ### |
117 | 124 |
|
118 | 125 |
def getTimeStamp(): |
... | ... |
@@ -124,23 +131,6 @@ def getTimeStamp(): |
124 | 131 |
return time.strftime( "%m/%d/%Y %T", time.localtime() ) |
125 | 132 |
##end def |
126 | 133 |
|
127 |
-def getEpochSeconds(sTime): |
|
128 |
- """Convert the time stamp supplied in the supplied string |
|
129 |
- to seconds since 1/1/1970 00:00:00. |
|
130 |
- Parameters: |
|
131 |
- sTime - the time stamp to be converted must be formatted |
|
132 |
- as %m/%d/%Y %H:%M:%S |
|
133 |
- Returns: epoch seconds |
|
134 |
- """ |
|
135 |
- try: |
|
136 |
- t_sTime = time.strptime(sTime, '%m/%d/%Y %H:%M:%S') |
|
137 |
- except Exception as exError: |
|
138 |
- print('%s getEpochSeconds: %s' % (getTimeStamp(), exError)) |
|
139 |
- return None |
|
140 |
- tSeconds = int(time.mktime(t_sTime)) |
|
141 |
- return tSeconds |
|
142 |
-##end def |
|
143 |
- |
|
144 | 134 |
def setStatusToOffline(): |
145 | 135 |
"""Set the detected status of the aredn node to |
146 | 136 |
"offline" and inform downstream clients by removing input |
... | ... |
@@ -168,8 +158,10 @@ def terminateAgentProcess(signal, frame): |
168 | 158 |
signal, frame - dummy parameters |
169 | 159 |
Returns: nothing |
170 | 160 |
""" |
161 |
+ # Inform downstream clients by removing output data file. |
|
162 |
+ if os.path.exists(_OUTPUT_DATA_FILE): |
|
163 |
+ os.remove(_OUTPUT_DATA_FILE) |
|
171 | 164 |
print('%s terminating arednsig agent process' % getTimeStamp()) |
172 |
- setStatusToOffline() |
|
173 | 165 |
sys.exit(0) |
174 | 166 |
##end def |
175 | 167 |
|
... | ... |
@@ -182,6 +174,8 @@ def getNodeData(dData): |
182 | 174 |
Returns: True if successful, |
183 | 175 |
or False if not successful |
184 | 176 |
""" |
177 |
+ global httpRetries |
|
178 |
+ |
|
185 | 179 |
try: |
186 | 180 |
currentTime = time.time() |
187 | 181 |
response = urlopen(arednNodeUrl, timeout=_HTTP_REQUEST_TIMEOUT) |
... | ... |
@@ -197,15 +191,29 @@ def getNodeData(dData): |
197 | 191 |
# If no response is received from the device, then assume that |
198 | 192 |
# the device is down or unavailable over the network. In |
199 | 193 |
# that case return None to the calling function. |
200 |
- print("%s getNodeData: %s" % (getTimeStamp(), exError)) |
|
201 |
- return False |
|
202 |
- ##end try |
|
194 |
+ httpRetries += 1 |
|
195 |
+ |
|
196 |
+ if reportUpdateFails: |
|
197 |
+ print("%s " % getTimeStamp(), end='') |
|
198 |
+ if reportUpdateFails or verboseMode: |
|
199 |
+ print("http request failed (%d): %s" % \ |
|
200 |
+ (httpRetries, exError)) |
|
201 |
+ |
|
202 |
+ if httpRetries > _MAX_HTTP_RETRIES: |
|
203 |
+ httpRetries = 0 |
|
204 |
+ return False |
|
205 |
+ else: |
|
206 |
+ time.sleep(_HTTP_RETRY_DELAY) |
|
207 |
+ return getNodeData(dData) |
|
208 |
+ ## end try |
|
203 | 209 |
|
204 | 210 |
if debugMode: |
205 | 211 |
print(content) |
206 | 212 |
if verboseMode: |
207 |
- print("http request successful: %.4f sec" % requestTime) |
|
213 |
+ print("http request successful: "\ |
|
214 |
+ "%.4f seconds" % requestTime) |
|
208 | 215 |
|
216 |
+ httpRetries = 0 |
|
209 | 217 |
dData['content'] = content |
210 | 218 |
return True |
211 | 219 |
##end def |
... | ... |
@@ -306,125 +314,21 @@ def setNodeStatus(updateSuccess): |
306 | 314 |
# Set status and send a message to the log if the device |
307 | 315 |
# previously offline and is now online. |
308 | 316 |
if not nodeOnline: |
309 |
- print('%s node online' % getTimeStamp()) |
|
317 |
+ print('%s aredn node online' % getTimeStamp()) |
|
310 | 318 |
nodeOnline = True |
311 | 319 |
return |
312 |
- elif failedUpdateCount == _MAX_FAILED_DATA_REQUESTS - 1: |
|
320 |
+ else: |
|
321 |
+ # The last attempt failed, so update the failed attempts |
|
322 |
+ # count. |
|
323 |
+ failedUpdateCount += 1 |
|
324 |
+ |
|
325 |
+ if failedUpdateCount == _MAX_FAILED_DATA_REQUESTS: |
|
313 | 326 |
# Max number of failed data requests, so set |
314 | 327 |
# device status to offline. |
315 | 328 |
setStatusToOffline() |
316 |
- ## end if |
|
317 |
- failedUpdateCount += 1 |
|
318 | 329 |
##end def |
319 | 330 |
|
320 |
- ### DATABASE FUNCTIONS ### |
|
321 |
- |
|
322 |
-def updateDatabase(dData): |
|
323 |
- """ |
|
324 |
- Update the rrdtool database by executing an rrdtool system command. |
|
325 |
- Format the command using the data extracted from the aredn node |
|
326 |
- response. |
|
327 |
- Parameters: dData - dictionary object containing data items to be |
|
328 |
- written to the rr database file |
|
329 |
- Returns: True if successful, False otherwise |
|
330 |
- """ |
|
331 |
- |
|
332 |
- time = getEpochSeconds(dData['date']) |
|
333 |
- |
|
334 |
- # Format the rrdtool update command. |
|
335 |
- strFmt = "rrdtool update %s %s:%s:%s:%s:%s:%s:%s:%s" |
|
336 |
- strCmd = strFmt % (_RRD_FILE, time, dData['signal'], \ |
|
337 |
- dData['noise'], dData['snr'], '0', \ |
|
338 |
- '0', '0', '0') |
|
339 |
- |
|
340 |
- if debugMode: |
|
341 |
- print("%s" % strCmd) # DEBUG |
|
342 |
- |
|
343 |
- # Run the command as a subprocess. |
|
344 |
- try: |
|
345 |
- subprocess.check_output(strCmd, shell=True, \ |
|
346 |
- stderr=subprocess.STDOUT) |
|
347 |
- except subprocess.CalledProcessError as exError: |
|
348 |
- print("%s: rrdtool update failed: %s" % \ |
|
349 |
- (getTimeStamp(), exError.output)) |
|
350 |
- return False |
|
351 |
- |
|
352 |
- if verboseMode and not debugMode: |
|
353 |
- print("database update successful") |
|
354 |
- |
|
355 |
- return True |
|
356 |
-##end def |
|
357 |
- |
|
358 |
-def createGraph(fileName, dataItem, gLabel, gTitle, gStart, |
|
359 |
- lower, upper, addTrend, autoScale): |
|
360 |
- """Uses rrdtool to create a graph of specified node data item. |
|
361 |
- Parameters: |
|
362 |
- fileName - name of file containing the graph |
|
363 |
- dataItem - data item to be graphed |
|
364 |
- gLabel - string containing a graph label for the data item |
|
365 |
- gTitle - string containing a title for the graph |
|
366 |
- gStart - beginning time of the graphed data |
|
367 |
- lower - lower bound for graph ordinate #NOT USED |
|
368 |
- upper - upper bound for graph ordinate #NOT USED |
|
369 |
- addTrend - 0, show only graph data |
|
370 |
- 1, show only a trend line |
|
371 |
- 2, show a trend line and the graph data |
|
372 |
- autoScale - if True, then use vertical axis auto scaling |
|
373 |
- (lower and upper parameters are ignored), otherwise use |
|
374 |
- lower and upper parameters to set vertical axis scale |
|
375 |
- Returns: True if successful, False otherwise |
|
376 |
- """ |
|
377 |
- gPath = _CHARTS_DIRECTORY + fileName + ".png" |
|
378 |
- trendWindow = { 'end-1day': 7200, |
|
379 |
- 'end-4weeks': 172800, |
|
380 |
- 'end-12months': 604800 } |
|
381 |
- |
|
382 |
- # Format the rrdtool graph command. |
|
383 |
- |
|
384 |
- # Set chart start time, height, and width. |
|
385 |
- strCmd = "rrdtool graph %s -a PNG -s %s -e now -w %s -h %s " \ |
|
386 |
- % (gPath, gStart, _CHART_WIDTH, _CHART_HEIGHT) |
|
387 |
- |
|
388 |
- # Set the range and scaling of the chart y-axis. |
|
389 |
- if lower < upper: |
|
390 |
- strCmd += "-l %s -u %s -r " % (lower, upper) |
|
391 |
- elif autoScale: |
|
392 |
- strCmd += "-A " |
|
393 |
- strCmd += "-Y " |
|
394 |
- |
|
395 |
- # Set the chart ordinate label and chart title. |
|
396 |
- strCmd += "-v %s -t %s " % (gLabel, gTitle) |
|
397 |
- |
|
398 |
- # Show the data, or a moving average trend line over |
|
399 |
- # the data, or both. |
|
400 |
- strCmd += "DEF:dSeries=%s:%s:LAST " % (_RRD_FILE, dataItem) |
|
401 |
- if addTrend == 0: |
|
402 |
- strCmd += "LINE1:dSeries#0400ff " |
|
403 |
- elif addTrend == 1: |
|
404 |
- strCmd += "CDEF:smoothed=dSeries,%s,TREND LINE2:smoothed#006600 " \ |
|
405 |
- % trendWindow[gStart] |
|
406 |
- elif addTrend == 2: |
|
407 |
- strCmd += "LINE1:dSeries#0400ff " |
|
408 |
- strCmd += "CDEF:smoothed=dSeries,%s,TREND LINE2:smoothed#006600 " \ |
|
409 |
- % trendWindow[gStart] |
|
410 |
- |
|
411 |
- if debugMode: |
|
412 |
- print("%s" % strCmd) # DEBUG |
|
413 |
- |
|
414 |
- # Run the formatted rrdtool command as a subprocess. |
|
415 |
- try: |
|
416 |
- result = subprocess.check_output(strCmd, \ |
|
417 |
- stderr=subprocess.STDOUT, \ |
|
418 |
- shell=True) |
|
419 |
- except subprocess.CalledProcessError as exError: |
|
420 |
- print("rrdtool graph failed: %s" % (exError.output)) |
|
421 |
- return False |
|
422 |
- |
|
423 |
- if verboseMode: |
|
424 |
- print("rrdtool graph: %s\n" % result.decode('utf-8'), end='') |
|
425 |
- return True |
|
426 |
- |
|
427 |
-##end def |
|
331 |
+ ### GRAPH FUNCTIONS ### |
|
428 | 332 |
|
429 | 333 |
def generateGraphs(): |
430 | 334 |
"""Generate graphs for display in html documents. |
... | ... |
@@ -443,23 +347,27 @@ def generateGraphs(): |
443 | 347 |
|
444 | 348 |
# 24 hour stock charts |
445 | 349 |
|
446 |
- createGraph('24hr_signal', 'S', 'dBm', |
|
350 |
+ |
|
351 |
+ #### REPLACE WITH createRadGraph #### |
|
352 |
+ |
|
353 |
+ |
|
354 |
+ rrdb.createAutoGraph('24hr_signal', 'S', 'dBm', |
|
447 | 355 |
'RSSI\ -\ Last\ 24\ Hours', 'end-1day', 0, 0, 2, autoScale) |
448 |
- createGraph('24hr_snr', 'SNR', 'dB', |
|
356 |
+ rrdb.createAutoGraph('24hr_snr', 'SNR', 'dB', |
|
449 | 357 |
'SNR\ -\ Last\ 24\ Hours', 'end-1day', 0, 0, 2, autoScale) |
450 | 358 |
|
451 | 359 |
# 4 week stock charts |
452 | 360 |
|
453 |
- createGraph('4wk_signal', 'S', 'dBm', |
|
361 |
+ rrdb.createAutoGraph('4wk_signal', 'S', 'dBm', |
|
454 | 362 |
'RSSI\ -\ Last\ 4\ Weeks', 'end-4weeks', 0, 0, 2, autoScale) |
455 |
- createGraph('4wk_snr', 'SNR', 'dB', |
|
363 |
+ rrdb.createAutoGraph('4wk_snr', 'SNR', 'dB', |
|
456 | 364 |
'SNR\ -\ Last\ 4\ Weeks', 'end-4weeks', 0, 0, 2, autoScale) |
457 | 365 |
|
458 | 366 |
# 12 month stock charts |
459 | 367 |
|
460 |
- createGraph('12m_signal', 'S', 'dBm', |
|
368 |
+ rrdb.createAutoGraph('12m_signal', 'S', 'dBm', |
|
461 | 369 |
'RSSI\ -\ Past\ Year', 'end-12months', 0, 0, 2, autoScale) |
462 |
- createGraph('12m_snr', 'SNR', 'dB', |
|
370 |
+ rrdb.createAutoGraph('12m_snr', 'SNR', 'dB', |
|
463 | 371 |
'SNR\ -\ Past\ Year', 'end-12months', 0, 0, 2, autoScale) |
464 | 372 |
|
465 | 373 |
if verboseMode: |
... | ... |
@@ -471,12 +379,12 @@ def getCLarguments(): |
471 | 379 |
"""Get command line arguments. There are four possible arguments |
472 | 380 |
-d turns on debug mode |
473 | 381 |
-v turns on verbose debug mode |
474 |
- -t sets the aredn node query interval |
|
382 |
+ -p sets the aredn node query interval |
|
475 | 383 |
-u sets the url of the aredn nodeing device |
476 | 384 |
Returns: nothing |
477 | 385 |
""" |
478 | 386 |
global verboseMode, debugMode, dataRequestInterval, \ |
479 |
- arednNodeUrl |
|
387 |
+ arednNodeUrl, reportUpdateFails |
|
480 | 388 |
|
481 | 389 |
index = 1 |
482 | 390 |
while index < len(sys.argv): |
... | ... |
@@ -485,9 +393,13 @@ def getCLarguments(): |
485 | 393 |
elif sys.argv[index] == '-d': |
486 | 394 |
verboseMode = True |
487 | 395 |
debugMode = True |
396 |
+ elif sys.argv[index] == '-r': |
|
397 |
+ reportUpdateFails = True |
|
398 |
+ |
|
399 |
+ # Update period and url options |
|
488 | 400 |
elif sys.argv[index] == '-p': |
489 | 401 |
try: |
490 |
- dataRequestInterval = abs(int(sys.argv[index + 1])) |
|
402 |
+ dataRequestInterval = abs(float(sys.argv[index + 1])) |
|
491 | 403 |
except: |
492 | 404 |
print("invalid polling period") |
493 | 405 |
exit(-1) |
... | ... |
@@ -499,42 +411,48 @@ def getCLarguments(): |
499 | 411 |
index += 1 |
500 | 412 |
else: |
501 | 413 |
cmd_name = sys.argv[0].split('/') |
502 |
- print("Usage: %s [-d] [-v] [-p seconds] [-u url]" % cmd_name[-1]) |
|
414 |
+ print("Usage: %s [-v|d] [-p seconds] [-u url]" % cmd_name[-1]) |
|
503 | 415 |
exit(-1) |
504 | 416 |
index += 1 |
505 | 417 |
##end def |
506 | 418 |
|
507 |
-def main(): |
|
419 |
+def setup(): |
|
508 | 420 |
"""Handles timing of events and acts as executive routine managing |
509 | 421 |
all other functions. |
510 | 422 |
Parameters: none |
511 | 423 |
Returns: nothing |
512 | 424 |
""" |
513 |
- global dataRequestInterval |
|
425 |
+ global rrdb |
|
514 | 426 |
|
515 |
- signal.signal(signal.SIGTERM, terminateAgentProcess) |
|
516 |
- signal.signal(signal.SIGINT, terminateAgentProcess) |
|
427 |
+ ## Get command line arguments. |
|
428 |
+ getCLarguments() |
|
517 | 429 |
|
518 |
- print('===================') |
|
430 |
+ print('======================================================') |
|
519 | 431 |
print('%s starting up arednsig agent process' % \ |
520 | 432 |
(getTimeStamp())) |
521 | 433 |
|
522 |
- # last time output JSON file updated |
|
523 |
- lastDataRequestTime = -1 |
|
524 |
- # last time charts generated |
|
525 |
- lastChartUpdateTime = - 1 |
|
526 |
- # last time the rrdtool database updated |
|
527 |
- lastDatabaseUpdateTime = -1 |
|
528 |
- |
|
529 |
- ## Get command line arguments. |
|
530 |
- getCLarguments() |
|
531 |
- |
|
532 | 434 |
## Exit with error if rrdtool database does not exist. |
533 | 435 |
if not os.path.exists(_RRD_FILE): |
534 | 436 |
print('rrdtool database does not exist\n' \ |
535 | 437 |
'use createArednsigRrd script to ' \ |
536 | 438 |
'create rrdtool database\n') |
537 | 439 |
exit(1) |
440 |
+ |
|
441 |
+ signal.signal(signal.SIGTERM, terminateAgentProcess) |
|
442 |
+ signal.signal(signal.SIGINT, terminateAgentProcess) |
|
443 |
+ |
|
444 |
+ # Define object for calling rrdtool database functions. |
|
445 |
+ rrdb = rrdbase.rrdbase( _RRD_FILE, _CHARTS_DIRECTORY, _CHART_WIDTH, \ |
|
446 |
+ _CHART_HEIGHT, verboseMode, debugMode ) |
|
447 |
+## end def |
|
448 |
+ |
|
449 |
+def loop(): |
|
450 |
+ # last time output JSON file updated |
|
451 |
+ lastDataRequestTime = -1 |
|
452 |
+ # last time charts generated |
|
453 |
+ lastChartUpdateTime = -1 |
|
454 |
+ # last time the rrdtool database updated |
|
455 |
+ lastDatabaseUpdateTime = -1 |
|
538 | 456 |
|
539 | 457 |
## main loop |
540 | 458 |
while True: |
... | ... |
@@ -558,9 +476,15 @@ def main(): |
558 | 476 |
if result: |
559 | 477 |
writeOutputFile(dData) |
560 | 478 |
|
561 |
- # If write output file successful, update the database. |
|
562 |
- if result: |
|
563 |
- result = updateDatabase(dData) |
|
479 |
+ # At the rrdtool database update interval, update the database. |
|
480 |
+ if result and (currentTime - lastDatabaseUpdateTime > \ |
|
481 |
+ _DATABASE_UPDATE_INTERVAL): |
|
482 |
+ lastDatabaseUpdateTime = currentTime |
|
483 |
+ # If write output file successful, update the database. |
|
484 |
+ if result: |
|
485 |
+ result = rrdb.updateDatabase(dData['date'], \ |
|
486 |
+ dData['signal'], dData['noise'], dData['snr'], \ |
|
487 |
+ '0', '0', '0', '0') |
|
564 | 488 |
|
565 | 489 |
# Set the node status to online or offline depending on the |
566 | 490 |
# success or failure of the above operations. |
... | ... |
@@ -571,7 +495,7 @@ def main(): |
571 | 495 |
if currentTime - lastChartUpdateTime > chartUpdateInterval: |
572 | 496 |
lastChartUpdateTime = currentTime |
573 | 497 |
p = multiprocessing.Process(target=generateGraphs, args=()) |
574 |
- #p.start() |
|
498 |
+ p.start() |
|
575 | 499 |
|
576 | 500 |
# Relinquish processing back to the operating system until |
577 | 501 |
# the next update interval. |
... | ... |
@@ -586,9 +510,9 @@ def main(): |
586 | 510 |
if remainingTime > 0.0: |
587 | 511 |
time.sleep(remainingTime) |
588 | 512 |
## end while |
589 |
- return |
|
590 | 513 |
## end def |
591 | 514 |
|
592 | 515 |
if __name__ == '__main__': |
593 |
- main() |
|
516 |
+ setup() |
|
517 |
+ loop() |
|
594 | 518 |
|
597 | 521 |
old mode 100755 |
598 | 522 |
new mode 100644 |
... | ... |
@@ -176,11 +176,12 @@ function createChart($chartFile, $dataItem, $label, $title, $begin, |
176 | 176 |
if ($addTrend == 0) { |
177 | 177 |
$cmd .= "LINE1:dSeries#0400ff "; |
178 | 178 |
} elseif ($addTrend == 1) { |
179 |
- $cmdfmt = "CDEF:smoothed=dSeries,%s,TREND LINE2:smoothed#006600 "; |
|
179 |
+ $cmdfmt = "CDEF:smoothed=dSeries,%s,TREND LINE3:smoothed#ff0000 "; |
|
180 | 180 |
$cmd .= sprintf($cmdfmt, $trendWindow); |
181 | 181 |
} elseif ($addTrend == 2) { |
182 | 182 |
$cmd .= "LINE1:dSeries#0400ff "; |
183 |
- $cmdfmt = "CDEF:smoothed=dSeries,%s,TREND LINE2:smoothed#006600 "; |
|
183 |
+ $cmdfmt = "CDEF:smoothed=dSeries,%s,TREND LINE3:smoothed#ff0000 "; |
|
184 |
+ #$cmdfmt = "CDEF:smoothed=dSeries,%s,XYZZY LINE3:smoothed#ff0000 "; |
|
184 | 185 |
$cmd .= sprintf($cmdfmt, $trendWindow); |
185 | 186 |
} |
186 | 187 |
|