-
Notifications
You must be signed in to change notification settings - Fork 7
Expand file tree
/
Copy pathProcessReader.monkey2
More file actions
211 lines (154 loc) · 4.01 KB
/
ProcessReader.monkey2
File metadata and controls
211 lines (154 loc) · 4.01 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
Namespace ted2go
#rem monkeydoc The ProcessReader class.
Allow us to read output from process.
We can reuse this class with any commands.
Each command starts new process.
You should to wait until work is finished - if you run while process already running then nothing happen.
There are 2 usage scenarios: RunAsync and Run.
With RunAsync we get output by PortionRead and Finished events.
This is not blocked method.
With Run we get output as result of this call.
This is blocked method.
Both methods are using Fiber to waiting process finished.
#end
Class ProcessReader
#rem monkeydoc Tag to identify reader.
#end
Field Tag:=""
#rem monkeydoc Invoked when a process finishes execution.
#end
Field Finished:Void( output:String,exitCode:Int )
#rem monkeydoc Invoked when read portion of output from process.
#end
Field PortionRead:Void( output:String )
#rem monkeydoc Invoked when a process finishes execution AND exitCode <> 0.
#end
Field Error:Void( exitCode:Int )
#rem monkeydoc Obtain a reader instance.
#end
Method New( tag:String="" )
Tag=tag
_items.Add( Self )
End
Function StopAll()
For Local r:=Eachin _items
r.Stop()
Next
End
#rem monkeydoc
#end
Function WaitingForStopAll( wait:Future<Bool> )
_stopSize=0
_stopCounter=0
For Local r:=Eachin _items
If r.IsRunning
_stopSize+=1
r.Finished+=Lambda( s:String,c:Int )
_stopCounter+=1
If _stopCounter=_stopSize Then wait.Set( True )
End
Endif
Next
If _stopSize=0
wait.Set( True )
Return
Endif
End
#rem monkeydoc Async reading of process. You should to subscribe on (at least) Finished event to get result.
This method can be used without creation of new Fiber.
If started while process already running - nothing happen.
#end
Method RunAsync( command:String )
If _running Return
New Fiber( Lambda()
RunInternal( command )
End )
End
#rem monkeydoc Sync reading of process.
This method must be used with creation of new Fiber, because it uses Future to waiting for process finished.
Return full output of a process.
If started while process already running - immediately return an empty string.
#end
Method Run:String( command:String )
If _running Return ""
Return RunInternal( command )
End
#rem monkeydoc Terminate process execution.
#end
Method Stop()
If Not _procOpen
' If _stdoutWaiting
' _stdoutWaiting.Set( False )
' Endif
Return
Endif
_process.Terminate()
OnStop()
End
#rem monkeydoc Is reading currently in progress.
#end
Property IsRunning:Bool()
Return _running
End
Private
Field _process:Process
Field _output:String
Field _running:Bool
Field _stdoutWaiting:Future<Bool>
Field _stdoutOpen:Bool,_procOpen:Bool
Global _items:=New Stack<ProcessReader>
Global _stopSize:Int,_stopCounter:Int
Method RunInternal:String( cmd:String )
If Not Start( cmd )
Print "Failed to start process '"+cmd+"'"
Return ""
Endif
' waiting for the end
_stdoutWaiting=New Future<Bool>
_stdoutWaiting.Get()
_stdoutWaiting=Null
Return _output
End
Method Start:Bool( cmd:String )
If _running Return False
Local process:=New Process
process.Finished=Lambda()
'Print "proc.finished: "+Tag
_procOpen=False
UpdateRunning()
End
process.StdoutReady=Lambda()
'Print "proc.stdoutReady: "+Tag
Local stdout:=process.ReadStdout()
If stdout
stdout=stdout.Replace( "~r~n","~n" ).Replace( "~r","~n" )
_output+=stdout
PortionRead( stdout )
Else
_stdoutOpen=False
UpdateRunning()
Endif
End
If Not process.Start( cmd ) Return False
_process=process
_running=True
_procOpen=True
_stdoutOpen=True
_output=""
Return True
End
Method UpdateRunning()
If Not _running Or _procOpen Or _stdoutOpen Return
OnStop()
Local code:=_process.ExitCode
Finished( _output,code )
If code<>0 Then Error( code )
If _stdoutWaiting
_stdoutWaiting.Set( True )
Endif
End
Method OnStop()
_running=False
_items.Remove( Self )
End
End