1 #Region "Microsoft.VisualBasic::0c4037dc8f33ba759713e63fddc0e238, Microsoft.VisualBasic.Core\ApplicationServices\Terminal\ProgressBar\ProgressBar.vb"
2
3     ' Author:
4     
5     '       asuka (amethyst.asuka@gcmodeller.org)
6     '       xie (genetics@smrucc.org)
7     '       xieguigang (xie.guigang@live.com)
8     
9     ' Copyright (c) 2018 GPL3 Licensed
10     
11     
12     ' GNU GENERAL PUBLIC LICENSE (GPL3)
13     
14     
15     ' This program is free software: you can redistribute it and/or modify
16     ' it under the terms of the GNU General Public License as published by
17     ' the Free Software Foundation, either version 3 of the License, or
18     ' (at your option) any later version.
19     
20     ' This program is distributed in the hope that it will be useful,
21     ' but WITHOUT ANY WARRANTY; without even the implied warranty of
22     ' MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
23     ' GNU General Public License for more details.
24     
25     ' You should have received a copy of the GNU General Public License
26     ' along with this program. If not, see <http://www.gnu.org/licenses/>.
27
28
29
30     ' /********************************************************************************/
31
32     ' Summaries:
33
34     '     Structure ColorTheme
35     
36     '         Properties: BackgroundColor, IsEmpty, MessageDetailColor, ProgressBarColor, ProgressMsgColor
37     
38     '         Function: [Default], DefaultTheme
39     
40     '     Class ProgressBar
41     
42     '         Properties: ElapsedMilliseconds, Enable
43     
44     '         Constructor: (+3 OverloadsSub New
45     '         Sub: [Step], consoleWindowResize, (+2 Overloads) Dispose, SetProgress, SetToEchoLine
46     '              tick
47     
48     '     Class ProgressProvider
49     
50     '         Properties: Current, Target
51     
52     '         Constructor: (+1 OverloadsSub New
53     '         Function: [Step], (+2 Overloads) ETA, StepProgress, ToString
54     
55     
56     ' /********************************************************************************/
57
58 #End Region
59
60 Imports System.Drawing
61 Imports System.Runtime.CompilerServices
62 Imports Microsoft.VisualBasic.Language.Default
63 Imports Microsoft.VisualBasic.Serialization.JSON
64
65 Namespace Terminal.ProgressBar
66
67     ''' <summary>
68     ''' The <see cref="ConsoleColor"/> theme for the <see cref="ProgressBar"/>
69     ''' </summary>
70     Public Structure ColorTheme : Implements IsEmpty
71
72         ' [ERROR 2017/11/5 下午 0704:37] <Print>:System.Exception: Print 
73         '  ---> System.Reflection.TargetInvocationException: 调用的目标发生了异常。 
74         '  ---> System.Exception: [
75         '           "未能从程序集“Microsoft.VisualBasic.Architecture.Framework_v3.0_22.0.76.201__8da45dcd8060cc9a, Version=3.0.32.34335, Culture=neutral, PublicKeyToken=null”中加载类型“Microsoft.VisualBasic.Terminal.ColorTheme”。",
76         '           "未能从程序集“Microsoft.VisualBasic.Architecture.Framework_v3.0_22.0.76.201__8da45dcd8060cc9a, Version=3.0.32.34335, Culture=neutral, PublicKeyToken=null”中加载类型“Microsoft.VisualBasic.Terminal.ColorTheme”。"
77         '       ] 
78         '  ---> System.Reflection.ReflectionTypeLoadException: 无法加载一个或多个请求的类型。有关更多信息,请检索 LoaderExceptions 属性。
79
80         ' 在 System.Reflection.RuntimeModule.GetTypes(RuntimeModule Module)
81         ' 在 System.Reflection.Assembly.GetTypes()
82         ' 在 Microsoft.VisualBasic.CommandLine.Reflection.RunDllEntryPoint.GetTypes(Assembly assm)
83         ' --- 内部异常堆栈跟踪的结尾 ---
84         ' 在 Microsoft.VisualBasic.CommandLine.Reflection.RunDllEntryPoint.GetTypes(Assembly assm)
85         ' 在 Microsoft.VisualBasic.CommandLine.Reflection.RunDllEntryPoint.GetDllMethod(Assembly assembly, String entryPoint)
86         ' 在 SMRUCC.WebCloud.httpd.CLI.RunDll(CommandLine args)
87         ' --- 内部异常堆栈跟踪的结尾 ---
88         ' 在 System.RuntimeMethodHandle.InvokeMethod(Object target, Object[] arguments, Signature sig, Boolean constructor)
89         ' 在 System.Reflection.RuntimeMethodInfo.UnsafeInvokeInternal(Object obj, Object[] parameters, Object[] arguments)
90         ' 在 System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
91         ' 在 System.Reflection.MethodBase.Invoke(Object obj, Object[] parameters)
92         ' 在 Microsoft.VisualBasic.CommandLine.Reflection.EntryPoints.APIEntryPoint.__directInvoke(Object[] callParameters, Object target, Boolean Throw)
93         ' --- 内部异常堆栈跟踪的结尾 ---
94
95         Public Property BackgroundColor As ConsoleColor
96         Public Property ProgressBarColor As ConsoleColor
97         Public Property ProgressMsgColor As ConsoleColor
98         Public Property MessageDetailColor As ConsoleColor
99
100         ''' <summary>
101         ''' Test if all of the property value is equals to ZERO(<see cref="ConsoleColor.Black"/>).
102         ''' </summary>
103         ''' <returns></returns>
104         Public ReadOnly Property IsEmpty As Boolean Implements IsEmpty.IsEmpty
105             <MethodImpl(MethodImplOptions.AggressiveInlining)>
106             Get
107                 Return BackgroundColor = 0 AndAlso
108                     ProgressBarColor = 0 AndAlso
109                     ProgressMsgColor = 0 AndAlso
110                     MessageDetailColor = 0
111             End Get
112         End Property
113
114         ''' <summary>
115         ''' Default value for optional parameter
116         ''' </summary>
117         ''' <returns></returns>
118         ''' <remarks>
119         ''' ###### 2017-11-5
120         ''' This property will cause bug in reflection.
121         ''' 
122         ''' ###### 2017-11-6
123         ''' Change from property to function to avoid bug caused application crashed
124         ''' </remarks>
125         Public Shared Function DefaultTheme() As DefaultValue(Of ColorTheme)
126             Return New DefaultValue(Of ColorTheme) With {
127                 .Value = [Default](),
128                 .assert = Function(t)
129                               Return DirectCast(t, ColorTheme).IsEmpty
130                           End Function
131             }
132         End Function
133         ''' <summary>
134         ''' The default color theme values
135         ''' </summary>
136         ''' <returns></returns>
137         Public Shared Function [Default]() As ColorTheme
138             Return New ColorTheme With {
139                 .BackgroundColor = ConsoleColor.Cyan,
140                 .MessageDetailColor = ConsoleColor.White,
141                 .ProgressBarColor = ConsoleColor.Yellow,
142                 .ProgressMsgColor = ConsoleColor.Green
143             }
144         End Function
145     End Structure
146
147     ''' <summary>
148     ''' Progress bar for the <see cref="Console"/> Screen.
149     ''' </summary>
150     ''' <remarks>
151     ''' http://www.cnblogs.com/masonlu/p/4668232.html
152     ''' </remarks>
153     Public Class ProgressBar : Inherits AbstractBar
154         Implements IDisposable
155
156         Dim colorBack As ConsoleColor = Console.BackgroundColor
157         Dim colorFore As ConsoleColor = Console.ForegroundColor
158
159         ''' <summary>
160         ''' The current progress percentage: [0, 100]
161         ''' </summary>
162         Dim current%
163         Dim y%
164         Dim theme As ColorTheme
165
166         Shared disabled As Boolean
167
168         Public Shared Property Enable As Boolean
169             Get
170                 Return Not disabled
171             End Get
172             Set(value As Boolean)
173                 disabled = Not value
174             End Set
175         End Property
176
177         Shared Sub New()
178             disabled = App.GetVariable("progress_bar").TextEquals(NameOf(disabled))
179         End Sub
180
181         ''' <summary>
182         ''' Create a console progress bar object with custom configuration.
183         ''' </summary>
184         ''' <param name="title">The title of the task which will takes long time for running.</param>
185         ''' <param name="Y">The row position number of the progress bar.</param>
186         ''' <param name="CLS">Clear the console screen?</param>
187         Sub New(title$, Y%, Optional CLS As Boolean = FalseOptional theme As ColorTheme = Nothing)
188             If CLS AndAlso App.IsConsoleApp AndAlso Not disabled Then
189                 Call Console.Clear()
190             End If
191
192             Call Console.WriteLine(title)
193
194             Me.theme = theme Or ColorTheme.DefaultTheme
195             Me.y = Y
196
197             If Not disabled Then
198                 AddHandler TerminalEvents.Resize, AddressOf consoleWindowResize
199
200                 Try
201                     Call consoleWindowResize(Nothing, Nothing)
202                 Catch ex As Exception
203
204                 End Try
205             End If
206         End Sub
207
208         ''' <summary>
209         ''' Create a console progress bar with default theme color <see cref="ColorTheme.DefaultTheme"/>
210         ''' (在当前位置之后设置进度条,这个构造函数不会清除整个终端屏幕)
211         ''' </summary>
212         ''' <param name="title$">The task title</param>
213         Sub New(title$)
214             Call Me.New(title, Y:=Console.CursorTop, CLS:=False)
215         End Sub
216
217         Private Sub consoleWindowResize(size As Size, old As Size)
218             Console.ResetColor()
219             Console.SetCursorPosition(0, y)
220             Console.BackgroundColor = ConsoleColor.DarkCyan
221             For i = 0 To Console.WindowWidth - 3
222                 '(0,1) 第二行
223                 Console.Write(" ")
224             Next
225             '(0,1) 第二行
226             Console.WriteLine(" ")
227             Console.BackgroundColor = colorBack
228
229             Call SetToEchoLine()
230         End Sub
231
232         ' title
233         ' progress bar
234         ' details
235         '
236         ' echo
237
238         ''' <summary>
239         ''' 将终端的输出位置放置到详细信息的下一行
240         ''' </summary>
241         <MethodImpl(MethodImplOptions.AggressiveInlining)>
242         Public Sub SetToEchoLine()
243             If Not disabled Then
244                 Console.SetCursorPosition(0, y + 3)
245             End If
246         End Sub
247
248         Public Overrides Sub [Step]()
249             Call SetProgress(current)
250             current += 1
251         End Sub
252
253         Dim timer As Stopwatch = Stopwatch.StartNew
254
255         ''' <summary>
256         ''' 获取当前实例测量得出的总运行时间(以毫秒为单位)。
257         ''' </summary>
258         ''' <returns>
259         ''' 一个只读长整型,表示当前实例测量得出的总毫秒数。
260         ''' </returns>
261         Public ReadOnly Property ElapsedMilliseconds As Long
262             <MethodImpl(MethodImplOptions.AggressiveInlining)>
263             Get
264                 Return timer.ElapsedMilliseconds
265             End Get
266         End Property
267
268         ''' <summary>
269         ''' 更新进度条的当前状态信息
270         ''' </summary>
271         ''' <param name="p%"></param>
272         ''' <param name="details$"></param>
273         Private Sub tick(p%, details$)
274             Console.BackgroundColor = ConsoleColor.Yellow
275             ' /运算返回完整的商,包括余数,SetCursorPosition会自动四舍五入
276             Dim cx As Integer = p * (Console.WindowWidth - 2) / 100
277
278             Console.SetCursorPosition(0, y)
279
280             If p < current Then
281                 Call consoleWindowResize(Nothing, Nothing)
282             End If
283
284             For i As Integer = 0 To cx
285                 Console.Write(" ")
286             Next
287
288             Console.BackgroundColor = colorBack
289             Console.ForegroundColor = ConsoleColor.Green
290             Console.SetCursorPosition(0, y + 1)
291             Console.Write("{0}%", p)
292             Console.ForegroundColor = colorFore
293
294             If Not String.IsNullOrEmpty(details) Then
295                 Console.WriteLine(vbTab & details)
296             End If
297
298             Call SetToEchoLine()
299         End Sub
300
301         ''' <summary>
302         ''' <paramref name="percent"/>是进度条的百分比
303         ''' </summary>
304         ''' <param name="percent">Percentage, 假设是从p到current</param>
305         Public Sub SetProgress(percent%, Optional details$ = "")
306             current = percent
307
308             If Not disabled Then
309                 Call tick(current, details)
310             End If
311         End Sub
312
313 #Region "IDisposable Support"
314         Private disposedValue As Boolean To detect redundant calls
315
316         ' IDisposable
317         Protected Overridable Sub Dispose(disposing As Boolean)
318             If Not disposedValue Then
319                 If disposing Then
320                     If Not disabled Then
321                         ' TODO: dispose managed state (managed objects).
322                         RemoveHandler TerminalEvents.Resize, AddressOf consoleWindowResize
323                     End If
324
325                     Call timer.Stop()
326                 End If
327
328                 ' TODO: free unmanaged resources (unmanaged objects) and override Finalize() below.
329                 ' TODO: set large fields to null.
330             End If
331             disposedValue = True
332         End Sub
333
334         ' TODO: override Finalize() only if Dispose(disposing As Boolean) above has code to free unmanaged resources.
335         'Protected Overrides Sub Finalize()
336         '    ' Do not change this code.  Put cleanup code in Dispose(disposing As Boolean) above.
337         '    Dispose(False)
338         '    MyBase.Finalize()
339         'End Sub
340
341         ' This code added by Visual Basic to correctly implement the disposable pattern.
342         Public Sub Dispose() Implements IDisposable.Dispose
343             Do not change this code.  Put cleanup code in Dispose(disposing As Boolean) above.
344             Dispose(True)
345             ' TODO: uncomment the following line if Finalize() is overridden above.
346             ' GC.SuppressFinalize(Me)
347         End Sub
348 #End Region
349     End Class
350
351     Public Class ProgressProvider
352
353         ''' <summary>
354         ''' 整个工作的总的tick数
355         ''' </summary>
356         ''' <returns></returns>
357         Public ReadOnly Property Target As Integer
358         ''' <summary>
359         ''' 当前已经完成的tick数
360         ''' </summary>
361         ''' <returns></returns>
362         Public ReadOnly Property Current As Integer
363
364         ''' <summary>
365         ''' 生成进度条的百分比值
366         ''' </summary>
367         ''' <param name="total"></param>
368         Sub New(total As Integer)
369             Target = total
370         End Sub
371
372         Dim previous#
373         Dim previousTime&
374
375         Public Function ETA(elapsed&, Optional avg As Boolean = TrueAs TimeSpan
376             Dim out As TimeSpan
377
378             If avg Then
379                 out = ETA(0R, Current / Target, elapsed)
380             Else
381                 out = ETA(
382                     previous,
383                     Current / Target,
384                     elapsed - previousTime)
385
386                 previousTime = elapsed
387                 previous = Current / Target
388             End If
389
390             Return out
391         End Function
392
393         ''' <summary>
394         ''' 返回来的百分比小数,还需要乘以100才能得到进度
395         ''' </summary>
396         ''' <returns></returns>
397         Public Function [Step]() As Double
398             _Current += 1
399             Return Current / Target
400         End Function
401
402         ''' <summary>
403         ''' 百分比进度,不需要再乘以100了
404         ''' </summary>
405         ''' <returns></returns>
406         Public Function StepProgress() As Integer
407             Return CInt([Step]() * 100)
408         End Function
409
410         Public Overrides Function ToString() As String
411             Return Me.GetJson
412         End Function
413
414         ''' <summary>
415         ''' 
416         ''' </summary>
417         ''' <param name="previous#">百分比</param>
418         ''' <param name="cur#">百分比</param>
419         ''' <param name="Elapsed#">当前的这个百分比差所经历过的时间</param>
420         ''' <returns></returns>
421         Public Shared Function ETA(previous#, cur#, Elapsed&) As TimeSpan
422             Dim d# = cur - previous
423
424             If d# = 0R Then
425                 d = 0.000000001
426             End If
427
428             Dim lefts = (1 - cur) / d   ' lefts = 100% - currents
429             Dim time = lefts * Elapsed
430             Dim estimates = TimeSpan.FromMilliseconds(time)
431             Return estimates
432         End Function
433     End Class
434 End Namespace