Commit aefe1e9f authored by Reto Da Forno's avatar Reto Da Forno
Browse files

visualization tool integrated

parent 396e3570
......@@ -92,8 +92,8 @@ lockfile = /tmp/flocklab/linktest_schedule.lock
; vizualisation of test results
[viz]
enablepreview = 1 ;set to 1 to enable generation of preview data
imgdir = /home/flocklab/viz ;path to preview directory
generate_plots = 1 ;set to 1 to enable generation of plotting data
dir = /home/flocklab/viz ;directory where plots are stored
; Cleaner which deletes test results (after a per-user retention time has expired)
[retentioncleaner]
......
......@@ -112,17 +112,15 @@ def main(argv):
# Delete cached test results ---
archive_path = "%s/%s%s" % (flocklab.config.get('archiver','archive_dir'), testid, flocklab.config.get('archiver','archive_ext'))
viz_pathes = glob.glob("%s/%s_*" % (flocklab.config.get('viz','imgdir'), testid))
pathes = [archive_path]
pathes.extend(viz_pathes)
for path in pathes:
if os.path.exists(path):
if os.path.isfile(path):
os.remove(path)
else:
shutil.rmtree(path)
logger.debug("Removed path %s for test %s."%(path, testid))
logger.debug("Removed path %s for test %s." % (path, testid))
# Delete test itself ---
if delete_all:
# Delete test itself:
......@@ -130,27 +128,27 @@ def main(argv):
WHERE (`serv_tests_key` = %s)
"""
starttime = time.time()
num_deleted_rows = cur.execute(sql%(testid))
num_deleted_rows = cur.execute(sql % (testid))
cn.commit()
logger.debug("Deleted %i rows of data in table tbl_serv_tests for test ID %s in %f seconds" %(num_deleted_rows, testid, (time.time()-starttime)))
logger.debug("Deleted %i rows of data in table tbl_serv_tests for test ID %s in %f seconds" % (num_deleted_rows, testid, (time.time()-starttime)))
else:
# Set test status to deleted but keep metadata ---
flocklab.set_test_status(cur, cn, int(testid), "deleted")
logger.debug("Set status for test ID %s to 'deleted'" %(testid))
# Delete old entries in viz cache ---
keeptime = flocklab.config.getint('cleaner', 'keeptime_viz')
earliest_keeptime = time.time() - (keeptime*86400)
imgdir_path = flocklab.config.get('viz','imgdir')
if os.path.isdir(imgdir_path):
for f in os.listdir(imgdir_path):
path = os.path.join(imgdir_path, f)
earliest_keeptime = time.time() - (keeptime * 86400)
vizdir = flocklab.config.get('viz','dir')
if os.path.isdir(vizdir):
for f in os.listdir(vizdir):
path = os.path.join(vizdir, f)
if os.stat(path).st_mtime < earliest_keeptime:
logger.debug("Removing viz cache %s..."%path)
logger.debug("Removing plots %s..." % path)
shutil.rmtree(path)
else:
logger.debug("Directory '%s' does not exist." % imgdir_path)
logger.debug("Directory '%s' does not exist." % vizdir)
# Get parameters ---
now = time.strftime(flocklab.config.get("database", "timeformat"), time.gmtime())
maxtestcleanuptime = flocklab.config.getint('cleaner', 'max_test_cleanuptime')
......
......@@ -2,6 +2,7 @@
import sys, os, getopt, errno, threading, shutil, time, datetime, subprocess, tempfile, queue, re, logging, traceback, __main__, types, hashlib, lxml.etree, MySQLdb, signal
import lib.flocklab as flocklab
import flocklab as fltools
logger = None
......@@ -911,6 +912,19 @@ def prepare_testresults(testid, cur):
else:
logger.warn("Could not copy XML config to test results directory.")
# Generate plot ---
if flocklab.config.getint('viz', 'generate_plots'):
if not os.path.isdir(flocklab.config.get('viz', 'dir')):
os.mkdir(flocklab.config.get('viz', 'dir'))
logger.debug("Generating plots...")
try:
fltools.visualizeFlocklabTrace(testresultsdir, outputDir=flocklab.config.get('viz', 'dir'), interactive=False)
logger.debug("Plots generated.")
except Exception:
logger.error("Failed to generate results plot for test %d. %s: %s" % (testid, str(sys.exc_info()[0]), str(sys.exc_info()[1])))
except SystemExit:
pass
# Archive test results ---
cmd = [flocklab.config.get('dispatcher', 'archiverscript'),"--testid=%d" % testid]
if emailResults:
......
......@@ -162,18 +162,16 @@ def read_from_db_file(dbfile):
# and aggregates them into single test result files.
#
##############################################################################
def worker_convert_and_aggregate(queueitem=None, nodeid=None, resultfile_path=None, resultfile_lock=None, commitsize=1, vizimgdir=None, parse_f=None, convert_f=None, viz_f=None, logqueue=None):
def worker_convert_and_aggregate(queueitem=None, nodeid=None, resultfile_path=None, resultfile_lock=None, commitsize=1, parse_f=None, convert_f=None, logqueue=None):
try:
_errors = []
cur_p = multiprocessing.current_process()
(itemtype, obsid, fdir, f, workerstate) = queueitem
obsdbfile_path = "%s/%s" % (fdir,f)
loggername = "(%s.%d) " % (cur_p.name, obsid)
#logqueue.put_nowait((loggername, logging.DEBUG, "Import file %s"%obsdbfile_path))
# Open file:
dbfile = open(obsdbfile_path, 'rb')
rows = 0
viz_values = []
conv_values = []
while not dbfile.closed:
# Process DB file line by line:
......@@ -181,7 +179,6 @@ def worker_convert_and_aggregate(queueitem=None, nodeid=None, resultfile_path=No
# Parse one line:
buf = read_from_db_file(dbfile)
obsdata = parse_f(buf)
viz_values.append(obsdata)
# Convert data if needed:
if convert_f != None:
conv_data = convert_f(obsdata, obsid, nodeid)
......@@ -189,10 +186,6 @@ def worker_convert_and_aggregate(queueitem=None, nodeid=None, resultfile_path=No
rows += 1
# Visualize data:
if (commitsize > 0) & (rows >= commitsize):
if viz_f != None:
#logqueue.put_nowait((loggername, logging.DEBUG, "Viz started..."))
viz_f(testid, owner_fk, viz_values, obsid, vizimgdir, logger)
#logqueue.put_nowait((loggername, logging.DEBUG, "Viz done."))
# Write data to file:
#logqueue.put_nowait((loggername, logging.DEBUG, "Opening file %s for writing..." % (resultfile_path)))
resultfile_lock.acquire()
......@@ -203,7 +196,6 @@ def worker_convert_and_aggregate(queueitem=None, nodeid=None, resultfile_path=No
logqueue.put_nowait((loggername, logging.DEBUG, "Committed results to %s after %d rows" % (resultfile_path, rows)))
rows = 0
conv_values = []
viz_values = []
except DbFileEof:
# logqueue.put_nowait((loggername, logging.DEBUG, "DbFileEof has occurred."))
break # dbfile has been closed in parser (most likely because EOF was reached)
......@@ -212,11 +204,7 @@ def worker_convert_and_aggregate(queueitem=None, nodeid=None, resultfile_path=No
_errors.append((msg, errno.EIO, obsid))
logqueue.put_nowait((loggername, logging.ERROR, msg))
if (len(conv_values) > 0):
# There is still data left. Do a last commit:
if (viz_f != None) and (len(viz_values) > 0):
#logqueue.put_nowait((loggername, logging.DEBUG, "Viz started..."))
viz_f(testid, owner_fk, viz_values, obsid, vizimgdir, logger)
#logqueue.put_nowait((loggername, logging.DEBUG, "Viz done."))
# There is still data left. Do a last commit
# Write data to file:
#logqueue.put_nowait((loggername, logging.DEBUG, "Opening file %s for final writing..." % (resultfile_path)))
resultfile_lock.acquire()
......@@ -1013,9 +1001,7 @@ def main(argv):
signal.signal(signal.SIGTERM, sigterm_handler)
signal.signal(signal.SIGINT, sigterm_handler)
# Loop through the folders and assign work to the worker processes:
vizimgdir = flocklab.config.get('viz','imgdir')
commitsize = flocklab.config.getint('fetcher', 'commitsize')
enableviz = flocklab.config.getint('viz','enablepreview')
loggerprefix = "(Mainloop) "
workmanager = WorkManager()
......@@ -1054,17 +1040,13 @@ def main(argv):
worker_args = [nextitem, nodeid, testresultsfile_dict['gpiotracing'][0], logqueue, None]
worker_f = worker_gpiotracing
logger.debug(loggerprefix + "resultfile_path: %s" % str(testresultsfile_dict['gpiotracing'][0]))
#if (enableviz == 1):
# worker_args[6] = flocklab.viz_gpio_monitor
elif (re.search("^powerprofiling_[0-9]{14}\.rld$", f) != None):
pool = service_pools_dict['powerprofiling']
worker_args = [nextitem, nodeid, testresultsfile_dict['powerprofiling'][0], logqueue, ppFileFormat]
worker_f = worker_powerprof
#if (enableviz == 1):
# worker_args[6] = flocklab.viz_powerprofiling
elif (re.search("^serial_[0-9]{14}\.db$", f) != None):
pool = service_pools_dict['serial']
worker_args = [nextitem, nodeid, testresultsfile_dict['serial'][0], testresultsfile_dict['serial'][1], commitsize, vizimgdir, parse_serial, convert_serial, None, logqueue]
worker_args = [nextitem, nodeid, testresultsfile_dict['serial'][0], testresultsfile_dict['serial'][1], commitsize, parse_serial, convert_serial, logqueue]
elif (re.search("^error_[0-9]{14}\.log$", f) != None):
pool = service_pools_dict['logs']
worker_args = [nextitem, nodeid, testresultsfile_dict['errorlog'][0], logqueue, None]
......
......@@ -603,9 +603,9 @@ def get_obsids(cursor=None, platform=None, status=None):
LEFT JOIN flocklab.tbl_serv_tg_adapt_types AS slot3 ON c.tg_adapt_types_fk = slot3.serv_tg_adapt_types_key
LEFT JOIN flocklab.tbl_serv_tg_adapt_list AS d ON obs.slot_4_tg_adapt_list_fk = d.serv_tg_adapt_list_key
LEFT JOIN flocklab.tbl_serv_tg_adapt_types AS slot4 ON d.tg_adapt_types_fk = slot4.serv_tg_adapt_types_key
WHERE obs.status IN (%s) AND '%s' IN (slot1.name, slot2.name, slot3.name, slot4.name)
WHERE obs.status IN (%s) AND '%s' IN (LOWER(slot1.name), LOWER(slot2.name), LOWER(slot3.name), LOWER(slot4.name))
ORDER BY obs.observer_id;
""" % (status, platform))
""" % (status, platform.lower()))
obslist = []
for rs in cursor.fetchall():
obslist.append(rs[0])
......@@ -963,94 +963,6 @@ def is_test_running(cursor=None):
### is_test_running())
##############################################################################
#
# VIZ stuff
#
##############################################################################
def viz_plot(t, d, testdir, obsid, imgdir):
fig = matplotlib.figure.Figure(figsize=(2*(t[len(t)-1] - t[0]), 1))
ax = fig.add_axes([0., 0., 1., 1.])
ax.patch.set_facecolor(None)
fig.patch.set_alpha(0.)
ax.set_frame_on(False)
ax.axes.get_yaxis().set_visible(False)
ax.axes.get_xaxis().set_visible(False)
canvas = matplotlib.backends.backend_agg.FigureCanvasAgg(fig)
ax.plot(t, d, '-', color = '#001050', linewidth=2)
ax.axis((t[0], t[len(t)-1], -1, 40))
canvas.get_renderer().clear()
canvas.draw()
try:
os.makedirs('%s/%s' % (imgdir, testdir))
except OSError as exception:
if exception.errno != errno.EEXIST:
raise
canvas.print_figure('%s/%s/power_%d_%d.png' % (imgdir, testdir, obsid, t[0]*1e3), pad_inches=0, dpi=50, transparent=True)
### END viz_plot()
def viz_powerprofiling(testid, owner_fk, values, obsid, imgdir, logger):
#logger.debug("Viz %i values" % len(values[0]))
# samples, count, start, end
t=[]
d=[]
try:
if len(values[0]) != len(values[1]):
raise Exception("Could not process data, timestamp count and value count must be equal.")
for i in range(len(values[0])): # packets
start = time.time()
t.extend(values[0][i])
d.extend(values[1][i])
if t[0] is None:
logger.warn("first timestamp in list is none.")
if t[-1] is None:
logger.warn("last timestamp in list is none.")
if t[-1] < t[0]:
logger.warn("timestamps are not propperly ordered. t[0]: %f, t[-1]: %f." % (t[0], t[-1]))
if (t[-1]-t[0] >= 2) | (i==len(values[0])-1):
try:
viz_plot(t, d, "%d_%d"%(testid, owner_fk), obsid, imgdir)
except:
msg = "Viz error: %s: %s, data t: %f .. %f size(t)=%d" %(str(sys.exc_info()[0]), str(sys.exc_info()[1]),t[0],t[-1],len(t))
msg = msg.join(traceback.format_list(traceback.extract_tb(sys.exc_info()[2])))
logger.error(msg)
t=[]
d=[]
#logger.debug("Viz time spent %f" % (time.time() - start))
except:
logger.error("Error in viz_powerprofiling: %s: %s" %(str(sys.exc_info()[0]), str(sys.exc_info()[1])))
### END viz_powerprofiling()
def viz_gpio_monitor(testid, owner_fk, values, obsid, imgdir, logger):
# gpio; edge; timestamp;
# print max time int values per file to gpiom_<obsid>_<starttime>.json
try:
os.makedirs('%s/%d_%d' % (imgdir, testid, owner_fk))
except OSError as exception:
if exception.errno != errno.EEXIST:
raise
starttime = 0
try:
for i in range(len(values)):
e = values[i]
if starttime == 0:
starttime = float(e[2])
f = open('%s/%d_%d/gpiom_%d_%d.json' % (imgdir, testid, owner_fk, obsid, 1e3 * starttime), 'w')
f.write('{"e":[')
if (float(e[2]) - starttime > 5) or (i==len(values)-1):
f.write('{"t":%d,"p":%s,"l":%s}\n' % (int(round((float(e[2]) - starttime) * 1e3)), e[0], e[1]))
f.write(']}\n')
f.close()
starttime = 0
else:
f.write('{"t":%d,"p":%s,"l":%s},\n' % (int(round((float(e[2]) - starttime) * 1e3)), e[0], e[1]))
except:
logger.error("Error in viz_gpio_monitor: %s: %s" %(str(sys.exc_info()[0]), str(sys.exc_info()[1])))
### END viz_gpio_monitor()
##############################################################################
#
# scheduleLinkTest - try to schedule a link test for every platform, according to config file
......
......@@ -416,6 +416,7 @@ def main(argv):
if obsid == "ALL":
# expand
rs = flocklab.get_obsids(cur, platform, stati)
print("Found '%s' for platform '%s' and stati '%s'." % (str(rs), platform, stati))
if rs:
obsidlist.extend(rs)
else:
......
......@@ -169,13 +169,13 @@ echo '<h1>Manage Tests for '.$_SESSION['firstname'] . ' ' . $_SESSION['lastname'
<table id="test_overview" class="tablesorter" style="display:none">
<thead>
<tr>
<th width="50px">ID</th>
<th width="35px">ID</th>
<th width="113px">Title</th>
<th width="135px">Description</th>
<th width="20px" class='qtip_show' title='State'>State</th>
<th width="190px">Start</th>
<th width="190px">End</th>
<th width="70px" class='qtip_show' title='Actions'>Actions</th>
<th width="35px" class='qtip_show' title='State'>State</th>
<th>Start</th>
<th>End</th>
<th width="80px" class='qtip_show' title='Actions'>Actions</th>
</tr>
</thead>
<tbody>
......@@ -251,16 +251,7 @@ echo '<h1>Manage Tests for '.$_SESSION['firstname'] . ' ' . $_SESSION['lastname'
echo "<td>" . htmlentities($row['description']) . "</td>";
else
echo "<td class='qtip_show' title='" . htmlentities($row['description']) . "'>" . htmlentities(substr($row['description'],0,$max_len)) . "...</td>";
// Status: dependent of state of test
// check viz status
$viz = "";
/*if (!is_null($row['time_start_act'])) {
$d = new DateTime($row['time_start_act']);
if ($now - $d->format('U') < 21 * 24 * 3600) {
if (file_exists($CONFIG['viz']['dir'].'/'.$row['serv_tests_key'].'_'. $_SESSION['serv_users_key']))
$viz="<img src='pics/icons/chart.png' style='margin-left:5px' height='16px' alt='Results' title='Preview results' class='qtip_show' onClick='document.resprev.testid.value = " . $row['serv_tests_key'] . ";document.resprev.submit();'>";
}
}*/
// Status
echo "<td>";
echo "<span style='display:none'>".$row['test_status']."</span>"; // needed to make cell sortable by JQuery
echo "<img src='".state_icon($row['test_status'])."' height='16px' alt='".state_short_description($row['test_status'])."' title='".state_long_description($row['test_status'])."' class='qtip_show' >";
......@@ -268,21 +259,21 @@ echo '<h1>Manage Tests for '.$_SESSION['firstname'] . ' ' . $_SESSION['lastname'
// Start time: dependent of state of test
if ( $running || $cleaningup || $finished || $failed || $aborting || $syncing || $synced || $retentionexp) {
echo "<td title='Actual start time' class='qtip_show'>" . date_to_tzdate($row['time_start_act']). "</td>";
}
}
elseif ($preparing || $planned) {
echo "<td title='Planned start time' class='qtip_show'><i class='starttime'>" . date_to_tzdate($row['time_start_wish']). "</i></td>";
}
}
else
echo "<td title='Test is not schedulable' class='qtip_show'>n/a</td>";
// End time: dependent of state of test
if ($planned || $preparing || $running) {
echo "<td title='Planned end time' class='qtip_show'><i class='endtime'>" . date_to_tzdate($row['time_end_wish']). "</i></td>";
}
}
elseif ($cleaningup || $finished || $failed || $syncing || $synced || $retentionexp) {
echo "<td title='Actual end time' class='qtip_show'>" . date_to_tzdate($row['time_end_act']). "</td>";
}
elseif ($aborting)
echo "<td title='Test is currently aborting' class='qtip_show'>n/a</td>";
}
elseif ($aborting)
echo "<td title='Test is currently aborting' class='qtip_show'>n/a</td>";
elseif (!$schedulable)
echo "<td title='Test is not schedulable' class='qtip_show'>n/a</td>";
else
......@@ -296,21 +287,22 @@ echo '<h1>Manage Tests for '.$_SESSION['firstname'] . ' ' . $_SESSION['lastname'
} elseif ($running) {
echo "<span style='display:none'>running</span>"; // needed to make cell sortable by JQuery
echo "<img src='pics/icons/cancel.png' height='16px' alt='Abort' title='Abort test' class='qtip_show link' onClick='document.tstabrt.testid.value = " . $row['serv_tests_key'] . ";document.tstabrt.submit();'>";
/*echo "<img src='pics/icons/chart.png' style='margin-left:5px' height='16px' alt='Results' title='Preview results' class='qtip_show' onClick='document.resprev.testid.value = " . $row['serv_tests_key'] . ";document.resprev.submit();'>";*/
} elseif ($preparing || $cleaningup || $syncing || $synced) {
echo "<span style='display:none'>preparing</span>"; // needed to make cell sortable by JQuery
//echo "<img src='pics/icons/cancel.png' height='16px' alt='Abort' title='Abort test' class='qtip_show' onClick='document.tstabrt.testid.value = " . $row['serv_tests_key'] . ";document.tstabrt.submit();'>";
echo $viz;
} elseif ($finished) {
echo "<span style='display:none'>finished</span>"; // needed to make cell sortable by JQuery
echo "<img src='pics/icons/trash.png' height='16px' alt='Delete' title='Delete test' class='qtip_show link' onClick='document.tstdel.testid.value = " . $row['serv_tests_key'] . ";document.tstdel.submit();'>";
echo "<img src='pics/icons/download.png' style='margin-left:5px' height='16px' alt='Download' title='Download results' class='qtip_show link' onClick='getResult(".$row['serv_tests_key'].", 100);'>";
echo $viz;
if ($CONFIG['viz']['generate_plots']) {
$plot = $CONFIG['viz']['dir'].'/flocklab_plot_'.$row['serv_tests_key'].'.html';
if (file_exists($plot)) {
echo "<a href='show_results_plot.php?t=".$row['serv_tests_key']."' target='_blank'><img src='pics/icons/chart.png' style='margin-left:5px' height='16px' alt='Results' title='Plot results' class='qtip_show' ></a>";
}
}
} elseif ($retentionexp) {
echo "<span style='display:none'>retentionexpiring</span>"; // needed to make cell sortable by JQuery
echo "<img src='pics/icons/trash.png' height='16px' alt='Delete' title='Delete test' class='qtip_show link' onClick='document.tstdel.testid.value = " . $row['serv_tests_key'] . ";document.tstdel.submit();'>";
echo "<img src='pics/icons/download.png' style='margin-left:5px' height='16px' alt='Download' title='Download results' class='qtip_show link' onClick='getResult(".$row['serv_tests_key'].", 100);'>";
echo $viz;
} elseif ($failed) {
echo "<span style='display:none'>notschedulable</span>"; // needed to make cell sortable by JQuery
echo "<img src='pics/icons/trash.png' height='16px' alt='Delete' title='Delete test.' class='qtip_show link' onClick='document.tstdel.testid.value = " . $row['serv_tests_key'] . ";document.tstdel.submit();'>";
......@@ -321,7 +313,7 @@ echo '<h1>Manage Tests for '.$_SESSION['firstname'] . ' ' . $_SESSION['lastname'
} else {
echo "<span style='display:none'>unknown</span>"; // needed to make cell sortable by JQuery
}
echo "<img src='pics/icons/preview.png' style='margin-left:5px' height='16px' alt='Download config' title='Download test configuration.' class='qtip_show link' onClick='document.tstcdnl.testid.value = " . $row['serv_tests_key'] . ";document.tstcdnl.submit();'>";
echo "<img src='pics/icons/preview.png' style='margin-left:5px' height='16px' alt='Download config' title='Download test configuration.' class='qtip_show link' onClick='document.tstcdnl.testid.value = " . $row['serv_tests_key'] . ";document.tstcdnl.submit();'>";
echo "</td>";
echo "</tr>";
}
......@@ -349,7 +341,6 @@ echo '<h1>Manage Tests for '.$_SESSION['firstname'] . ' ' . $_SESSION['lastname'
<form name="tstdel" method="post" action="test_delete.php"><input type="hidden" name="testid" value=""></form>
<form name="tstedt" method="post" action="test_edit.php"><input type="hidden" name="testid" value=""></form>
<form name="tstabrt" method="post" action="test_abort.php"><input type="hidden" name="testid" value=""></form>
<form name="resprev" method="post" action="result_preview_viz.php"><input type="hidden" name="testid" value=""></form>
<form name="resdwn" method="post" id="downloadform" action="result_download_archive.php"><input type="hidden" name="testid" value=""><input type="hidden" name="query" value=""></form>
<form name="tstcdnl" method="post" action="testconfig_download.php"><input type="hidden" name="testid" value=""></form>
<p><a style="color:#666666;text-decoration:none;" href="newtest.php"><span class="texticon">+</span> add new test</a></p>
......
<?php
/*
* __author__ = "Roman Lim <lim@tik.ee.ethz.ch>"
* __copyright__ = "Copyright 2012, ETH Zurich, Switzerland"
* __license__ = "GPL"
* __version__ = "$Revision$"
* __date__ = "$Date$"
* __id__ = "$Id$"
* __source__ = "$URL$"
*/
?>
<?php
require_once('include/layout.php');require_once('include/presets.php');
$errors = array();
$style='';
if (isset($_GET['testid']))
$testid = $_GET['testid'];
else if (isset($_POST['testid']))
$testid = $_POST['testid'];
if (isset($testid)) {
// check test_owner = user
if (check_testid($testid, $_SESSION['serv_users_key'])) {
$status = get_teststatus($testid);
// if ($status!='running' && $status!='cleaning up') {
// array_push($errors, "Only running tests have a test preview.");
// }
}
else
array_push($errors, "Test does not belong to you.");
// Show validation errors:
if (isset($errors)) {
if (!empty($errors)) {
echo "<div class=\"warning\"><div style=\"float:left;\"><img alt=\"\" src=\"pics/icons/att.png\"></div>";
echo "<p>Error occured:</p><ul>";
foreach ($errors as $error)
echo "<li>" . $error . "</li>";
echo "</div><p></p>";
} else {
$style='
<style type="text/css">
.gpio0 {background-color: red }
.gpio1 {background-color: blue }
.gpio2 {background-color: #0ff }
.gpio3 {background-color: purple }
.gpio4 {background-color: yellow }
</style>
';
?>
<script type="text/javascript" src="scripts/jquery-ui-1.8.21.custom.min.js"></script>
<script type="text/javascript">
function log(msg) {
$('#log').append(msg+'<br>');
}
function slide_update(progress) {
if (progress===undefined)
var progress = 3;
var r = $('#slide').data('round');
$('#slide').data('round', r+1);
var p = parseInt($('#slide').css('left')) - progress;
$('#slide').css('left', (p)+'px');
view_start = slide_start - p / pps * 1e3;
view_end = view_start + parseInt($('#view').css('width')) / pps * 1e3;
// update timeline
if (r % 3 == 0)
$('.timeline').each(function(index) {
var div = $(this);
var vizmeta = div.data('vizmeta');
if (!vizmeta.loading && (vizmeta.end < view_end + vizmeta.preloadtime)) {
vizmeta.loading = true;
vizmeta.updater(div);
}
});
// update power
if (r % 3 == 1)
$('.obs > .power').each(function(index) {
var obsdiv = $(this);
var vizmeta = obsdiv.data('vizmeta');
if (!vizmeta.loading && (vizmeta.end < view_end + vizmeta.preloadtime)) {
vizmeta.loading = true;
vizmeta.updater(obsdiv);
}
});
// update gpio
if (r % 3 == 2)
$('.obs > .gpio').each(function(index) {
var obsdiv = $(this);
var vizmeta = obsdiv.data('vizmeta');
if (!vizmeta.loading && (vizmeta.end < view_end + vizmeta.preloadtime)) {
vizmeta.loading = true;
vizmeta.updater(obsdiv);
}
});
// update player
if (r % 10 == 0) {
// test.start, test.end
// view_end
var player = $('#player_slider');
var maxlen = player.width();
var newpos = Math.round((view_end - test.start) / (test.end - test.start+1e3) * (player.width()-$('#player_slider').find('img').first().width()));
// prog.width(prog.width()+1);
if (progress > 0)
$('#player_slider').find('img').first().css('left',(newpos)+'px');
$('#player_slider').find('div').first().width((newpos+9)+'px');
}
if (reachedEnd())
clearInterval(updateInterval);
};
function updateTimeline(div) {
var vizmeta = div.data('vizmeta');
div.append('<div style="position:absolute;left:'+Math.round(((vizmeta.end - slide_start) / 1e3 * pps))+'px;top:14px;margin:0;padding:0;height:6px;border-left:1px solid black;"><div style="text-align:center;width:60px;position:absolute;top:-15px;left:-30px">'+((vizmeta.end - test.start) / 1e3)+'<\/div><\/div>');
vizmeta.end = vizmeta.end + 1e3;
$(div).children().each(function() {
if (parseInt($(this).css('left')) + 30 < -1 * parseInt($('#slide').css('left'))) {
// remove this tick
$(this).remove();
}
});
if (vizmeta.end < test.end)
vizmeta.loading = false;
}
function removeOld(obsdiv) {
var oldestdiv = $('img',obsdiv).first();
if (parseInt($(oldestdiv).css('left')) + parseInt($(oldestdiv).css('width')) < -1 * parseInt($('#slide').css('left'))) {
// remove this image
$(oldestdiv).remove();
return 1;
}
return 0;
}
function powerLoadNext(obsdiv) {
var vizmeta = obsdiv.data('vizmeta');
var starttime = vizmeta.end - 10;
var obsid = vizmeta.obsid;
$.ajax({
url: 'viz_feed.php?t='+test.id+'&o='+obsid+'&s='+starttime+'&m=0',
type: 'GET',
// data: { t:""+test.id, o:""+obsid, s:""+starttime, m:"0", q:"1"},
success: function(data, textStatus, jqXHR) {
var imgstarttime = parseInt(jqXHR.getResponseHeader('Start-Time'));
// add image to slide
var img = $('<img src="" alt="" style="position:absolute;left:'+Math.round(((imgstarttime- slide_start) / 1e3 * pps))+'px">');
$(img).load(function(evtObj) {
$(obsdiv).append($(this));
var w = $(this).width();
vizmeta.end = imgstarttime + w / pps * 1e3;
if (vizmeta.end < view_end + vizmeta.preloadtime) {
powerLoadNext(obsdiv);
}
else {
if (vizmeta.end < test.end)
vizmeta.loading = false;
removeOld(obsdiv);
}
});
$(img).attr('src', 'viz_feed.php?t='+test.id+'&o='+obsid+'&s='+starttime+'&m=0');
},
error: function(data, textStatus, jqXHR) {
// start timer to retry