openstudio-workflow 1.0.0.pat1 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +12 -0
  3. data/README.md +16 -68
  4. data/Rakefile +9 -9
  5. data/bin/openstudio_cli +786 -0
  6. data/lib/openstudio/workflow/adapters/input/local.rb +97 -0
  7. data/lib/openstudio/workflow/adapters/output/local.rb +90 -0
  8. data/lib/openstudio/workflow/adapters/output/socket.rb +70 -0
  9. data/lib/openstudio/workflow/{jobs/run_preflight/run_preflight.rb → adapters/output/web.rb} +37 -19
  10. data/lib/openstudio/workflow/{adapter.rb → adapters/output_adapter.rb} +53 -51
  11. data/lib/openstudio/workflow/job.rb +22 -0
  12. data/lib/openstudio/workflow/jobs/{run_energyplus → resources}/monthly_report.idf +0 -0
  13. data/lib/openstudio/workflow/jobs/run_energyplus.rb +49 -0
  14. data/lib/openstudio/workflow/jobs/run_ep_measures.rb +55 -0
  15. data/lib/openstudio/workflow/jobs/run_initialization.rb +136 -0
  16. data/lib/openstudio/workflow/jobs/run_os_measures.rb +59 -0
  17. data/lib/openstudio/workflow/jobs/run_postprocess.rb +53 -0
  18. data/lib/openstudio/workflow/jobs/run_preprocess.rb +81 -0
  19. data/lib/openstudio/workflow/jobs/run_reporting_measures.rb +86 -0
  20. data/lib/openstudio/workflow/jobs/run_translation.rb +49 -0
  21. data/lib/openstudio/workflow/multi_delegator.rb +1 -3
  22. data/lib/openstudio/workflow/registry.rb +137 -0
  23. data/lib/openstudio/workflow/run.rb +182 -221
  24. data/lib/openstudio/workflow/time_logger.rb +1 -1
  25. data/lib/openstudio/workflow/util/energyplus.rb +564 -0
  26. data/lib/openstudio/workflow/util/io.rb +33 -0
  27. data/lib/openstudio/workflow/util/measure.rb +520 -0
  28. data/lib/openstudio/workflow/util/model.rb +100 -0
  29. data/lib/openstudio/workflow/util/post_process.rb +177 -0
  30. data/lib/openstudio/workflow/util/weather_file.rb +108 -0
  31. data/lib/openstudio/workflow/util.rb +14 -0
  32. data/lib/openstudio/workflow/version.rb +1 -1
  33. data/lib/openstudio/workflow_json.rb +399 -0
  34. data/lib/openstudio/workflow_runner.rb +213 -0
  35. data/lib/openstudio-workflow.rb +13 -118
  36. metadata +45 -85
  37. data/lib/openstudio/extended_runner.rb +0 -105
  38. data/lib/openstudio/workflow/adapters/local.rb +0 -101
  39. data/lib/openstudio/workflow/adapters/mongo.rb +0 -227
  40. data/lib/openstudio/workflow/jobs/lib/apply_measures.rb +0 -253
  41. data/lib/openstudio/workflow/jobs/run_energyplus/run_energyplus.rb +0 -314
  42. data/lib/openstudio/workflow/jobs/run_openstudio/run_openstudio.rb +0 -230
  43. data/lib/openstudio/workflow/jobs/run_postprocess/run_postprocess.rb +0 -110
  44. data/lib/openstudio/workflow/jobs/run_reporting_measures/run_reporting_measures.rb +0 -471
  45. data/lib/openstudio/workflow/jobs/run_runmanager/run_runmanager.rb +0 -247
  46. data/lib/openstudio/workflow/jobs/run_xml/run_xml.rb +0 -279
@@ -0,0 +1,399 @@
1
+ ######################################################################
2
+ # Copyright (c) 2008-2014, Alliance for Sustainable Energy.
3
+ # All rights reserved.
4
+ #
5
+ # This library is free software; you can redistribute it and/or
6
+ # modify it under the terms of the GNU Lesser General Public
7
+ # License as published by the Free Software Foundation; either
8
+ # version 2.1 of the License, or (at your option) any later version.
9
+ #
10
+ # This library is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13
+ # Lesser General Public License for more details.
14
+ #
15
+ # You should have received a copy of the GNU Lesser General Public
16
+ # License along with this library; if not, write to the Free Software
17
+ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18
+ ######################################################################
19
+
20
+ require 'pathname'
21
+
22
+ # Optional_Shim provides a wrapper that looks like an OpenStudio Optional
23
+ class Optional_Shim
24
+ def initialize(obj)
25
+ @obj = obj
26
+ end
27
+
28
+ def empty?
29
+ @obj.nil?
30
+ end
31
+
32
+ def is_initialized
33
+ !@obj.nil?
34
+ end
35
+
36
+ def get
37
+ raise 'Uninitialized Optional_Shim' if @obj.nil?
38
+ @obj
39
+ end
40
+ end
41
+
42
+ s = ''
43
+ unless s.respond_to?(:to_StepResult)
44
+ class String
45
+ def to_StepResult
46
+ self
47
+ end
48
+ end
49
+ end
50
+ unless s.respond_to?(:to_VariantType)
51
+ class String
52
+ def to_VariantType
53
+ self
54
+ end
55
+ end
56
+ end
57
+
58
+ # WorkflowStepResultValue_Shim provides a shim interface to the WorkflowStepResultValue class in OpenStudio 2.X when running in OpenStudio 1.X
59
+ class WorkflowStepResultValue_Shim
60
+ def initialize(name, value, type)
61
+ @name = name
62
+ @value = value
63
+ @type = type
64
+ end
65
+
66
+ attr_reader :name
67
+
68
+ attr_reader :value
69
+
70
+ def variantType
71
+ @type
72
+ end
73
+
74
+ def valueAsString
75
+ @value.to_s
76
+ end
77
+
78
+ def valueAsDouble
79
+ @value.to_f
80
+ end
81
+
82
+ def valueAsInteger
83
+ @value.to_i
84
+ end
85
+
86
+ def valueAsBoolean
87
+ @value
88
+ end
89
+ end
90
+
91
+ # WorkflowStepResult_Shim provides a shim interface to the WorkflowStepResult class in OpenStudio 2.X when running in OpenStudio 1.X
92
+ class WorkflowStepResult_Shim
93
+ def initialize(result)
94
+ @result = result
95
+ end
96
+
97
+ def stepErrors
98
+ return @result[:step_errors]
99
+ end
100
+
101
+ def stepWarnings
102
+ return @result[:step_warnings]
103
+ end
104
+
105
+ def stepInfo
106
+ return @result[:step_info]
107
+ end
108
+
109
+ def stepValues
110
+ result = []
111
+ @result[:step_values].each do |step_value|
112
+ result << WorkflowStepResultValue_Shim.new(step_value[:name], step_value[:value], step_value[:type])
113
+ end
114
+ return result
115
+ end
116
+
117
+ def stepResult
118
+ Optional_Shim.new(@result[:step_result])
119
+ end
120
+
121
+ def setStepResult(step_result)
122
+ @result[:step_result] = step_result
123
+ end
124
+ end
125
+
126
+ # WorkflowStep_Shim provides a shim interface to the WorkflowStep class in OpenStudio 2.X when running in OpenStudio 1.X
127
+ class WorkflowStep_Shim
128
+ def initialize(step)
129
+ @step = step
130
+ end
131
+
132
+ attr_reader :step
133
+
134
+ def result
135
+ if @step[:result]
136
+ Optional_Shim.new(WorkflowStepResult_Shim.new(@step[:result]))
137
+ else
138
+ Optional_Shim.new
139
+ end
140
+ end
141
+
142
+ # std::string measureDirName() const;
143
+ def measureDirName
144
+ @step[:measure_dir_name]
145
+ end
146
+
147
+ # std::map<std::string, Variant> arguments() const;
148
+ def arguments
149
+ # TODO: match C++
150
+ @step[:arguments]
151
+ end
152
+ end
153
+
154
+ # WorkflowJSON_Shim provides a shim interface to the WorkflowJSON class in OpenStudio 2.X when running in OpenStudio 1.X
155
+ class WorkflowJSON_Shim
156
+ def initialize(workflow, osw_dir)
157
+ @workflow = workflow
158
+ @osw_dir = osw_dir
159
+ @current_step_index = 0
160
+ end
161
+
162
+ # std::string string(bool includeHash=true) const;
163
+ def string
164
+ JSON.fast_generate(@workflow)
165
+ end
166
+
167
+ def timeString
168
+ ::Time.now.utc.strftime("%Y%m%dT%H%M%SZ")
169
+ end
170
+
171
+ # Returns the absolute path to the directory this workflow was loaded from or saved to. Returns current working dir for new WorkflowJSON.
172
+ # openstudio::path oswDir() const;
173
+ def oswDir
174
+ OpenStudio.toPath(@osw_dir)
175
+ end
176
+
177
+ def saveAs(path)
178
+ File.open(path.to_s, 'w') do |file|
179
+ file << JSON.pretty_generate(@workflow)
180
+ end
181
+ end
182
+
183
+ # Sets the started at time.
184
+ def start
185
+ @workflow[:started_at] = timeString
186
+ end
187
+
188
+ # Get the current step index.
189
+ def currentStepIndex
190
+ @current_step_index
191
+ end
192
+
193
+ # Get the current step.
194
+ # boost::optional<WorkflowStep> currentStep() const;
195
+ def currentStep
196
+ steps = @workflow[:steps]
197
+
198
+ step = nil
199
+ if @current_step_index < steps.size
200
+ step = WorkflowStep_Shim.new(steps[@current_step_index])
201
+ end
202
+ return Optional_Shim.new(step)
203
+ end
204
+
205
+ # Increments current step, returns true if there is another step.
206
+ # bool incrementStep();
207
+ def incrementStep
208
+ @current_step_index += 1
209
+
210
+ if @current_step_index < @workflow[:steps].size
211
+ return true
212
+ end
213
+
214
+ return false
215
+ end
216
+
217
+ # Returns the root directory, default value is '.'. Evaluated relative to oswDir if not absolute.
218
+ # openstudio::path rootDir() const;
219
+ # openstudio::path absoluteRootDir() const;
220
+ def rootDir
221
+ if @workflow[:root_dir]
222
+ OpenStudio.toPath(@workflow[:root_dir])
223
+ else
224
+ OpenStudio.toPath(@osw_dir)
225
+ end
226
+ end
227
+
228
+ def absoluteRootDir
229
+ OpenStudio.toPath(File.absolute_path(rootDir.to_s, @osw_dir.to_s))
230
+ end
231
+
232
+ # Returns the run directory, default value is './run'. Evaluated relative to rootDir if not absolute.
233
+ # openstudio::path runDir() const;
234
+ # openstudio::path absoluteRunDir() const;
235
+ def runDir
236
+ if @workflow[:run_directory]
237
+ OpenStudio.toPath(@workflow[:run_directory])
238
+ else
239
+ OpenStudio.toPath('./run')
240
+ end
241
+ end
242
+
243
+ def absoluteRunDir
244
+ OpenStudio.toPath(File.absolute_path(runDir.to_s, rootDir.to_s))
245
+ end
246
+
247
+ def outPath
248
+ if @workflow[:out_name]
249
+ OpenStudio.toPath(@workflow[:out_name])
250
+ else
251
+ OpenStudio.toPath('./out.osw')
252
+ end
253
+ end
254
+
255
+ def absoluteOutPath
256
+ OpenStudio.toPath(File.absolute_path(outPath.to_s, oswDir.to_s))
257
+ end
258
+
259
+ # Returns the paths that will be searched in order for files, default value is './files/'. Evaluated relative to rootDir if not absolute.
260
+ # std::vector<openstudio::path> filePaths() const;
261
+ # std::vector<openstudio::path> absoluteFilePaths() const;
262
+ def filePaths
263
+ result = OpenStudio::PathVector.new
264
+ if @workflow[:file_paths]
265
+ @workflow[:file_paths].each do |file_path|
266
+ result << OpenStudio.toPath(file_path)
267
+ end
268
+ else
269
+ result << OpenStudio.toPath('./files')
270
+ result << OpenStudio.toPath('./weather')
271
+ result << OpenStudio.toPath('../../files')
272
+ result << OpenStudio.toPath('../../weather')
273
+ result << OpenStudio.toPath('./')
274
+ end
275
+ result
276
+ end
277
+
278
+ def absoluteFilePaths
279
+ result = OpenStudio::PathVector.new
280
+ filePaths.each do |file_path|
281
+ result << OpenStudio.toPath(File.absolute_path(file_path.to_s, rootDir.to_s))
282
+ end
283
+ result
284
+ end
285
+
286
+ # Attempts to find a file by name, searches through filePaths in order and returns first match.
287
+ # boost::optional<openstudio::path> findFile(const openstudio::path& file);
288
+ # boost::optional<openstudio::path> findFile(const std::string& fileName);
289
+ def findFile(file)
290
+ file = file.to_s
291
+
292
+ # check if absolute and exists
293
+ if Pathname.new(file).absolute?
294
+ if File.exist?(file)
295
+ return OpenStudio::OptionalPath.new(OpenStudio.toPath(file))
296
+ end
297
+
298
+ # absolute path does not exist
299
+ return OpenStudio::OptionalPath.new
300
+ end
301
+
302
+ absoluteFilePaths.each do |file_path|
303
+ result = File.join(file_path.to_s, file)
304
+ if File.exist?(result)
305
+ return OpenStudio::OptionalPath.new(OpenStudio.toPath(result))
306
+ end
307
+ end
308
+ OpenStudio::OptionalPath.new
309
+ end
310
+
311
+ # Returns the paths that will be searched in order for measures, default value is './measures/'. Evaluated relative to rootDir if not absolute.
312
+ # std::vector<openstudio::path> measurePaths() const;
313
+ # std::vector<openstudio::path> absoluteMeasurePaths() const;
314
+ def measurePaths
315
+ result = OpenStudio::PathVector.new
316
+ if @workflow[:measure_paths]
317
+ @workflow[:measure_paths].each do |measure_path|
318
+ result << OpenStudio.toPath(measure_path)
319
+ end
320
+ else
321
+ result << OpenStudio.toPath('./measures')
322
+ result << OpenStudio.toPath('../../measures')
323
+ result << OpenStudio.toPath('./')
324
+ end
325
+ result
326
+ end
327
+
328
+ def absoluteMeasurePaths
329
+ result = OpenStudio::PathVector.new
330
+ measurePaths.each do |measure_path|
331
+ result << OpenStudio.toPath(File.absolute_path(measure_path.to_s, rootDir.to_s))
332
+ end
333
+ result
334
+ end
335
+
336
+ # Attempts to find a measure by name, searches through measurePaths in order and returns first match. */
337
+ # boost::optional<openstudio::path> findMeasure(const openstudio::path& measureDir);
338
+ # boost::optional<openstudio::path> findMeasure(const std::string& measureDirName);
339
+ def findMeasure(measureDir)
340
+ measureDir = measureDir.to_s
341
+
342
+ # check if absolute and exists
343
+ if Pathname.new(measureDir).absolute?
344
+ if File.exist?(measureDir)
345
+ return OpenStudio::OptionalPath.new(OpenStudio.toPath(measureDir))
346
+ end
347
+
348
+ # absolute path does not exist
349
+ return OpenStudio::OptionalPath.new
350
+ end
351
+
352
+ absoluteMeasurePaths.each do |measure_path|
353
+ result = File.join(measure_path.to_s, measureDir)
354
+ if File.exist?(result)
355
+ return OpenStudio::OptionalPath.new(OpenStudio.toPath(result))
356
+ end
357
+ end
358
+ OpenStudio::OptionalPath.new
359
+ end
360
+
361
+ # Returns the seed file path. Evaluated relative to filePaths if not absolute.
362
+ # boost::optional<openstudio::path> seedFile() const;
363
+ def seedFile
364
+ result = OpenStudio::OptionalPath.new
365
+ if @workflow[:seed_file]
366
+ result = OpenStudio::OptionalPath.new(OpenStudio.toPath(@workflow[:seed_file]))
367
+ end
368
+ result
369
+ end
370
+
371
+ # Returns the weather file path. Evaluated relative to filePaths if not absolute.
372
+ # boost::optional<openstudio::path> weatherFile() const;
373
+ def weatherFile
374
+ result = OpenStudio::OptionalPath.new
375
+ if @workflow[:weather_file]
376
+ result = OpenStudio::OptionalPath.new(OpenStudio.toPath(@workflow[:weather_file]))
377
+ end
378
+ result
379
+ end
380
+
381
+ # Returns the workflow steps. */
382
+ # std::vector<WorkflowStep> workflowSteps() const;
383
+ def workflowSteps
384
+ result = []
385
+ @workflow[:steps].each do |step|
386
+ result << WorkflowStep_Shim.new(step)
387
+ end
388
+ result
389
+ end
390
+
391
+ def setCompletedStatus(status)
392
+ @workflow[:completed_status] = status
393
+ @workflow[:completed_at] = timeString
394
+ end
395
+
396
+ def setEplusoutErr(eplusout_err)
397
+ @workflow[:eplusout_err] = eplusout_err
398
+ end
399
+ end
@@ -0,0 +1,213 @@
1
+ ######################################################################
2
+ # Copyright (c) 2008-2014, Alliance for Sustainable Energy.
3
+ # All rights reserved.
4
+ #
5
+ # This library is free software; you can redistribute it and/or
6
+ # modify it under the terms of the GNU Lesser General Public
7
+ # License as published by the Free Software Foundation; either
8
+ # version 2.1 of the License, or (at your option) any later version.
9
+ #
10
+ # This library is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13
+ # Lesser General Public License for more details.
14
+ #
15
+ # You should have received a copy of the GNU Lesser General Public
16
+ # License along with this library; if not, write to the Free Software
17
+ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18
+ ######################################################################
19
+
20
+ require_relative 'workflow_json'
21
+
22
+ # Extend OS Runner to persist measure information throughout the workflow
23
+ # Provide shims to support OpenStudio 2.X functionality in OpenStudio 1.X
24
+ class WorkflowRunner < OpenStudio::Ruleset::OSRunner
25
+ def initialize(multi_logger, workflow_json, openstudio_2)
26
+ @multi_logger = multi_logger
27
+ @workflow_json = workflow_json
28
+ @openstudio_2 = openstudio_2
29
+
30
+ begin
31
+ # OpenStudio 2.X
32
+ super(@workflow_json)
33
+ rescue Exception => e
34
+ # OpenStudio 1.X
35
+ @workflow = workflow_json
36
+ @units_preference = 'SI'
37
+ @language_preference = 'EN'
38
+ super()
39
+ end
40
+ end
41
+
42
+ def timeString
43
+ ::Time.now.utc.strftime("%Y%m%dT%H%M%SZ")
44
+ end
45
+
46
+ # Returns the workflow currently being run. New in OS 2.0.
47
+ # WorkflowJSON workflow() const;
48
+ def workflow
49
+ if @openstudio_2
50
+ super
51
+ else
52
+ @workflow
53
+ end
54
+ end
55
+
56
+ # Returns preferred unit system, either 'IP' or 'SI'. New in OS 2.0. */
57
+ # std::string unitsPreference() const;
58
+ def unitsPreference
59
+ if @openstudio_2
60
+ super
61
+ else
62
+ @units_preference
63
+ end
64
+ end
65
+
66
+ # Returns preferred language, e.g. 'en' or 'fr'. New in OS 2.0. */
67
+ # std::string languagePreference() const;
68
+ def languagePreference
69
+ if @openstudio_2
70
+ super
71
+ else
72
+ @language_preference
73
+ end
74
+ end
75
+
76
+ # called right when each measure is run
77
+ # only called in OpenStudio 1.X
78
+ # virtual void prepareForUserScriptRun(const UserScript& userScript);
79
+ def prepareForUserScriptRun(userScript)
80
+ if @openstudio_2
81
+ prepareForMeasureRun(userScript)
82
+ else
83
+ current_step = @workflow.currentStep
84
+
85
+ unless current_step.empty?
86
+ current_step.get.step[:result] = {}
87
+ current_step.get.step[:result][:started_at] = timeString
88
+ end
89
+
90
+ # TODO: capture std out and err
91
+
92
+ # TODO: get initial list of files
93
+
94
+ super
95
+ end
96
+ end
97
+
98
+ def result
99
+ if @openstudio_2
100
+ super
101
+ else
102
+ os_result = super
103
+
104
+ current_step = @workflow.currentStep
105
+
106
+ if current_step.empty?
107
+ raise 'Cannot find current_step'
108
+ end
109
+ current_step = current_step.get
110
+
111
+ if current_step.step[:result].nil?
112
+ # skipped, prepareForUserScriptRun was not called
113
+ current_step.step[:result] = {}
114
+ current_step.step[:result][:started_at] = timeString
115
+ current_step.step[:result][:step_result] = 'Skip'
116
+ else
117
+ current_step.step[:result][:step_result] = os_result.value.valueName
118
+ end
119
+
120
+ current_step.step[:result][:completed_at] = timeString
121
+
122
+ # TODO: restore stdout and stderr
123
+
124
+ # TODO: check for created files
125
+
126
+ current_step.step[:result][:step_errors] = []
127
+ os_result.errors.each do |error|
128
+ current_step.step[:result][:step_errors] << error.logMessage
129
+ end
130
+
131
+ current_step.step[:result][:step_warnings] = []
132
+ os_result.warnings.each do |warning|
133
+ current_step.step[:result][:step_warnings] << warning.logMessage
134
+ end
135
+
136
+ current_step.step[:result][:step_info] = []
137
+ os_result.info.each do |info|
138
+ current_step.step[:result][:step_info] << info.logMessage
139
+ end
140
+
141
+ unless os_result.initialCondition.empty?
142
+ current_step.step[:result][:initial_condition] = os_result.initialCondition.get.logMessage
143
+ end
144
+
145
+ unless os_result.finalCondition.empty?
146
+ current_step.step[:result][:final_condition] = os_result.finalCondition.get.logMessage
147
+ end
148
+
149
+ current_step.step[:result][:step_values] = []
150
+ os_result.attributes.each do |attribute|
151
+ result = nil
152
+ if attribute.valueType == 'Boolean'.to_AttributeValueType
153
+ result = { name: attribute.name, value: attribute.valueAsBoolean, type: 'Boolean' }
154
+ elsif attribute.valueType == 'Double'.to_AttributeValueType
155
+ result = { name: attribute.name, value: attribute.valueAsDouble, type: 'Double' }
156
+ elsif attribute.valueType == 'Integer'.to_AttributeValueType
157
+ result = { name: attribute.name, value: attribute.valueAsInteger, type: 'Integer' }
158
+ elsif attribute.valueType == 'Unsigned'.to_AttributeValueType
159
+ result = { name: attribute.name, value: attribute.valueAsUnsigned, type: 'Integer' }
160
+ elsif attribute.valueType == 'String'.to_AttributeValueType
161
+ result = { name: attribute.name, value: attribute.valueAsString, type: 'String' }
162
+ end
163
+
164
+ current_step.step[:result][:step_values] << result unless result.nil?
165
+ end
166
+
167
+ return WorkflowStepResult_Shim.new(current_step.step[:result])
168
+ end
169
+ end
170
+
171
+ # incrementing step copies result to previous results
172
+ # void incrementStep();
173
+ def incrementStep
174
+ if @openstudio_2
175
+ super
176
+ else
177
+ # compute result
178
+ current_result = result
179
+
180
+ @workflow.incrementStep
181
+ end
182
+ end
183
+
184
+ # Overload registerInfo
185
+ def registerInfo(message)
186
+ super
187
+ @multi_logger.info message
188
+ end
189
+
190
+ # Overload registerInfo
191
+ def registerWarning(message)
192
+ super
193
+ @multi_logger.warn message
194
+ end
195
+
196
+ # Overload registerError
197
+ def registerError(message)
198
+ super
199
+ @multi_logger.error message
200
+ end
201
+
202
+ # Overload registerInitialCondition
203
+ def registerInitialCondition(message)
204
+ super
205
+ @multi_logger.info message
206
+ end
207
+
208
+ # Overload registerFinalCondition
209
+ def registerFinalCondition(message)
210
+ super
211
+ @multi_logger.info message
212
+ end
213
+ end