diff --git a/daemon/main/nzbget.h b/daemon/main/nzbget.h index e5247f8b..8dbc4d1d 100644 --- a/daemon/main/nzbget.h +++ b/daemon/main/nzbget.h @@ -15,7 +15,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program. If not, see . + * along with this program. If not, see . */ @@ -224,6 +224,7 @@ compiled */ #include #include #include +#include #include #include diff --git a/daemon/remote/XmlRpc.cpp b/daemon/remote/XmlRpc.cpp index 530dd8eb..9422f6e5 100644 --- a/daemon/remote/XmlRpc.cpp +++ b/daemon/remote/XmlRpc.cpp @@ -39,6 +39,8 @@ #include "UrlCoordinator.h" #include "ExtensionManager.h" #include "SystemInfo.h" +#include "Benchmark.h" +#include "Xml.h" extern void ExitProc(); extern void Reload(); @@ -361,6 +363,42 @@ class TestServerSpeedXmlCommand: public SafeXmlCommand virtual void Execute(); }; +class TestDiskSpeedXmlCommand final : public SafeXmlCommand +{ +public: + void Execute() override; + + std::string ToJsonStr(uint64_t size, double time) + { + Json::JsonObject json; + + json["SizeMB"] = size / 1024ull / 1024ull; + json["DurationMS"] = time; + + return Json::Serialize(json); + } + + std::string ToXmlStr(uint64_t size, double time) + { + xmlNodePtr rootNode = xmlNewNode(nullptr, BAD_CAST "value"); + xmlNodePtr structNode = xmlNewNode(nullptr, BAD_CAST "struct"); + + std::string sizeMB = std::to_string(size / 1024ull / 1024ull); + std::string durationMS = std::to_string(time); + + Xml::AddNewNode(structNode, "SizeMB", "i4", sizeMB.c_str()); + Xml::AddNewNode(structNode, "DurationMS", "double", durationMS.c_str()); + + xmlAddChild(rootNode, structNode); + + std::string result = Xml::Serialize(rootNode); + + xmlFreeNode(rootNode); + + return result; + } +}; + class StartScriptXmlCommand : public XmlCommand { public: @@ -817,6 +855,10 @@ std::unique_ptr XmlRpcProcessor::CreateCommand(const char* methodNam { command = std::make_unique(); } + else if (!strcasecmp(methodName, "testdiskspeed")) + { + command = std::make_unique(); + } else if (!strcasecmp(methodName, "startscript")) { command = std::make_unique(); @@ -3696,6 +3738,82 @@ void TestServerSpeedXmlCommand::Execute() BuildBoolResponse(true); } + + +void TestDiskSpeedXmlCommand::Execute() +{ + char* dirPath; + int writeBufferKiB; + int maxFileSizeGiB; + int timeoutSec; + + if (!NextParamAsStr(&dirPath)) + { + BuildErrorResponse(2, "Invalid argument (Path)"); + return; + } + + if (!NextParamAsInt(&writeBufferKiB)) + { + BuildErrorResponse(2, "Invalid argument (Write Buffer)"); + return; + } + + if (writeBufferKiB < 0) + { + BuildErrorResponse(2, "Write Buffer cannot be negative"); + return; + } + + if (!NextParamAsInt(&maxFileSizeGiB)) + { + BuildErrorResponse(2, "Invalid argument (Max file size)"); + return; + } + + if (maxFileSizeGiB < 1) + { + BuildErrorResponse(2, "Max file size must be positive"); + return; + } + + if (!NextParamAsInt(&timeoutSec)) + { + BuildErrorResponse(2, "Invalid argument (Timeout)"); + return; + } + + if (timeoutSec < 1) + { + BuildErrorResponse(2, "Timeout must be positive"); + return; + } + + try + { + size_t bufferSizeBytes = writeBufferKiB * 1024; + uint64_t maxFileSizeBytes = maxFileSizeGiB * 1024ull * 1024ull * 1024ull; + + Benchmark::DiskBenchmark db; + auto [size, time] = db.Run( + dirPath, + bufferSizeBytes, + maxFileSizeBytes, + std::chrono::seconds(timeoutSec) + ); + + std::string jsonStr = IsJson() ? + ToJsonStr(size, time) : + ToXmlStr(size, time); + + AppendResponse(jsonStr.c_str()); + } + catch (const std::exception& e) + { + BuildErrorResponse(2, e.what()); + } +} + // bool startscript(string script, string command, string context, struct[] options); void StartScriptXmlCommand::Execute() { diff --git a/daemon/sources.cmake b/daemon/sources.cmake index c5e15889..f799d86d 100644 --- a/daemon/sources.cmake +++ b/daemon/sources.cmake @@ -93,6 +93,7 @@ set(SRC ${CMAKE_SOURCE_DIR}/daemon/util/Util.cpp ${CMAKE_SOURCE_DIR}/daemon/util/Json.cpp ${CMAKE_SOURCE_DIR}/daemon/util/Xml.cpp + ${CMAKE_SOURCE_DIR}/daemon/util/Benchmark.cpp ${CMAKE_SOURCE_DIR}/daemon/system/SystemInfo.cpp ${CMAKE_SOURCE_DIR}/daemon/system/OS.cpp diff --git a/daemon/util/Benchmark.cpp b/daemon/util/Benchmark.cpp new file mode 100644 index 00000000..a89d2506 --- /dev/null +++ b/daemon/util/Benchmark.cpp @@ -0,0 +1,170 @@ +/* + * This file is part of nzbget. See . + * + * Copyright (C) 2024 Denis + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "nzbget.h" + +#include +#include +#include "FileSystem.h" +#include "Benchmark.h" + +namespace Benchmark +{ + using namespace std::chrono; + + std::pair DiskBenchmark::Run( + const std::string& dir, + size_t bufferSizeBytes, + uint64_t maxFileSizeBytes, + seconds timeout) const noexcept(false) + { + ValidateBufferSize(bufferSizeBytes); + + const std::string filename = dir + PATH_SEPARATOR + GetUniqueFilename(); + std::ofstream file = OpenFile(filename); + + std::vector buffer; + UseBuffer(file, buffer, bufferSizeBytes); + + size_t dataSize = bufferSizeBytes > 0 ? bufferSizeBytes : 1024; + std::vector data = GenerateRandomCharsVec(dataSize); + + nanoseconds timeoutNS = duration_cast(timeout); + + return RunBench(file, filename, data, maxFileSizeBytes, timeoutNS); + } + + std::pair DiskBenchmark::RunBench( + std::ofstream& file, + const std::string& filename, + const std::vector& data, + uint64_t maxFileSizeBytes, + std::chrono::nanoseconds timeoutNS) const noexcept(false) + { + uint64_t totalWritten = 0; + + auto start = steady_clock::now(); + try + { + while (totalWritten < maxFileSizeBytes && (steady_clock::now() - start) < timeoutNS) + { + file.write(data.data(), data.size()); + totalWritten += data.size(); + } + } + catch (const std::exception& e) + { + CleanUp(file, filename); + + std::string errMsg = "Failed to write data to file " + filename + ". " + e.what(); + throw std::runtime_error(errMsg); + } + auto finish = steady_clock::now(); + double elapsed = duration(finish - start).count(); + + CleanUp(file, filename); + + return { totalWritten, elapsed }; + } + + void DiskBenchmark::DeleteFile(const std::string& filename) const noexcept(false) + { + if (!FileSystem::DeleteFile(filename.c_str())) + { + std::string errMsg = "Failed to delete " + filename + " test file"; + throw std::runtime_error(errMsg); + } + } + + void DiskBenchmark::CleanUp( + std::ofstream& file, + const std::string& filename) const noexcept(false) + { + file.close(); + DeleteFile(filename); + } + + std::string DiskBenchmark::GetUniqueFilename() const noexcept(false) + { + return std::to_string( + high_resolution_clock::now().time_since_epoch().count() + ) + ".bin"; + } + + std::ofstream DiskBenchmark::OpenFile(const std::string& filename) const noexcept(false) + { + std::ofstream file(filename, std::ios::binary); + + if (!file.is_open()) + { + std::string errMsg = std::string("Failed to create test file: ") + strerror(errno); + throw std::runtime_error(errMsg); + } + + file.exceptions(std::ofstream::badbit | std::ofstream::failbit); + file.sync_with_stdio(false); + + return file; + } + + void DiskBenchmark::ValidateBufferSize(size_t bufferSz) const noexcept(false) + { + if (bufferSz > m_maxBufferSize) + { + throw std::invalid_argument("The buffer size is too big"); + } + } + + void DiskBenchmark::UseBuffer( + std::ofstream& file, + std::vector& buffer, + size_t buffSize + ) const noexcept(false) + { + if (buffSize > 0) + { + buffer.resize(buffSize); + file.rdbuf()->pubsetbuf(buffer.data(), buffSize); + } + else + { + file.rdbuf()->pubsetbuf(nullptr, 0); + } + } + + std::vector DiskBenchmark::GenerateRandomCharsVec(size_t size) const noexcept(false) + { + if (size == 0) + { + return {}; + } + + std::random_device rd; + std::mt19937 gen(rd()); + std::uniform_int_distribution<> distrib(0, 127); + + std::vector v(size); + std::generate(begin(v), end(v), [&distrib, &gen]() + { + return static_cast(distrib(gen)); + }); + + return v; + } +} diff --git a/daemon/util/Benchmark.h b/daemon/util/Benchmark.h new file mode 100644 index 00000000..5d112b24 --- /dev/null +++ b/daemon/util/Benchmark.h @@ -0,0 +1,70 @@ +/* + * This file is part of nzbget. See . + * + * Copyright (C) 2024 Denis + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef BENCHMARK_H +#define BENCHMARK_H + +#include +#include +#include + +namespace Benchmark +{ + class DiskBenchmark final + { + public: + std::pair Run( + const std::string& dir, + size_t bufferSizeBytes, + uint64_t maxFileSizeBytes, + std::chrono::seconds timeout) const noexcept(false); + + private: + std::pair RunBench( + std::ofstream& file, + const std::string& filename, + const std::vector& data, + uint64_t maxFileSizeBytes, + std::chrono::nanoseconds timeoutNS) const noexcept(false); + + void DeleteFile(const std::string& filename) const noexcept(false); + + std::string GetUniqueFilename() const noexcept(false); + + void CleanUp( + std::ofstream& file, + const std::string& filename) const noexcept(false); + + std::ofstream OpenFile(const std::string& filename) const noexcept(false); + + void ValidateBufferSize(size_t bufferSz) const noexcept(false); + + void UseBuffer( + std::ofstream& file, + std::vector& buffer, + size_t buffSize + ) const noexcept(false); + + std::vector GenerateRandomCharsVec(size_t size) const noexcept(false); + + const uint32_t m_maxBufferSize = 1024 * 1024 * 512; + }; +} + +#endif diff --git a/docs/api/API.md b/docs/api/API.md index eb619121..0131d5c0 100644 --- a/docs/api/API.md +++ b/docs/api/API.md @@ -96,3 +96,4 @@ If HTTP basic authentication is somewhat problematic the username/password can a - [testextension](TESTEXTENSION.md) - [testserver](TESTSERVER.md) - [testserverspeed](TESTSERVERSPEED.md) +- [testdiskspeed](TESTDISKSPEED.md) diff --git a/docs/api/TESTDISKSPEED.md b/docs/api/TESTDISKSPEED.md new file mode 100644 index 00000000..de894f1c --- /dev/null +++ b/docs/api/TESTDISKSPEED.md @@ -0,0 +1,27 @@ +## API-method `testdiskspeed` + +## Since +`v24.3` + +### Signature +``` c++ +bool testdiskspeed( + string dirPath, + int writeBufferSize, + int maxFileSize, + int timeout, +); +``` + +### Description +The function writes data to a file until either the maximum file size is reached or the timeout period expires. + +### Arguments +- **dirPath** `(string)` - The path to the directory where the test file will be created. +- **writeBuffer** `(int)` - The size of the buffer used for writing data to the file (in KiB). +- **maxFileSize** `(int)` - The maximum size of the file to be created (in GiB). +- **timeout** `(int)` - Test timeout (in seconds). + +### Return value +- **SizeMB** `(int)` - Written data size (in MiB). +- **DurationMS** `(int)` - Test duration (in milliseconds). diff --git a/tests/util/Benchmark.cpp b/tests/util/Benchmark.cpp new file mode 100644 index 00000000..4534e805 --- /dev/null +++ b/tests/util/Benchmark.cpp @@ -0,0 +1,48 @@ +/* + * This file is part of nzbget. See . + * + * Copyright (C) 2024 Denis + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "nzbget.h" + +#include + +#include +#include "Benchmark.h" + +BOOST_AUTO_TEST_CASE(BenchmarkTest) +{ + Benchmark::DiskBenchmark db; + { + size_t tooBigBuffer = 1024ul * 1024ul * 1024ul; + BOOST_CHECK_THROW( + db.Run("./", tooBigBuffer, 1024, std::chrono::seconds(1)), std::invalid_argument + ); + } + + { + BOOST_CHECK_THROW( + db.Run("InvalidPath", 1024, 1024, std::chrono::seconds(1)), std::runtime_error + ); + } + + { + uint64_t maxFileSize = 1024 * 1024; + auto [size, duration] = db.Run("./", 1024, maxFileSize, std::chrono::seconds(1)); + BOOST_CHECK(size >= maxFileSize || duration < 1.3); + } +} diff --git a/tests/util/CMakeLists.txt b/tests/util/CMakeLists.txt index b0648a2c..fc6a66c4 100644 --- a/tests/util/CMakeLists.txt +++ b/tests/util/CMakeLists.txt @@ -4,12 +4,14 @@ file(GLOB UtilTestSrc UtilTest.cpp NStringTest.cpp JsonTest.cpp + BenchmarkTest.cpp ${CMAKE_SOURCE_DIR}/daemon/util/FileSystem.cpp ${CMAKE_SOURCE_DIR}/daemon/util/NString.cpp ${CMAKE_SOURCE_DIR}/daemon/util/Util.cpp ${CMAKE_SOURCE_DIR}/daemon/util/Json.cpp ${CMAKE_SOURCE_DIR}/daemon/util/Xml.cpp ${CMAKE_SOURCE_DIR}/daemon/util/Log.cpp + ${CMAKE_SOURCE_DIR}/daemon/util/Benchmark.cpp ) add_executable(UtilTests ${UtilTestSrc}) diff --git a/webui/index.html b/webui/index.html index 2a61fa22..b6b82e03 100644 --- a/webui/index.html +++ b/webui/index.html @@ -661,13 +661,86 @@

+ +
progress_activity
@@ -731,11 +804,21 @@

System

Free / Total disk space (DestDir) - + + + + Free / Total disk space (InterDir) - + + + + Write buffer diff --git a/webui/index.js b/webui/index.js index 9527c0c5..ecba89b9 100644 --- a/webui/index.js +++ b/webui/index.js @@ -853,6 +853,7 @@ var Refresher = (new function($) 'readurl', 'servervolumes', 'testserverspeed', + 'testdiskspeed', ]; $('#RefreshMenu li a').click(refreshIntervalClick); diff --git a/webui/style.css b/webui/style.css index aa3c45a4..984da831 100644 --- a/webui/style.css +++ b/webui/style.css @@ -18,7 +18,7 @@ * along with this program. If not, see . */ -* { + * { scrollbar-width: thin; } @@ -51,6 +51,13 @@ body { padding-right: 0; } +.flex-center { + display: flex; + align-items: center; + width: inherit; + gap: 5px; +} + #AddDialog_URLProp { vertical-align: sub; color: inherit; @@ -87,6 +94,23 @@ body { font-size: 128px; } +.dist-speedtest__controls { + display: flex; + gap: 10px; +} + +#ConfigContent select.dist-speedtest__select--width { + width: 80px; +} + +.help-text-error { + color: #b94a48; +} + +#SysInfo_DiskSpeedTestBtn.btn--disabled { + opacity: 0.6 !important; +} + .spinner { color: #0088cc; -webkit-animation:spin 1s linear infinite; diff --git a/webui/system-info.js b/webui/system-info.js index 7a6d87d8..f5214950 100644 --- a/webui/system-info.js +++ b/webui/system-info.js @@ -18,6 +18,142 @@ */ +function DiskSpeedTestsForm() +{ + var _512MiB = 1024 * 1024 * 512; + var $timeout; + var $maxSize; + var $writeBufferInput; + var $diskSpeedTestInputLabel; + var $diskSpeedTestInput; + var $diskSpeedTestBtn; + var $diskSpeedTestErrorTxt + + var SPINNER = 'progress_activity'; + var TEST_BTN_DEFAULT_TEXT = 'Run test'; + + this.init = function(writeBuffer, dirPath, label, lsKey) + { + $timeout = $('#SysInfo_DiskSpeedTestTimeout'); + $maxSize = $('#SysInfo_DiskSpeedTestMaxSize'); + $writeBufferInput = $('#SysInfo_DiskSpeedTestWriteBufferInput'); + $diskSpeedTestInputLabel = $('#SysInfo_DiskSpeedTestInputLabel'); + $diskSpeedTestInput = $('#SysInfo_DiskSpeedTestInput'); + $diskSpeedTestBtn = $('#SysInfo_DiskSpeedTestBtn'); + $diskSpeedTestErrorTxt = $('#SysInfo_DiskSpeedTestErrorTxt'); + + disableBtnToggle(false); + + $diskSpeedTestBtn.text(getSpeedResFromLS(lsKey) || TEST_BTN_DEFAULT_TEXT); + + $writeBufferInput.val(writeBuffer); + $diskSpeedTestInputLabel.text(label); + $diskSpeedTestInput.val(dirPath); + + $diskSpeedTestBtn + .off('click') + .on('click', function() + { + var writeBufferVal = + $writeBufferInput.val(); + var path = $diskSpeedTestInput.val(); + var maxSize = + $maxSize.val(); + var timeout = + $timeout.val(); + runDiskSpeedTest(path, writeBufferVal, maxSize, timeout, lsKey); + }); + + $writeBufferInput + .off('change paste keyup') + .on('change paste keyup', function(event) + { + if (isValidWriteBuffer(event.target.value)) + disableBtnToggle(false); + else + disableBtnToggle(true); + }); + + $diskSpeedTestInput + .off('change paste keyup') + .on('change paste keyup', function(event) + { + if (isValidPath(event.target.value)) + disableBtnToggle(false); + else + disableBtnToggle(true); + }); + } + + function runDiskSpeedTest(path, writeBufferSize, maxFileSize, timeout, lsKey) + { + $diskSpeedTestErrorTxt.empty(); + $diskSpeedTestBtn.html(SPINNER); + disableBtnToggle(true); + + RPC.call('testdiskspeed', [path, writeBufferSize, maxFileSize, timeout], + function(rawRes) + { + var res = makeResults(rawRes); + saveSpeedResToLS(lsKey, res); + $diskSpeedTestBtn.html(makeResults(rawRes)); + disableBtnToggle(false); + }, + function(res) + { + $diskSpeedTestBtn.html(getSpeedResFromLS(lsKey) || TEST_BTN_DEFAULT_TEXT); + disableBtnToggle(false); + + var errTxt = res.split('
')[0]; + $diskSpeedTestErrorTxt.html(errTxt); + }, + ); + } + + function makeResults(res) + { + var r = res.SizeMB / (res.DurationMS / 1000); + return Util.formatSizeMB(r) + '/s'; + } + + function disableBtnToggle(disable) + { + if (disable) + { + $diskSpeedTestBtn.addClass('btn--disabled'); + } + else + { + $diskSpeedTestBtn.removeClass('btn--disabled'); + } + } + + function isValidWriteBuffer(input) + { + var val = input.trim(); + if (!val) + return false; + + var num = Number(val); + + return !isNaN(num) && num >= 0 && num < _512MiB; + } + + function isValidPath(input) + { + var path = input.trim(); + + return path !== ''; + } + + function saveSpeedResToLS(key, res) + { + localStorage.setItem(key, res); + } + + function getSpeedResFromLS(key) + { + return localStorage.getItem(key) || ''; + } +} + var SystemInfo = (new function($) { this.id = "Config-SystemInfo"; @@ -31,6 +167,8 @@ var SystemInfo = (new function($) var $SysInfo_IP; var $SysInfo_DestDiskSpace; var $SysInfo_InterDiskSpace; + var $SysInfo_DestDirDiskTestBtn; + var $SysInfo_InterDirDiskTestBtn; var $SysInfo_DestDiskSpaceContainer; var $SysInfo_InterDiskSpaceContainer; var $SysInfo_ArticleCache; @@ -43,6 +181,7 @@ var SystemInfo = (new function($) var $SpeedTest_Stats; var $SpeedTest_StatsHeader; var $SpeedTest_StatsTable; + var $DiskSpeedTest_Modal; var $SystemInfo_Spinner; var $SystemInfo_MainContent; @@ -58,6 +197,10 @@ var SystemInfo = (new function($) var testNZBListId = 'dropdown_test_nzb_list_'; var lastTestStatsId = 'last_test_stats_'; var serverTestSpinnerId = 'server_test_spinner_'; + + var DEST_DIR_LS_KEY = 'DestDirSpeedResults'; + var INTER_DIR_LS_KEY = 'InterDirSpeedResults'; + var lastTestStatsBtns = {}; var spinners = {}; var allStats = []; @@ -68,18 +211,12 @@ var SystemInfo = (new function($) { $SysInfo_Uptime.text(Util.formatTimeHMS(status['UpTimeSec'])); - var destDirOpt = Options.findOption(Options.options, 'DestDir'); - var interDirOpt = Options.findOption(Options.options, 'InterDir'); + var writeBufferKB = Options.option('WriteBuffer'); + var destDir = Options.option('DestDir'); + var interDir = Options.option('InterDir') || destDir; - - - if (destDirOpt && interDirOpt) - { - var destDirPath = destDirOpt.Value; - var interDistPath = interDirOpt.Value ? interDirOpt.Value : destDirPath; - renderDiskSpace(+status['FreeDiskSpaceMB'], +status['TotalDiskSpaceMB'], destDirPath); - renderInterDiskSpace(+status['FreeInterDiskSpaceMB'], +status['TotalInterDiskSpaceMB'], interDistPath); - } + renderDestDiskSpace(writeBufferKB, +status['FreeDiskSpaceMB'], +status['TotalDiskSpaceMB'], destDir); + renderInterDiskSpace(writeBufferKB, +status['FreeInterDiskSpaceMB'], +status['TotalInterDiskSpaceMB'], interDir); } } @@ -112,6 +249,8 @@ var SystemInfo = (new function($) $SysInfo_IP = $('#SysInfo_IP'); $SysInfo_DestDiskSpace = $('#SysInfo_DestDiskSpace'); $SysInfo_InterDiskSpace = $('#SysInfo_InterDiskSpace'); + $SysInfo_DestDirDiskTestBtn = $('#SysInfo_DestDirDiskTestBtn'); + $SysInfo_InterDirDiskTestBtn = $('#SysInfo_InterDirDiskTestBtn'); $SysInfo_InterDiskSpaceContainer = $('#SysInfo_InterDiskSpaceContainer'); $SysInfo_DestDiskSpaceContainer = $('#SysInfo_DestDiskSpaceContainer'); $SysInfo_ArticleCache = $('#SysInfo_ArticleCache'); @@ -120,10 +259,11 @@ var SystemInfo = (new function($) $SysInfo_LibrariesTable = $('#SysInfo_LibrariesTable'); $SysInfo_NewsServersTable = $('#SysInfo_NewsServersTable'); $SysInfo_ErrorAlert = $('#SystemInfoErrorAlert'); - $SysInfo_ErrorAlertText = $('#SystemInfoAlert-text'); + $SysInfo_ErrorAlertText = $('#SystemInfo_alertText'); $SpeedTest_Stats = $('#SpeedTest_Stats'); $SpeedTest_StatsHeader = $('#SpeedTest_StatsHeader'); $SpeedTest_StatsTable = $('#SpeedTest_StatsTable tbody'); + $DiskSpeedTest_Modal = $('#DiskSpeedTest_Modal'); $SystemInfo_Spinner = $('#SystemInfo_Spinner'); $SystemInfo_MainContent = $('#SystemInfo_MainContent'); @@ -152,17 +292,6 @@ var SystemInfo = (new function($) ); } - function pathsOnSameDisk(path1, path2) - { - path1 = path1.replace(/\\/g, '/'); - path2 = path2.replace(/\\/g, '/'); - - var drive1 = path1.match(/^[a-zA-Z]:\//i) ? path1.match(/^[a-zA-Z]:\//i)[0] : '/'; - var drive2 = path2.match(/^[a-zA-Z]:\//i) ? path2.match(/^[a-zA-Z]:\//i)[0] : '/'; - - return drive1 === drive2; - } - function hideSpinner() { $SystemInfo_Spinner.hide(); @@ -236,8 +365,8 @@ var SystemInfo = (new function($) $SysInfo_Arch.text(sysInfo['CPU'].Arch || 'Unknown'); $SysInfo_ConfPath.text(Options.option('ConfigFile')); $SysInfo_ArticleCache.text(Util.formatSizeMB(+Options.option('ArticleCache'))); - $SysInfo_WriteBuffer.text(Util.formatSizeMB(+Options.option('WriteBuffer'))); + renderWriteBuffer(+Options.option('WriteBuffer')); renderIP(sysInfo['Network']); renderAppVersion(Options.option('Version')); renderTools(sysInfo['Tools']); @@ -245,6 +374,12 @@ var SystemInfo = (new function($) renderNewsServers(Status.getStatus()['NewsServers']); } + function renderWriteBuffer(writeBufferKB) + { + var writeBufferMB = writeBufferKB / 1024; + $SysInfo_WriteBuffer.text(Util.formatSizeMB(writeBufferMB)); + } + function renderSpinners(downloads) { for (var key in spinners) { @@ -284,16 +419,36 @@ var SystemInfo = (new function($) }); } - function renderDiskSpace(free, total, path) + function renderDestDiskSpace(writeBufferKB, free, total, dirPath) { $SysInfo_DestDiskSpace.text(formatDiskInfo(free, total)); - $SysInfo_DestDiskSpaceContainer.attr('title', path); + $SysInfo_DestDiskSpaceContainer.attr('title', dirPath); + $SysInfo_DestDirDiskTestBtn.off('click').on('click', function() + { + showDiskSpeedModal(writeBufferKB, dirPath, 'DestDir', DEST_DIR_LS_KEY); + }); + + var savedResults = localStorage.getItem(DEST_DIR_LS_KEY); + if (savedResults) + { + $SysInfo_DestDirDiskTestBtn.text(savedResults); + } } - function renderInterDiskSpace(free, total, path) + function renderInterDiskSpace(writeBufferKB, free, total, dirPath) { $SysInfo_InterDiskSpace.text(formatDiskInfo(free, total)); - $SysInfo_InterDiskSpaceContainer.attr('title', path); + $SysInfo_InterDiskSpaceContainer.attr('title', dirPath); + $SysInfo_InterDirDiskTestBtn.off('click').on('click', function() + { + showDiskSpeedModal(writeBufferKB, dirPath, 'InterDir', INTER_DIR_LS_KEY); + }); + + var savedResults = localStorage.getItem(INTER_DIR_LS_KEY); + if (savedResults) + { + $SysInfo_InterDirDiskTestBtn.text(savedResults); + } } function formatDiskInfo(free, total) @@ -538,6 +693,12 @@ var SystemInfo = (new function($) ); } + function showDiskSpeedModal(writeBuffer, dirPath, label, lsKey) + { + $DiskSpeedTest_Modal.show(); + (new DiskSpeedTestsForm()).init(writeBuffer, dirPath, label, lsKey); + } + function showStatsTable(stats) { $($SpeedTest_Stats).show();