FileOperationProgress.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495
  1. //---------------------------------------------------------------------------
  2. #include <vcl.h>
  3. #pragma hdrstop
  4. #include "Common.h"
  5. #include "FileOperationProgress.h"
  6. //---------------------------------------------------------------------------
  7. #define TRANSFER_BUF_SIZE 4096
  8. //---------------------------------------------------------------------------
  9. __fastcall TFileOperationProgressType::TFileOperationProgressType()
  10. {
  11. FOnProgress = NULL;
  12. FOnFinished = NULL;
  13. Clear();
  14. }
  15. //---------------------------------------------------------------------------
  16. __fastcall TFileOperationProgressType::TFileOperationProgressType(
  17. TFileOperationProgressEvent AOnProgress, TFileOperationFinished AOnFinished)
  18. {
  19. FOnProgress = AOnProgress;
  20. FOnFinished = AOnFinished;
  21. FReset = false;
  22. Clear();
  23. }
  24. //---------------------------------------------------------------------------
  25. __fastcall TFileOperationProgressType::~TFileOperationProgressType()
  26. {
  27. assert(!InProgress || FReset);
  28. assert(!Suspended || FReset);
  29. }
  30. //---------------------------------------------------------------------------
  31. void __fastcall TFileOperationProgressType::Clear()
  32. {
  33. FileName = L"";
  34. AsciiTransfer = false;
  35. ResumeStatus = rsNotAvailable;
  36. Count = 0;
  37. FFilesFinished = 0;
  38. StartTime = Now();
  39. Suspended = false;
  40. FSuspendTime = 0;
  41. InProgress = false;
  42. FileInProgress = false;
  43. TotalTransfered = 0;
  44. TotalSkipped = 0;
  45. TotalSize = 0;
  46. SkippedSize = 0;
  47. TotalSizeSet = false;
  48. Operation = foNone;
  49. Temp = false;
  50. SkipToAll = false;
  51. BatchOverwrite = boNo;
  52. // to bypass check in ClearTransfer()
  53. TransferSize = 0;
  54. CPSLimit = 0;
  55. FTicks.clear();
  56. FTotalTransferredThen.clear();
  57. ClearTransfer();
  58. }
  59. //---------------------------------------------------------------------------
  60. void __fastcall TFileOperationProgressType::ClearTransfer()
  61. {
  62. if ((TransferSize > 0) && (TransferedSize < TransferSize))
  63. {
  64. __int64 RemainingSize = (TransferSize - TransferedSize);
  65. TotalSkipped += RemainingSize;
  66. }
  67. LocalSize = 0;
  68. TransferSize = 0;
  69. LocallyUsed = 0;
  70. SkippedSize = 0;
  71. TransferedSize = 0;
  72. TransferingFile = false;
  73. FLastSecond = 0;
  74. }
  75. //---------------------------------------------------------------------------
  76. void __fastcall TFileOperationProgressType::Start(TFileOperation AOperation,
  77. TOperationSide ASide, int ACount)
  78. {
  79. Start(AOperation, ASide, ACount, false, L"", 0);
  80. }
  81. //---------------------------------------------------------------------------
  82. void __fastcall TFileOperationProgressType::Start(TFileOperation AOperation,
  83. TOperationSide ASide, int ACount, bool ATemp,
  84. const UnicodeString ADirectory, unsigned long ACPSLimit)
  85. {
  86. Clear();
  87. Operation = AOperation;
  88. Side = ASide;
  89. Count = ACount;
  90. InProgress = true;
  91. Cancel = csContinue;
  92. Directory = ADirectory;
  93. Temp = ATemp;
  94. CPSLimit = ACPSLimit;
  95. try
  96. {
  97. DoProgress();
  98. }
  99. catch (...)
  100. {
  101. // connection can be lost during progress callbacks
  102. ClearTransfer();
  103. InProgress = false;
  104. throw;
  105. }
  106. }
  107. //---------------------------------------------------------------------------
  108. void __fastcall TFileOperationProgressType::Reset()
  109. {
  110. FReset = true;
  111. }
  112. //---------------------------------------------------------------------------
  113. void __fastcall TFileOperationProgressType::Stop()
  114. {
  115. // added to include remaining bytes to TotalSkipped, in case
  116. // the progress happens to update before closing
  117. ClearTransfer();
  118. InProgress = false;
  119. DoProgress();
  120. }
  121. //---------------------------------------------------------------------------
  122. void __fastcall TFileOperationProgressType::Suspend()
  123. {
  124. assert(!Suspended);
  125. Suspended = true;
  126. FSuspendTime = GetTickCount();
  127. DoProgress();
  128. }
  129. //---------------------------------------------------------------------------
  130. void __fastcall TFileOperationProgressType::Resume()
  131. {
  132. assert(Suspended);
  133. Suspended = false;
  134. // shift timestamps for CPS calculation in advance
  135. // by the time the progress was suspended
  136. unsigned long Stopped = (GetTickCount() - FSuspendTime);
  137. size_t i = 0;
  138. while (i < FTicks.size())
  139. {
  140. FTicks[i] += Stopped;
  141. ++i;
  142. }
  143. DoProgress();
  144. }
  145. //---------------------------------------------------------------------------
  146. int __fastcall TFileOperationProgressType::OperationProgress()
  147. {
  148. assert(Count);
  149. int Result = (FFilesFinished * 100)/Count;
  150. return Result;
  151. }
  152. //---------------------------------------------------------------------------
  153. int __fastcall TFileOperationProgressType::TransferProgress()
  154. {
  155. int Result;
  156. if (TransferSize)
  157. {
  158. Result = (int)((TransferedSize * 100)/TransferSize);
  159. }
  160. else
  161. {
  162. Result = 0;
  163. }
  164. return Result;
  165. }
  166. //---------------------------------------------------------------------------
  167. int __fastcall TFileOperationProgressType::TotalTransferProgress()
  168. {
  169. assert(TotalSizeSet);
  170. int Result = TotalSize > 0 ? (int)(((TotalTransfered + TotalSkipped) * 100)/TotalSize) : 0;
  171. return Result < 100 ? Result : 100;
  172. }
  173. //---------------------------------------------------------------------------
  174. int __fastcall TFileOperationProgressType::OverallProgress()
  175. {
  176. if (TotalSizeSet)
  177. {
  178. assert((Operation == foCopy) || (Operation == foMove));
  179. return TotalTransferProgress();
  180. }
  181. else
  182. {
  183. return OperationProgress();
  184. }
  185. }
  186. //---------------------------------------------------------------------------
  187. void __fastcall TFileOperationProgressType::DoProgress()
  188. {
  189. SetThreadExecutionState(ES_SYSTEM_REQUIRED);
  190. FOnProgress(*this, Cancel);
  191. }
  192. //---------------------------------------------------------------------------
  193. void __fastcall TFileOperationProgressType::Finish(UnicodeString FileName,
  194. bool Success, TOnceDoneOperation & OnceDoneOperation)
  195. {
  196. assert(InProgress);
  197. FOnFinished(Operation, Side, Temp, FileName,
  198. /* TODO : There wasn't 'Success' condition, was it by mistake or by purpose? */
  199. Success && (Cancel == csContinue), OnceDoneOperation);
  200. FFilesFinished++;
  201. DoProgress();
  202. }
  203. //---------------------------------------------------------------------------
  204. void __fastcall TFileOperationProgressType::SetFile(UnicodeString AFileName, bool AFileInProgress)
  205. {
  206. FileName = AFileName;
  207. FileInProgress = AFileInProgress;
  208. ClearTransfer();
  209. FFileStartTime = Now();
  210. DoProgress();
  211. }
  212. //---------------------------------------------------------------------------
  213. void __fastcall TFileOperationProgressType::SetFileInProgress()
  214. {
  215. assert(!FileInProgress);
  216. FileInProgress = true;
  217. DoProgress();
  218. }
  219. //---------------------------------------------------------------------------
  220. void __fastcall TFileOperationProgressType::SetLocalSize(__int64 ASize)
  221. {
  222. LocalSize = ASize;
  223. DoProgress();
  224. }
  225. //---------------------------------------------------------------------------
  226. void __fastcall TFileOperationProgressType::AddLocallyUsed(__int64 ASize)
  227. {
  228. LocallyUsed += ASize;
  229. if (LocallyUsed > LocalSize)
  230. {
  231. LocalSize = LocallyUsed;
  232. }
  233. DoProgress();
  234. }
  235. //---------------------------------------------------------------------------
  236. bool __fastcall TFileOperationProgressType::IsLocallyDone()
  237. {
  238. assert(LocallyUsed <= LocalSize);
  239. return (LocallyUsed == LocalSize);
  240. }
  241. //---------------------------------------------------------------------------
  242. unsigned long __fastcall TFileOperationProgressType::AdjustToCPSLimit(
  243. unsigned long Size)
  244. {
  245. if (CPSLimit > 0)
  246. {
  247. // we must not return 0, hence, if we reach zero,
  248. // we wait until the next second
  249. do
  250. {
  251. unsigned int Second = (GetTickCount() / MSecsPerSec);
  252. if (Second != FLastSecond)
  253. {
  254. FRemainingCPS = CPSLimit;
  255. FLastSecond = Second;
  256. }
  257. if (FRemainingCPS == 0)
  258. {
  259. SleepEx(100, true);
  260. DoProgress();
  261. }
  262. }
  263. while ((CPSLimit > 0) && (FRemainingCPS == 0));
  264. // CPSLimit may have been dropped in DoProgress
  265. if (CPSLimit > 0)
  266. {
  267. if (FRemainingCPS < Size)
  268. {
  269. Size = FRemainingCPS;
  270. }
  271. FRemainingCPS -= Size;
  272. }
  273. }
  274. return Size;
  275. }
  276. //---------------------------------------------------------------------------
  277. unsigned long __fastcall TFileOperationProgressType::LocalBlockSize()
  278. {
  279. unsigned long Result = TRANSFER_BUF_SIZE;
  280. if (LocallyUsed + Result > LocalSize)
  281. {
  282. Result = (unsigned long)(LocalSize - LocallyUsed);
  283. }
  284. Result = AdjustToCPSLimit(Result);
  285. return Result;
  286. }
  287. //---------------------------------------------------------------------------
  288. void __fastcall TFileOperationProgressType::SetTotalSize(__int64 ASize)
  289. {
  290. TotalSize = ASize;
  291. TotalSizeSet = true;
  292. DoProgress();
  293. }
  294. //---------------------------------------------------------------------------
  295. void __fastcall TFileOperationProgressType::SetTransferSize(__int64 ASize)
  296. {
  297. TransferSize = ASize;
  298. DoProgress();
  299. }
  300. //---------------------------------------------------------------------------
  301. void __fastcall TFileOperationProgressType::ChangeTransferSize(__int64 ASize)
  302. {
  303. // reflect change on file size (due to text transfer mode conversion particulary)
  304. // on total transfer size
  305. if (TotalSizeSet)
  306. {
  307. TotalSize += (ASize - TransferSize);
  308. }
  309. TransferSize = ASize;
  310. DoProgress();
  311. }
  312. //---------------------------------------------------------------------------
  313. void __fastcall TFileOperationProgressType::RollbackTransfer()
  314. {
  315. TransferedSize -= SkippedSize;
  316. assert(TransferedSize <= TotalTransfered);
  317. TotalTransfered -= TransferedSize;
  318. assert(SkippedSize <= TotalSkipped);
  319. FTicks.clear();
  320. FTotalTransferredThen.clear();
  321. TotalSkipped -= SkippedSize;
  322. SkippedSize = 0;
  323. TransferedSize = 0;
  324. TransferSize = 0;
  325. LocallyUsed = 0;
  326. }
  327. //---------------------------------------------------------------------------
  328. void __fastcall TFileOperationProgressType::AddTransfered(__int64 ASize,
  329. bool AddToTotals)
  330. {
  331. TransferedSize += ASize;
  332. if (TransferedSize > TransferSize)
  333. {
  334. // this can happen with SFTP when downloading file that
  335. // grows while being downloaded
  336. if (TotalSizeSet)
  337. {
  338. TotalSize += (TransferedSize - TransferSize);
  339. }
  340. TransferSize = TransferedSize;
  341. }
  342. if (AddToTotals)
  343. {
  344. TotalTransfered += ASize;
  345. unsigned long Ticks = GetTickCount();
  346. if (FTicks.empty() ||
  347. (FTicks.back() > Ticks) || // ticks wrap after 49.7 days
  348. ((Ticks - FTicks.back()) >= MSecsPerSec))
  349. {
  350. FTicks.push_back(Ticks);
  351. FTotalTransferredThen.push_back(TotalTransfered);
  352. }
  353. if (FTicks.size() > 10)
  354. {
  355. FTicks.erase(FTicks.begin());
  356. FTotalTransferredThen.erase(FTotalTransferredThen.begin());
  357. }
  358. }
  359. DoProgress();
  360. }
  361. //---------------------------------------------------------------------------
  362. void __fastcall TFileOperationProgressType::AddResumed(__int64 ASize)
  363. {
  364. TotalSkipped += ASize;
  365. SkippedSize += ASize;
  366. AddTransfered(ASize, false);
  367. AddLocallyUsed(ASize);
  368. }
  369. //---------------------------------------------------------------------------
  370. unsigned long __fastcall TFileOperationProgressType::TransferBlockSize()
  371. {
  372. unsigned long Result = TRANSFER_BUF_SIZE;
  373. if (TransferedSize + Result > TransferSize)
  374. {
  375. Result = (unsigned long)(TransferSize - TransferedSize);
  376. }
  377. Result = AdjustToCPSLimit(Result);
  378. return Result;
  379. }
  380. //---------------------------------------------------------------------------
  381. unsigned long __fastcall TFileOperationProgressType::StaticBlockSize()
  382. {
  383. return TRANSFER_BUF_SIZE;
  384. }
  385. //---------------------------------------------------------------------------
  386. bool __fastcall TFileOperationProgressType::IsTransferDone()
  387. {
  388. assert(TransferedSize <= TransferSize);
  389. return (TransferedSize == TransferSize);
  390. }
  391. //---------------------------------------------------------------------------
  392. void __fastcall TFileOperationProgressType::SetAsciiTransfer(bool AAsciiTransfer)
  393. {
  394. AsciiTransfer = AAsciiTransfer;
  395. DoProgress();
  396. }
  397. //---------------------------------------------------------------------------
  398. void __fastcall TFileOperationProgressType::SetResumeStatus(TResumeStatus AResumeStatus)
  399. {
  400. ResumeStatus = AResumeStatus;
  401. DoProgress();
  402. }
  403. //---------------------------------------------------------------------------
  404. TDateTime __fastcall TFileOperationProgressType::TimeElapsed()
  405. {
  406. return Now() - StartTime;
  407. }
  408. //---------------------------------------------------------------------------
  409. unsigned int __fastcall TFileOperationProgressType::CPS()
  410. {
  411. unsigned int Result;
  412. if (FTicks.empty())
  413. {
  414. Result = 0;
  415. }
  416. else
  417. {
  418. unsigned long Ticks = (Suspended ? FSuspendTime : GetTickCount());
  419. unsigned long TimeSpan;
  420. if (Ticks < FTicks.front())
  421. {
  422. // clocks has wrapped, guess 10 seconds difference
  423. TimeSpan = 10000;
  424. }
  425. else
  426. {
  427. TimeSpan = (Ticks - FTicks.front());
  428. }
  429. if (TimeSpan == 0)
  430. {
  431. Result = 0;
  432. }
  433. else
  434. {
  435. __int64 Transferred = (TotalTransfered - FTotalTransferredThen.front());
  436. Result = (unsigned int)(Transferred * MSecsPerSec / TimeSpan);
  437. }
  438. }
  439. return Result;
  440. }
  441. //---------------------------------------------------------------------------
  442. TDateTime __fastcall TFileOperationProgressType::TimeExpected()
  443. {
  444. unsigned int CurCps = CPS();
  445. if (CurCps)
  446. {
  447. return TDateTime((double)(((double)(TransferSize - TransferedSize)) / CurCps) / SecsPerDay);
  448. }
  449. else
  450. {
  451. return 0;
  452. }
  453. }
  454. //---------------------------------------------------------------------------
  455. TDateTime __fastcall TFileOperationProgressType::TotalTimeExpected()
  456. {
  457. assert(TotalSizeSet);
  458. unsigned int CurCps = CPS();
  459. // sanity check
  460. if ((CurCps > 0) && (TotalSize > TotalSkipped))
  461. {
  462. return TDateTime((double)((double)(TotalSize - TotalSkipped) / CurCps) /
  463. SecsPerDay);
  464. }
  465. else
  466. {
  467. return 0;
  468. }
  469. }
  470. //---------------------------------------------------------------------------
  471. TDateTime __fastcall TFileOperationProgressType::TotalTimeLeft()
  472. {
  473. assert(TotalSizeSet);
  474. unsigned int CurCps = CPS();
  475. // sanity check
  476. if ((CurCps > 0) && (TotalSize > TotalSkipped + TotalTransfered))
  477. {
  478. return TDateTime((double)((double)(TotalSize - TotalSkipped - TotalTransfered) / CurCps) /
  479. SecsPerDay);
  480. }
  481. else
  482. {
  483. return 0;
  484. }
  485. }