Chciałem popełnić ten post jako mały tutorial, ale nie ma chyba odpowiedniego działu.Możesz się podzielić know-how ? ja na ten moment mam tylko logowanie do pliku co godzine .. a wolałbym ładne wykresyRozwiązanie to pozwala na cykliczne odpalanie (CRON), logowanie wyników do np. pliku (znacznik czasu, ping, download, upload) oraz prezentację (np. w postaci wykresów).
#!/bin/sh
COMMAND=$(speedtest --simple)
DATA=$(echo $COMMAND | tr "[:alpha:]+[/:]" "\n")
DATA=$(echo $DATA | tr "[:blank:]" ,)
PING=$(printf "%s" $DATA | cut -d, -f 1)
DOWNLOAD=$(printf "%s" $DATA | cut -d, -f 2)
UPLOAD=$(printf "%s" $DATA | cut -d, -f 3)
[ -z $PING ] && PING=0
[ -z $DOWNLOAD ] && DOWNLOAD=0
[ -z $UPLOAD ] && UPLOAD=0
DATETIME=$(date +"%Y-%m-%d %H:%M:%S")
printf "$DATETIME,%.1f,%.2f,%.2f" $PING $DOWNLOAD $UPLOAD
echo
crontab -b
0,15,30,45 * * * * /usr/local/bin/speedtest.sh >> /var/log/speedtest.csv
2019-03-25 12:00:28,5.6,41.89,39.27
2019-03-25 12:15:29,9.8,41.90,39.27
2019-03-25 12:30:28,5.5,41.90,41.69
2019-03-25 12:45:28,5.6,41.90,41.31
2019-03-25 13:00:28,6.1,41.88,41.28
2019-03-25 13:15:28,6.0,41.90,41.41
2019-03-25 13:30:29,6.2,41.90,41.09
2019-03-25 13:45:29,9.6,41.79,40.67
2019-03-25 14:00:28,5.8,41.88,41.07
#!/bin/sh
LOG_FILE='/var/log/speedtest.csv'
DATETIME_FROM=$(date -d 'today' +'%Y-%m-%d 00:00:00')
DATETIME_TO=$(date -d 'today' +'%Y-%m-%d %H:%M:%S')
OUTPUT_PATH=$TMPDIR
OUTPUT_FILE='chart.png'
CHART_GENERATOR='/usr/local/bin/speedtest_generate_chart.py'
CHART_TYPE='summary'
while getopts 'f:l:m:o:p:t:' flag; do
case "${flag}" in
f) DATETIME_FROM=$OPTARG ;;
l) LOG_FILE=$OPTARG ;;
m) CHART_TYPE=$OPTARG ;;
o) OUTPUT_FILE=$OPTARG ;;
p) OUTPUT_PATH=$OPTARG ;;
t) DATETIME_TO=$OPTARG ;;
*) exit 1 ;;
esac
done
if [ ! -f $LOG_FILE ] ; then
echo "Log file $LOG_FILE does not exist, aborting."
exit
fi
if [ ! -s $LOG_FILE ] ; then
echo "Log file $LOG_FILE is empty, aborting."
exit
fi
[ -z $OUTPUT_PATH ] && OUTPUT_PATH='/tmp'
if [ ! -d $OUTPUT_PATH ] ; then
echo "Output path $OUTPUT_PATH does not exist, aborting."
exit
fi
if [ ! -w $OUTPUT_PATH ] ; then
echo "Output path $OUTPUT_PATH is not writable, aborting."
exit
fi
TMP_LOG_FILE=$(mktemp)
awk -F, -v datetime_from="$DATETIME_FROM" -v datetime_to="$DATETIME_TO" '$1>=datetime_from && $1<=datetime_to' $LOG_FILE > $TMP_LOG_FILE
python3 $CHART_GENERATOR $TMP_LOG_FILE $CHART_TYPE "$OUTPUT_PATH/$OUTPUT_FILE"
rm $TMP_LOG_FILE
#!/usr/bin/python
import sys
import os
import datetime
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
from matplotlib.dates import DateFormatter
from matplotlib.dates import HourLocator
logFile = sys.argv[1]
type = sys.argv[2]
outputFile = sys.argv[3]
if not os.path.exists(logFile):
sys.exit(1)
if not os.path.isfile(logFile):
sys.exit(1)
timestamps = []
pings = []
downloads = []
uploads = []
logFileDataSeparator = ','
logFileDateTimeFormat = '%Y-%m-%d %H:%M:%S'
darkBackground = None
formatStringPing = 'r-'
formatStringDownload = 'g-'
formatStringUpload = 'b-'
formatStringDateFormatter = '%H'
xLabel = 'Time [h]'
yLabelPing = 'Ping [ms]'
yLabelDownload = 'Download [Mbit/s]'
yLabelUpload = 'Upload [Mbit/s]'
yLabelSpeed = 'Speed [Mbit/s]'
grid = True
gridColor = 'lightgray'
gridLineStyle = ':'
gridLineWidth = 1
with open(logFile, "r") as file:
for line in file:
line = line.rstrip()
columns = line.split(logFileDataSeparator)
timestamps.append(datetime.datetime.strptime(columns[0], logFileDateTimeFormat))
pings.append(columns[1])
downloads.append(columns[2])
uploads.append(columns[3])
if darkBackground:
plt.style.use('dark_background')
if type == 'ping':
fig, ax = plt.subplots()
ax.set_xlabel(xLabel)
ax.set_ylabel(yLabelPing)
ax.plot_date(timestamps, pings, fmt=formatStringPing, xdate=True, ydate=False)
ax.set_ylim(bottom=0)
if grid:
ax.grid(None, color=gridColor, linestyle=gridLineStyle, linewidth=gridLineWidth)
ax.tick_params(labelsize='medium', width=1, labeltop=None, labelright=None)
ax.xaxis.set_major_locator(HourLocator())
ax.xaxis.set_major_formatter(DateFormatter(formatStringDateFormatter))
plt.savefig(outputFile)
elif type == 'download':
fig, ax = plt.subplots()
ax.set_xlabel(xLabel)
ax.set_ylabel(yLabelDownload)
ax.plot_date(timestamps, downloads, fmt=formatStringDownload, xdate=True, ydate=False)
ax.set_ylim(bottom=0)
if grid:
ax.grid(None, color=gridColor, linestyle=gridLineStyle, linewidth=gridLineWidth)
ax.tick_params(labelsize='medium', width=1, labeltop=None, labelright=None)
ax.xaxis.set_major_locator(HourLocator())
ax.xaxis.set_major_formatter(DateFormatter(formatStringDateFormatter))
plt.savefig(outputFile)
elif type == 'upload':
fig, ax = plt.subplots()
ax.set_xlabel(xLabel)
ax.set_ylabel(yLabelUpload)
ax.plot_date(timestamps, uploads, fmt=formatStringUpload, xdate=True, ydate=False)
ax.set_ylim(bottom=0)
if grid:
ax.grid(None, color=gridColor, linestyle=gridLineStyle, linewidth=gridLineWidth)
ax.tick_params(labelsize='medium', width=1, labeltop=None, labelright=None)
ax.xaxis.set_major_locator(HourLocator())
ax.xaxis.set_major_formatter(DateFormatter(formatStringDateFormatter))
plt.savefig(outputFile)
elif type == 'summary':
fig, ax1 = plt.subplots()
ax1.set_xlabel(xLabel)
ax1.set_ylabel(yLabelSpeed)
ax1.tick_params(axis='y', labelsize='medium', width=1)
ax1.plot_date(timestamps, downloads, fmt=formatStringDownload, xdate=True, ydate=False)
ax1.plot_date(timestamps, uploads, fmt=formatStringUpload, xdate=True, ydate=False)
ax1.set_ylim(bottom=0)
ax1.xaxis.set_major_locator(HourLocator())
ax1.xaxis.set_major_formatter(DateFormatter(formatStringDateFormatter))
if grid:
ax1.grid(None, color=gridColor, linestyle=gridLineStyle, linewidth=gridLineWidth)
ax2 = ax1.twinx()
ax2.set_ylabel(yLabelPing)
ax2.plot_date(timestamps, pings, fmt=formatStringPing, xdate=True, ydate=False)
fig.tight_layout()
plt.savefig(outputFile)
/usr/local/bin/speedtest_generate_chart.sh -l /var/log/speedtest.csv -o 20190316_download.png -f '2019-03-16 00:00:00' -t '2019-03-17 00:00:00' -m download
<?php
$datetimeFrom = isset($_GET['from']) ? strtotime($_GET['from']) : false;
$datetimeTo = isset($_GET['to']) ? strtotime($_GET['to']) : false;
$csvFile = '/var/log/speedtest.csv';
$data = [];
if (file_exists($csvFile)) {
$keys = ['datetime', 'ping', 'download', 'upload'];
foreach (file($csvFile, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES) as $line) {
$append = true;
$values = explode(',', $line);
$datetime = strtotime($values[0]);
if ($datetime) {
if ($datetimeFrom and $datetime < $datetimeFrom) {
$append = false;
}
if ($append and $datetimeTo and $datetime > $datetimeTo) {
$append = false;
}
}
if ($append) {
array_push($data, array_combine($keys, $values));
}
}
}
$json = json_encode($data);
print_r($json);
speedtest_csv2json.php?from=20190325120000&to=20190325140100
[{"datetime":"2019-03-25 12:00:28","ping":"5.6","download":"41.89","upload":"39.27"},{"datetime":"2019-03-25 12:15:29","ping":"9.8","download":"41.90","upload":"39.27"},{"datetime":"2019-03-25 12:30:28","ping":"5.5","download":"41.90","upload":"41.69"},{"datetime":"2019-03-25 12:45:28","ping":"5.6","download":"41.90","upload":"41.31"},{"datetime":"2019-03-25 13:00:28","ping":"6.1","download":"41.88","upload":"41.28"},{"datetime":"2019-03-25 13:15:28","ping":"6.0","download":"41.90","upload":"41.41"},{"datetime":"2019-03-25 13:30:29","ping":"6.2","download":"41.90","upload":"41.09"},{"datetime":"2019-03-25 13:45:29","ping":"9.6","download":"41.79","upload":"40.67"},{"datetime":"2019-03-25 14:00:28","ping":"5.8","download":"41.88","upload":"41.07"}]
<?php
$allowCustomization = true;
$config = array(
'selector' => 'speedtestChart',
'width' => 600,
'height' => 400,
'marginLeft' => 50,
'marginRight' => 50,
'marginTop' => 30,
'marginBottom' => 30,
'url' => 'speedtest_csv2json.php', # can be also an URL like 'http://SOMEWHERE/speedtest_csv2json.php'
'color' => 'black',
'backgroundColor' => 'white',
'datetimeFormat' => '%Y-%m-%d %H:%M:%S',
'ping' => true,
'pingStroke' => 'red',
'pingStrokeWidth' => 'red',
'download' => true,
'downloadStroke' => 'green',
'downloadStrokeWidth' => '1px',
'upload' => true,
'uploadStroke' => 'blue',
'uploadStrokeWidth' => '1px',
'dateLine' => true,
'dateLineStroke' => 'steelblue',
'dateLineStrokeWidth' => '1px',
'tooltip' => true,
'tooltipLeft' => 20,
'tooltipTop' => 20,
'xAxisOrient' => 'bottom',
'xAxisTicks' => 5,
'xAxisTickFormat' => '%Y-%m-%d',
'xAxisTickPadding' => 10,
'xAxisTickLineStroke' => 'black',
'xAxisTickLineStrokeWidth' => '1px',
'xAxisTickLineStrokeDasharray' => '2,2',
'xAxisTickLineOpacity' => '0.5',
'yAxisSpeedOrient' => 'left',
'yAxisSpeedTicks' => 5,
'yAxisSpeedTickFormat' => 'd',
'yAxisSpeedTickPadding' => 10,
'yAxisPingOrient' => 'right',
'yAxisPingTicks' => 5,
'yAxisPingTickFormat' => 'd',
'yAxisPingTickPadding' => 10,
'yAxisTickLineStroke' => 'black',
'yAxisTickLineStrokeWidth' => '1px',
'yAxisTickLineStrokeDasharray' => '2,2',
'yAxisTickLineOpacity' => '0.5',
);
if ($allowCustomization) {
foreach ($_GET as $key => $value) {
if (isset($config[$key])) {
switch ($key) {
case 'url':
case 'color':
case 'backgroundColor':
case 'selector':
case 'dateLineStroke':
case 'dateLineStrokeWidth':
case 'datetimeFormat':
case 'downloadStroke':
case 'downloadStrokeWidth':
case 'uploadStroke':
case 'uploadStrokeWidth':
case 'pingStroke':
case 'pingStrokeWidth':
case 'xAxisOrient':
case 'xAxisTickFormat':
case 'xAxisTickLineStroke':
case 'xAxisTickLineStrokeWidth':
case 'xAxisTickLineStrokeDasharray':
case 'xAxisTickLineOpacity':
case 'yAxisSpeedOrient':
case 'yAxisSpeedTickFormat':
case 'yAxisPingOrient':
case 'yAxisPingTickFormat':
case 'yAxisTickLineStroke':
case 'yAxisTickLineStrokeWidth':
case 'yAxisTickLineStrokeDasharray':
case 'yAxisTickLineOpacity':
$config[$key] = htmlentities($value, ENT_QUOTES);
break;
case 'height':
case 'width':
case 'marginLeft':
case 'marginRight':
case 'marginTop':
case 'marginBottom':
case 'tooltipTop':
case 'xAxisTicks':
case 'xAxisTickPadding':
case 'yAxisSpeedTicks':
case 'yAxisSpeedTickPadding':
case 'yAxisPingTicks':
case 'yAxisPingTickPadding':
$config[$key] = (int) $value;
break;
case 'download':
case 'upload':
case 'ping':
case 'dateLine':
case 'tooltip':
$config[$key] = (bool) $value;
break;
}
}
}
}
$data = array(
'width' => $config['width'],
'height' => $config['height'],
'margin' => array(
'left' => $config['marginLeft'],
'right' => $config['marginRight'],
'top' => $config['marginTop'],
'bottom' => $config['marginBottom'],
),
'url' => $config['url'],
'color' => $config['color'],
'backgroundColor' => $config['backgroundColor'],
'datetimeFormat' => $config['datetimeFormat'],
'selector' => '#' . $config['selector'],
'ping' => array(
'display' => $config['ping'] ? true : false,
'stroke' => $config['pingStroke'],
'strokeWidth' => $config['pingStrokeWidth'],
),
'download' => array(
'display' => $config['download'] ? true : false,
'stroke' => $config['downloadStroke'],
'strokeWidth' => $config['downloadStrokeWidth'],
),
'upload' => array(
'display' => $config['upload'] ? true : false,
'stroke' => $config['uploadStroke'],
'strokeWidth' => $config['uploadStrokeWidth'],
),
'dateLine' => array(
'display' => $config['dateLine'] ? true : false,
'stroke' => $config['dateLineStroke'],
'strokeWidth' => $config['dateLineStrokeWidth'],
),
'tooltip' => array(
'display' => $config['tooltip'] ? true : false,
'left' => $config['tooltipLeft'],
'top' => $config['tooltipTop'],
'rowSeparator' => '<br/>',
),
'xAxis' => array(
'orient' => $config['xAxisOrient'],
'ticks' => $config['xAxisTicks'],
'tick' => array(
'format' => $config['xAxisTickFormat'],
'padding' => $config['xAxisTickPadding'],
'line' => array(
'stroke' => $config['xAxisTickLineStroke'],
'strokeWidth' => $config['xAxisTickLineStrokeWidth'],
'strokeDasharray' => $config['xAxisTickLineStrokeDasharray'],
'opacity' => $config['xAxisTickLineOpacity'],
),
),
),
'yAxis' => array(
'speed' => array(
'orient' => $config['yAxisSpeedOrient'],
'ticks' => $config['yAxisSpeedTicks'],
'tick' => array(
'format' => $config['yAxisSpeedTickFormat'],
'padding' => $config['yAxisSpeedTickPadding'],
),
),
'ping' => array(
'orient' => $config['yAxisPingOrient'],
'ticks' => $config['yAxisPingTicks'],
'tick' => array(
'format' => $config['yAxisPingTickFormat'],
'padding' => $config['yAxisPingTickPadding'],
),
),
'tick' => array(
'line' => array(
'stroke' => $config['yAxisTickLineStroke'],
'strokeWidth' => $config['yAxisTickLineStrokeWidth'],
'strokeDasharray' => $config['yAxisTickLineStrokeDasharray'],
'opacity' => $config['yAxisTickLineOpacity'],
),
),
),
);
$json = json_encode($data);
print_r($json);
{
"width": 600,
"height": 400,
"margin": {
"left": 50,
"right": 50,
"top": 30,
"bottom": 30
},
"url": "speedtest_csv2json.php",
"color": "black",
"backgroundColor": "white",
"datetimeFormat": "%Y-%m-%d %H:%M:%S",
"selector": "#speedtestChart",
"ping": {
"display": true,
"stroke": "red",
"strokeWidth": "red"
},
"download": {
"display": true,
"stroke": "green",
"strokeWidth": "1px"
},
"upload": {
"display": true,
"stroke": "blue",
"strokeWidth": "1px"
},
"dateLine": {
"display": true,
"stroke": "steelblue",
"strokeWidth": "1px"
},
"tooltip": {
"display": true,
"left": 20,
"top": 20,
"rowSeparator": ""
},
"xAxis": {
"orient": "bottom",
"ticks": 5,
"tick": {
"format": "%Y-%m-%d",
"padding": 10,
"line": {
"stroke": "black",
"strokeWidth": "1px",
"strokeDasharray": "2,2",
"opacity": "0.5"
}
}
},
"yAxis": {
"speed": {
"orient": "left",
"ticks": 5,
"tick": {
"format": "d",
"padding": 10
}
},
"ping": {
"orient": "right",
"ticks": 5,
"tick": {
"format": "d",
"padding": 10
}
},
"tick": {
"line": {
"stroke": "black",
"strokeWidth": "1px",
"strokeDasharray": "2,2",
"opacity": "0.5"
}
}
}
}
d3.speedtestChart = function(config) {
var chartWidth = config.width - config.margin.left - config.margin.right,
chartHeight = config.height - config.margin.top - config.margin.bottom,
parseDate = d3.time.format(config.datetimeFormat).parse,
x = d3.time.scale().range([0, chartWidth]),
xAxis = d3.svg.axis().scale(x)
.orient(config.xAxis.orient)
.ticks(config.xAxis.ticks)
.tickFormat(d3.time.format(config.xAxis.tick.format))
.innerTickSize(-chartHeight)
.outerTickSize(0)
.tickPadding(config.xAxis.tick.padding),
ySpeed = d3.scale.linear().range([chartHeight, 0]),
yAxisSpeed = d3.svg.axis().scale(ySpeed)
.orient(config.yAxis.speed.orient)
.ticks(config.yAxis.speed.ticks)
.tickFormat(d3.format(config.yAxis.speed.tick.format))
.innerTickSize(-chartWidth)
.outerTickSize(0)
.tickPadding(config.yAxis.speed.tick.padding),
downloadLine = d3.svg.line()
.x(function(d) { return x(d.datetime); })
.y(function(d) { return ySpeed(d.download); }),
uploadLine = d3.svg.line()
.x(function(d) { return x(d.datetime); })
.y(function(d) { return ySpeed(d.upload); }),
yPing = d3.scale.linear().range([chartHeight, 0]),
yAxisPing = d3.svg.axis().scale(yPing)
.orient(config.yAxis.ping.orient)
.ticks(config.yAxis.ping.ticks)
.tickFormat(d3.format(config.yAxis.ping.tick.format))
.innerTickSize((config.download.display || config.upload.display) ? 0 : -chartWidth)
.outerTickSize(0)
.tickPadding(config.yAxis.ping.tick.padding),
pingLine = d3.svg.line()
.x(function(d) { return x(d.datetime); })
.y(function(d) { return yPing(d.ping); }),
draw = function(config) {
var svgWrapper = d3.select(config.selector)
.append('div')
.attr('id', config.selector.replace('#', '') + '-chart-wrapper')
.style({
position: 'relative',
}),
svg = svgWrapper
.append('svg')
.attr('width', chartWidth + config.margin.left + config.margin.right)
.attr('height', chartHeight + config.margin.top + config.margin.bottom)
.style('background-color', config.backgroundColor)
.style({
'background-color': config.backgroundColor,
color: config.color,
})
.append('g')
.attr('transform', 'translate(' + config.margin.left + ',' + config.margin.top + ')'),
dateLine = svg.append('path')
.attr('class', 'date-line')
.style({
stroke: config.dateLine.stroke,
'stroke-width': config.dateLine.strokeWidth,
}),
tooltip = svgWrapper
.append('div')
.attr('id', config.selector.replace('#', '') + '-chart-tooltip')
.style({
display: 'none',
position: 'absolute',
'text-align': 'left',
padding: '8px',
'background-color': config.backgroundColor,
color: config.color,
border: '1px solid ' + config.color,
'border-radius': '4px',
'box-shadow': '0px 0px 5px 0px rgba(0,0,0,0.3)',
'z-index': 1,
}),
showTooltip = function() {
d3.select(config.selector + '-chart-tooltip')
.style('display', 'inline');
},
hideTooltip = function() {
d3.select(config.selector + '-chart-tooltip')
.style('display', 'none');
},
tooltipDate = null,
tooltipText = [],
getTooltipDate = function() {
return tooltipDate;
},
setTooltipDate = function(str) {
tooltipDate = str;
},
setTooltipText = function(texts) {
if (!(texts instanceof Array)) {
texts = [];
}
if (texts.length) {
texts = texts.filter(function(text) {
return text;
});
}
tooltipText = texts;
},
refreshTooltipText = function() {
if (config.tooltip.display) {
let content = tooltipText.slice();
content.unshift(getTooltipDate());
content = content.join(config.tooltip.rowSeparator);
d3.select(config.selector + '-chart-tooltip')
.html(content);
}
};
svg
.append('rect')
.attr('y', 0)
.attr('width', chartWidth)
.attr('height', chartHeight)
.style('opacity', 0);
d3.json(config.url, function(data) {
data.forEach(function(d) {
d.datetime = parseDate(d.datetime);
d.ping = +d.ping;
d.download = +d.download;
d.upload = +d.upload;
});
x.domain(d3.extent(data, function(d) { return d.datetime; }));
ySpeed.domain([0, d3.max(data, function(d) { return Math.max(d.download, d.upload); })]);
yPing.domain([0, d3.max(data, function(d) { return d.ping; })]);
svg
.append('g')
.attr('class', 'x axis')
.attr('transform', 'translate(0,' + chartHeight + ')')
.style({
stroke: config.color,
'stroke-width': '1px',
'shape-rendering': 'crispEdges',
})
.call(xAxis);
if (config.download.display || config.upload.display) {
svg.append('g')
.attr('class', 'y axis')
.style({
stroke: config.color,
'stroke-width': '1px',
'shape-rendering': 'crispEdges',
})
.call(yAxisSpeed);
}
if (config.download.display) {
svg.append('path')
.attr('class', 'line')
.attr('d', downloadLine(data))
.style({
stroke: config.download.stroke,
'stroke-width': config.download.strokeWidth,
fill: 'none',
});
}
if (config.upload.display) {
svg.append('path')
.attr('class', 'line')
.attr('d', uploadLine(data))
.style({
stroke: config.upload.stroke,
'stroke-width': config.upload.strokeWidth,
fill: 'none',
});
}
if (config.ping.display) {
svg.append('g')
.attr('class', 'y axis')
.attr('transform', 'translate(' + ((config.download.display || config.upload.display) ? chartWidth : 0) + ', 0)')
.style({
stroke: config.color,
'stroke-width': '1px',
'shape-rendering': 'crispEdges',
})
.call(yAxisPing);
svg.append('path')
.attr('class', 'line')
.attr('d', pingLine(data))
.style({
stroke: config.ping.stroke,
'stroke-width': config.ping.strokeWidth,
fill: 'none',
});
}
svg
.selectAll('.x.axis > .tick > line')
.style({
stroke: config.xAxis.tick.line.stroke,
'stroke-width': config.xAxis.tick.line.strokeWidth,
'stroke-dasharray': config.xAxis.tick.line.strokeDasharray,
opacity: config.xAxis.tick.line.opacity,
});
svg
.selectAll('.x.axis > .tick > text')
.style({
stroke: 'none',
fill: config.color,
});
svg
.selectAll('.y.axis > .tick > line')
.style({
stroke: config.yAxis.tick.line.stroke,
'stroke-width': config.yAxis.tick.line.strokeWidth,
'stroke-dasharray': config.yAxis.tick.line.strokeDasharray,
opacity: config.yAxis.tick.line.opacity,
});
svg
.selectAll('.y.axis > .tick > text')
.style({
stroke: 'none',
fill: config.color,
});
let bisectDate = d3.bisector(function(d) {
return d.datetime;
}).left,
onMouseOver = function() {
if (config.tooltip.display) {
let coordinates = d3.mouse(this);
if (coordinates[0] >= 0 && coordinates[0] <= chartWidth && coordinates[1] <= chartHeight) {
showTooltip();
}
}
},
onMouseMove = function() {
let coordinates = d3.mouse(this);
if (coordinates[0] >= 0 && coordinates[0] <= chartWidth && coordinates[1] <= chartHeight) {
if (config.dateLine.display) {
dateLine.style('display', null)
.attr('d', function () {
return 'M' + coordinates[0] + ',' + (chartHeight) + ' ' + coordinates[0] + ', 0';
});
}
if (config.tooltip.display) {
let date = moment(x.invert(coordinates[0])).format('YYYY-MM-DD HH:mm:ss'),
index = bisectDate(data, parseDate(date)),
dataLeft = data[index - 1],
dataRight = data[index],
dataNearest = null;
if (typeof dataLeft !== 'undefined') {
if (typeof dataRight !== 'undefined') {
dataNearest = parseDate(date) - dataLeft.datetime > dataRight.datetime - parseDate(date) ? dataRight : dataLeft;
} else {
dataNearest = dataLeft;
}
} else {
if (typeof dataRight !== 'undefined') {
dataNearest = dataRight;
}
}
if (typeof dataNearest !== 'undefined') {
setTooltipDate(moment(dataNearest.datetime).format('YYYY-MM-DD HH:mm'));
let tooltipText = [];
if (config.ping) {
tooltipText.push('Ping: ' + dataNearest.ping);
}
if (config.download) {
tooltipText.push('Download: ' + dataNearest.download);
}
if (config.upload) {
tooltipText.push('Upload: ' + dataNearest.upload);
}
setTooltipText(tooltipText);
d3.select(config.selector + '-chart-tooltip')
.style({
left: (coordinates[0] + config.margin.left + config.tooltip.left) + 'px',
top: (coordinates[1] + config.margin.top + config.tooltip.top) + 'px',
});
refreshTooltipText();
}
}
}
},
onMouseOut = function() {
if (config.dateLine.display) {
dateLine.style('display', 'none');
}
if (config.tooltip.display) {
hideTooltip();
setTooltipText([]);
}
};
svg
.on('mouseover', onMouseOver)
.on('mousemove', onMouseMove)
.on('mouseout', onMouseOut);
});
};
return draw(config);
};
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.24.0/moment.min.js"></script>
<script src="speedtest_chart.js"></script>
<script>
$(document).ready(function() {
$.get('speedtest_chart_config.php?selector=summaryChart', function(configJSON) {
var summaryChartConfig = JSON.parse(configJSON);
d3.speedtestChart(summaryChartConfig);
});
$.get('speedtest_chart_config.php?selector=downloadChart&download=1&upload=0&ping=0&backgroundColor=black&color=white&xAxisTickLineStroke=white&yAxisTickLineStroke=white', function(configJSON) {
var downloadChartConfig = JSON.parse(configJSON);
d3.speedtestChart(downloadChartConfig);
});
$.get('speedtest_chart_config.php?selector=uploadChart&download=0&upload=1&ping=0', function(configJSON) {
var uploadChartConfig = JSON.parse(configJSON);
d3.speedtestChart(uploadChartConfig);
});
$.get('speedtest_chart_config.php?selector=pingChart&download=0&upload=0&ping=1&yAxisPingOrient=left&backgroundColor=black&color=white&xAxisTickLineStroke=white&yAxisTickLineStroke=white', function(configJSON) {
var pingChartConfig = JSON.parse(configJSON);
d3.speedtestChart(pingChartConfig);
});
});
</script>
<style>
body {
font-family: Arial;
font-size: 11px;
}
</style>
</head>
<body>
<div id="summaryChart"></div>
<div id="downloadChart"></div>
<div id="uploadChart"></div>
<div id="pingChart"></div>
</body>
</html>
Oh'Linux? Software hackingChciałem popełnić ten post jako mały tutorial, ale nie ma chyba odpowiedniego działu.
PHP Parse error: syntax error, unexpected '$datetimeFrom' (T_VARIABLE) in /var/www/html/speedtest_csv2json.php on line 3