Commit | Line | Data |
---|---|---|
c8292b34 MW |
1 | /* -*-scala-*- |
2 | * | |
3 | * Reporting progress for long-running jobs | |
4 | * | |
5 | * (c) 2018 Straylight/Edgeware | |
6 | */ | |
7 | ||
8 | /*----- Licensing notice --------------------------------------------------* | |
9 | * | |
10 | * This file is part of the Trivial IP Encryption (TrIPE) Android app. | |
11 | * | |
12 | * TrIPE is free software: you can redistribute it and/or modify it under | |
13 | * the terms of the GNU General Public License as published by the Free | |
14 | * Software Foundation; either version 3 of the License, or (at your | |
15 | * option) any later version. | |
16 | * | |
17 | * TrIPE is distributed in the hope that it will be useful, but WITHOUT | |
18 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
19 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
20 | * for more details. | |
21 | * | |
22 | * You should have received a copy of the GNU General Public License | |
23 | * along with TrIPE. If not, see <https://www.gnu.org/licenses/>. | |
24 | */ | |
25 | ||
68df6e8f | 26 | package uk.org.distorted.tripe; package object progress { |
c8292b34 MW |
27 | |
28 | /*----- Imports -----------------------------------------------------------*/ | |
29 | ||
68df6e8f MW |
30 | import scala.collection.mutable.{Publisher, Subscriber}; |
31 | ||
68df6e8f | 32 | import java.lang.System.currentTimeMillis; |
c8292b34 | 33 | |
04a5abae | 34 | /*----- Progress displays -------------------------------------------------*/ |
c8292b34 | 35 | |
04a5abae MW |
36 | trait Model { |
37 | protected val t0 = currentTimeMillis; | |
c8292b34 | 38 | |
04a5abae MW |
39 | def what: String; |
40 | def max: Long; | |
c8292b34 | 41 | |
04a5abae | 42 | def eta(cur: Long): Double = { |
c8292b34 MW |
43 | /* Report the estimated time remaining in seconds, or -1 if no idea. |
44 | * | |
04a5abae MW |
45 | * The model here is very stupid. Weird jobs should override this and |
46 | * do something more sensible. | |
c8292b34 MW |
47 | */ |
48 | ||
04a5abae MW |
49 | val max = this.max; |
50 | val delta = currentTimeMillis - t0 | |
51 | if (max < 0 || cur <= 0) -1 else delta*(max - cur)/cur.toDouble | |
68df6e8f MW |
52 | } |
53 | ||
04a5abae | 54 | protected def fmt1(n: Long): String = n.toString; |
68df6e8f | 55 | |
04a5abae MW |
56 | def format(cur: Long): String = { |
57 | val max = this.max; | |
58 | val fc = fmt1(cur); | |
59 | if (max >= 0) { val fm = fmt1(max); s"%${fm.length}s/%s".format(fc, fm) } | |
60 | else if (cur > 0) fc | |
61 | else "" | |
68df6e8f | 62 | } |
04a5abae | 63 | } |
68df6e8f | 64 | |
04a5abae | 65 | class SimpleModel(val what: String, val max: Long) extends Model; |
68df6e8f | 66 | |
04a5abae | 67 | private val UDATA = Seq("kB", "MB", "GB", "TB", "PB", "EB"); |
68df6e8f | 68 | |
04a5abae MW |
69 | trait DataModel extends Model { |
70 | override def fmt1(n: Long): String = { | |
71 | val (x, u) = ((n.toDouble, "B ") /: UDATA) { (xu, n) => (xu, n) match { | |
72 | case ((x, u), name) if x >= 1024.0 => (x/1024.0, name) | |
73 | case (xu, _) => xu | |
74 | } } | |
75 | f"$x%6.1f$u%s" | |
68df6e8f MW |
76 | } |
77 | } | |
78 | ||
04a5abae MW |
79 | trait BaseReporter { |
80 | def done(); | |
81 | def failed(e: Exception); | |
82 | } | |
68df6e8f | 83 | |
04a5abae MW |
84 | trait JobReporter extends BaseReporter { |
85 | def step(cur: Long); | |
86 | def change(model: Model, cur: Long); | |
c8292b34 MW |
87 | } |
88 | ||
04a5abae MW |
89 | trait OperationReporter extends BaseReporter { |
90 | def step(detail: String); | |
91 | } | |
68df6e8f | 92 | |
9190adc6 MW |
93 | def withReporter[T, P <: BaseReporter] |
94 | (rep: P, body: P => T): T = { | |
04a5abae MW |
95 | val ret = try { body(rep) } |
96 | catch { case e: Exception => rep.failed(e); throw e; } | |
97 | rep.done(); | |
98 | ret | |
99 | } | |
68df6e8f | 100 | |
04a5abae MW |
101 | trait Eyecandy { |
102 | def note(msg: String); | |
103 | def clear(); | |
104 | def commit(); | |
105 | def record(msg: String) { note(msg); commit(); } | |
106 | def done(); | |
107 | def cancelled() { failed("cancelled"); } | |
108 | def failed(msg: String); | |
68df6e8f | 109 | |
04a5abae MW |
110 | def beginJob(model: Model): JobReporter |
111 | // = new JobReporter(model); | |
68df6e8f | 112 | |
04a5abae MW |
113 | def beginOperation(what: String): OperationReporter |
114 | // = new OperationReporter(what); | |
68df6e8f | 115 | |
04a5abae MW |
116 | def job[T](model: Model)(body: JobReporter => T): T = |
117 | withReporter(beginJob(model), body); | |
68df6e8f | 118 | |
04a5abae MW |
119 | def operation[T](what: String)(body: OperationReporter => T): T = |
120 | withReporter(beginOperation(what), body); | |
68df6e8f MW |
121 | } |
122 | ||
c8292b34 | 123 | /*----- That's all, folks -------------------------------------------------*/ |
68df6e8f MW |
124 | |
125 | } |