· 4 years ago · Jan 20, 2021, 02:38 AM
1package com.mincom.ellipse.script.custom
2
3import java.nio.file.Files
4import java.util.concurrent.TimeUnit
5
6import javax.sql.DataSource
7
8import org.apache.commons.io.FilenameUtils
9import org.apache.commons.lang.StringUtils
10import org.slf4j.Logger
11import org.slf4j.LoggerFactory
12
13import com.mincom.batch.script.Restart
14import com.mincom.ellipse.edoi.ejb.msf010.MSF010Key
15import com.mincom.ellipse.edoi.ejb.msf010.MSF010Rec
16import com.mincom.ellipse.edoi.ejb.msf012.MSF012Key
17import com.mincom.ellipse.edoi.ejb.msf012.MSF012Rec
18import com.mincom.ellipse.edoi.ejb.msf080.MSF080Key
19import com.mincom.ellipse.edoi.ejb.msf080.MSF080Rec
20import com.mincom.ellipse.edoi.ejb.msf081.MSF081Key
21import com.mincom.ellipse.edoi.ejb.msf081.MSF081Rec
22import com.mincom.ellipse.edoi.ejb.msf085.MSF085Key
23import com.mincom.ellipse.edoi.ejb.msf085.MSF085Rec
24import com.mincom.ellipse.edoi.ejb.msf08b.MSF08BKey
25import com.mincom.ellipse.edoi.ejb.msf08b.MSF08BRec
26import com.mincom.ellipse.efs.EFSFile
27import com.mincom.ellipse.efs.EFSHelper
28import com.mincom.ellipse.efs.EFSTypes
29import com.mincom.ellipse.efs.impl.EllipseDBFileSystemImpl
30import com.mincom.enterpriseservice.ellipse.batchrequest.BatchRequestServiceCreateRequestDTO
31import com.mincom.enterpriseservice.ellipse.condmeasurement.CondMeasurementServiceCreateRequestDTO
32import com.mincom.enterpriseservice.ellipse.equipment.EquipmentServiceReadReplyDTO
33import com.mincom.enterpriseservice.ellipse.equipment.EquipmentServiceReadRequestDTO
34import com.mincom.enterpriseservice.exception.EnterpriseServiceOperationException
35import com.mincom.eql.Constraint
36import com.mincom.eql.Query
37import com.mincom.eql.impl.QueryImpl
38import com.opencsv.CSVParser
39import com.opencsv.CSVParserBuilder
40import com.opencsv.CSVReader
41import com.opencsv.CSVReaderBuilder
42
43import groovy.sql.Sql
44
45class TableData {
46 String assocRec
47 String tableDesc
48 String tableCode
49
50 public void setTableCode(String tableCode) {
51 //Because, CmoFile.headers stored in upperCase
52 this.tableCode = tableCode?.toUpperCase()
53 }
54}
55
56class ParamOcbcmo {
57 String timer
58 String iteration
59 String originator
60 String submitMsb340
61}
62
63class CmoFile {
64 List<String> dataRows = []
65 List<Map<String, String>> dataPairs = []
66}
67
68class CmoResult {
69 String fileNumber
70 String inputFileName
71 int readCount
72 int rejectCount
73 int processedCount
74 String rejectFileName
75 String headerErrorMessages
76 String categoryErrorMessages
77}
78
79/**
80 * This class was implementations of <b>OCBCMO - Lab. Condition Monitoring Data to Ellipse</b> interface.
81 *
82 * <p>Tables used:</p>
83 * <ul>
84 * <li><b>MSF010</b></li>
85 * <li><b>MSF600</b></li>
86 * </ul>
87 *
88 * <p>Services used:</p>
89 * <ul>
90 * <li><b>CondMeasurement</b> -> throws EnterpriseServiceOperationException</li>
91 * </ul>
92 *
93 * @author Michael Kempf
94 * @author Adhitya Yudha
95 * @author Chaerun Muhaimin
96 * @since December 12th, 2017
97 *
98 */
99public class ProcessOcbcmo extends SuperBatch {
100
101 private static final version = 6
102
103 private ParamOcbcmo batchParams
104 private static final String MEASURE_DATE = 'setMeasureDate'
105 private static final String COMP_CODE = 'setCompCode'
106 private static final String EQUIPMENT_REF = 'setEquipmentRef'
107
108 private static final String MSB340_BATCH = 'MSB340'
109
110 private List<String> requiredAttribs = [MEASURE_DATE, COMP_CODE, EQUIPMENT_REF]
111 private List<TableData> protocolAttribs
112 private Map<String, List<TableData>> mapCatLabData = [:]
113 private Map<String, List<String>> mapCatNotExistCondMonMeas = [:]
114
115 private Map<String, Map<String, String>> mapCode = [:]
116 private List<CmoResult> cmoResults = []
117 private StringBuilder reportString = new StringBuilder()
118
119 private int totalProcessedRecord
120 private boolean exceptionExists
121 private StringBuilder exceptionMessages = new StringBuilder()
122
123 private Calendar measureTime = Calendar.getInstance()
124
125 private String batchNo
126 private static final String BATCHNO_KEY_SUFFIX = '_BATCHNO'
127 private static final String SEQ_PREFIX = 'C'
128
129 /*----- Fields from New Framework -----*/
130 private Calendar startDateTime
131 private String fileTimeStamp
132 private MSF080Rec msf080Rec
133 private MSF081Rec msf081Rec
134 private String progName
135 private String progReportId
136 private String progTitle
137 private String districtCode
138 private boolean alert
139 private File interfaceBaseDir
140 private File progDir
141 private File inputDir
142 private File rejectDir
143 private File archiveDir
144 private File controlReportDir
145
146 /*----- Fields to keep compatibility to OCTA Framework -----*/
147 private static final String CUSTOMREPORTFOLDER = '/controlreport/'
148 private NamBaseInterface namBaseInterface
149 private NamBatchCommands namBatchCommands
150 private DataSource dataSource
151 private int readParametersReturn
152 private schedReturnMessages = new String[2]
153 private boolean bFileOpenReport = false
154 private BatchTextReports textReport
155
156 public ProcessOcbcmo(Binding binding) {
157 this.binding = binding
158 info("debug: ProcessOcbcmo version: ${version}")
159 }
160
161 public void runBatch() {
162 info('debug: ProcessOcbcmo.runBatch starts')
163
164 try {
165 initialise(binding)
166 readParametersReturn = namBaseInterface.readParameters(true)
167 namBatchCommands.debugEnabled = namBaseInterface.debugEnabled
168 interfaceBaseDir = new File(namBaseInterface.ellipseInterfaceParentDir.toLowerCase())
169 progDir = new File(interfaceBaseDir, progName.toLowerCase())
170 inputDir = new File(progDir, 'input')
171 rejectDir = new File(progDir, 'reject')
172 archiveDir = new File(progDir, 'archive')
173 controlReportDir = new File(progDir, 'controlreport')
174
175 if (0 == readParametersReturn) {
176 batchParams = params.fill(new ParamOcbcmo())
177 MSF012Rec batchRec = findMsf012(progName + BATCHNO_KEY_SUFFIX)
178 batchNo = batchRec ? batchRec.dataArea?.trim() : SEQ_PREFIX + NamBase36Util.encode(0, 15)
179
180 //clean up the local input directory >> archive with NP on file name (before get files from FTP)
181 archiveNotProcessedFiles()
182
183 // move all FTP files over into ellipse local input directory
184 getFilesFromFTP()
185
186 //Pre Config Validation
187 preConfigValidation()
188 processAllFiles()
189 if (totalProcessedRecord) {
190 if (batchParams.submitMsb340?.toBoolean()) {
191 batchNo = nextSequence(batchNo)
192 createBatchRequest(MSB340_BATCH, "${progName}_${batchNo}", Calendar.getInstance(), NamConstant.EMPTY_STRING, NamConstant.EMPTY_STRING, NamConstant.EMPTY_STRING, batchParams.originator)
193 //Save lastBatchNo
194 updateMsf012(progName + BATCHNO_KEY_SUFFIX, batchNo)
195 Restart.transactionUtil.commitAndContinue()
196
197 String msb340Uuid = findBatchUuid(MSB340_BATCH, "${progName}_${batchNo}")
198 if (!msb340Uuid) {
199 logFunctionalException(createTimeoutMessage(MSB340_BATCH, "${progName}_${batchNo}"))
200 alert = true
201 }
202
203 //Add MSB340 control report
204 addOutputToEllipse(findBatchReportFile(MSB340_BATCH, msb340Uuid).absolutePath, 'text/plain')
205 }
206 }
207
208 // Record end time and processing time
209 namBaseInterface.setLastEndDateTime()
210 writeControlReport()
211 reportString << "\n Total batch processing time: ${namBaseInterface.processingTimeInMinutes}\n\n"
212
213 // Rescheduling
214 createNextRequest()
215 writeToReport(reportString.toString())
216
217 // Close report and output files
218 interfaceProcessHouseKeeping(reportString.toString())
219 updateLastRunDate()
220 } else {
221 throw new NamInfrastructureException(namBaseInterface.parameterReadReturnMessages[1]?.toString())
222 }
223 } catch (Throwable th) {
224 error("debug: runBatch exception ${th.toString()}", th)
225 reportString << '\n'
226 reportString << ' *** CRITICAL INFRASTRUCTURE ALERT *** :\n'
227 reportString << "${th.toString()}\n"
228 namBaseInterface.createAndEmailCustomReport(progName, reportString.toString(), false, CUSTOMREPORTFOLDER, progDir.absolutePath, progName + ' ** ALERT CRITICAL INFRASTRUCTURE ERROR **', NamConstant.EMPTY_STRING)
229 throw th
230 }
231 }
232
233 /**
234 * This function performs validation for ellipse configuration specific for
235 * this interface
236 * <p>
237 * <ul>
238 * <li>Michael Kempf - Initial</li>
239 * <li>2018-10-08 - Adhitya - Add Throw NamInfrastructureException for invalid data</li>
240 * </ul>
241 *
242 * @author Michael Kempf
243 * @author Adhitya Yudha
244 * @since December 12th, 2017
245 */
246 private void preConfigValidation() {
247 info('debug: ProcessOcbcmo.preConfigValidation starts ')
248
249 // Validate MSF010 +SUB Table Type exists in MSF010 +LAB
250 List<TableData> notExistMeasurementType = findNotExistMeasurementType()
251 if (notExistMeasurementType) {
252 String message = "Measurement type ${notExistMeasurementType.tableCode} does not exist in MSF010 +LAB Table Type"
253 error(message)
254 throw new NamInfrastructureException(message)
255 }
256
257 //validate Protocol Web Service
258 protocolAttribs = findProtocolAttribs() //Find all protocol
259 List<String> attribs = protocolAttribs.collect({ it.tableDesc }) //Get attributes from TABLE_DESC column
260 List<String> attribsNotExist = requiredAttribs.findAll({ !(it in attribs) }) //Find required attributes that not found in MSF010 +LAB
261 if (attribsNotExist) {
262 String message = "Protocol Web service attribute(s) ${attribsNotExist} does not exist in the +LAB Table Type"
263 error(message)
264 throw new NamInfrastructureException(message)
265 }
266 }
267
268 /**
269 * This method check if table code exist from MSF010
270 * <p>
271 * <ul>
272 * <li>Michael Kempf - Initial</li>
273 * </ul>
274 *
275 * @author Michael Kempf
276 * @since November 9th, 2017
277 * @return
278 */
279 private List<TableData> findNotExistMeasurementType() {
280 info('debug: ProcessOcbcmo.getMeasurementTypeData starts')
281
282 String sqlString = """
283 SELECT
284 TABLE_DESC,
285 TABLE_CODE,
286 ASSOC_REC
287 FROM MSF010
288 WHERE
289 TABLE_TYPE = '+SUB'
290 AND TABLE_CODE NOT IN (SELECT ASSOC_REC FROM MSF010 WHERE TABLE_TYPE = '+LAB' AND ACTIVE_FLAG = 'Y')
291 AND ACTIVE_FLAG = 'Y'
292 """
293
294 log("SQL: ${sqlString}")
295 List<TableData> result = []
296 Sql sql = getSqlConnection()
297 sql.eachRow(sqlString) { row ->
298 TableData measurementType = new TableData()
299 measurementType.assocRec = row.ASSOC_REC?.trim()
300 measurementType.tableDesc = row.TABLE_DESC?.trim()
301 measurementType.tableCode = row.TABLE_CODE?.trim()
302 result.add(measurementType)
303 }
304
305 return result
306 }
307
308 /**
309 * Find MSF010 +LAB records that not exist in MSF341 COND_MON_MEAS
310 *
311 * @author Chaerun Muhaimin
312 * @since May 27th, 2019
313 * @return
314 */
315 private List<String> findNotExistCondMonMeas(String category) {
316 info('debug: ProcessOcbcmo.findNotExistCondMonMeas starts')
317
318 String sqlString = """
319 SELECT
320 ASSOC_REC
321 FROM MSF010 A
322 JOIN (
323 SELECT
324 SUBSTR(TABLE_CODE, 5, 1) AS CAT
325 FROM MSF010
326 WHERE TABLE_TYPE = '+LAB'
327 AND ACTIVE_FLAG = 'Y'
328 AND UPPER(TABLE_DESC) = UPPER('FILE:${category}')
329 ) B ON A.TABLE_CODE like (B.CAT || ':%')
330 WHERE A.TABLE_TYPE = '+LAB'
331 AND A.ASSOC_REC NOT IN (SELECT DISTINCT COND_MON_MEAS FROM MSF341)
332 AND A.ACTIVE_FLAG = 'Y'
333 """
334
335 log("SQL: ${sqlString}")
336 List<String> result = []
337 Sql sql = getSqlConnection()
338 sql.eachRow(sqlString) { row ->
339 result.add(row.ASSOC_REC?.trim())
340 }
341
342 return result
343 }
344
345 /**
346 * This function will get Protocol Web Service Attribute from MSF010 +LAB.<br>
347 * All records from MSF010 +LAB with <b>TABLE_DESC</b> start with <b>'set'</b> are
348 * Protocol Web Service Attributes.
349 * <p>
350 * <ul>
351 * <li>Michael Kempf - Initial</li>
352 * </ul>
353 *
354 * @author Michael Kempf
355 * @since November 9th, 2017
356 * @return
357 */
358 private List<TableData> findProtocolAttribs() {
359 info('debug: ProcessOcbcmo.findProtocolAttribs starts')
360
361 String sqlString = """
362 SELECT
363 TABLE_DESC,
364 TABLE_CODE,
365 ASSOC_REC
366 FROM MSF010
367 WHERE
368 TABLE_TYPE = '+LAB'
369 AND ACTIVE_FLAG = 'Y'
370 AND SUBSTR(TABLE_DESC, 1, 3) = 'set'
371 """
372
373 log("SQL: ${sqlString}")
374 List<TableData> result = []
375 Sql sql = getSqlConnection()
376 sql.eachRow(sqlString) { row ->
377 TableData protocol = new TableData()
378 protocol.assocRec = row.ASSOC_REC?.trim()
379 protocol.tableDesc = row.TABLE_DESC?.trim()
380 protocol.tableCode = row.TABLE_CODE?.trim()
381 result.add(protocol)
382 }
383
384 return result
385 }
386
387 /**
388 * This method check if table code exist from MSF010
389 * <p>
390 * <ul>
391 * <li>Michael Kempf - Initial</li>
392 * <li>2018-10-12 - Adhitya Yudha - Add filter by category</li>
393 * </ul>
394 *
395 * @author Michael Kempf
396 * @since November 9th, 2017
397 * @return
398 */
399
400 private List<TableData> findLabDataWithCat(String category) {
401 info('debug: ProcessOcbcmo.findLabDataWithCat starts')
402
403 String sqlString = """
404 SELECT
405 A.TABLE_DESC,
406 SUBSTR(A.TABLE_CODE, INSTR(A.TABLE_CODE, ':') + 1) TABLE_CODE,
407 A.ASSOC_REC
408 FROM MSF010 A
409 JOIN (
410 SELECT
411 SUBSTR(TABLE_CODE, 5, 1) AS CAT
412 FROM MSF010
413 WHERE TABLE_TYPE = '+LAB'
414 AND ACTIVE_FLAG = 'Y'
415 AND UPPER(TABLE_DESC) = UPPER('FILE:${category}')
416 ) B ON A.TABLE_CODE like (B.CAT || ':%')
417 WHERE A.TABLE_TYPE = '+LAB'
418 AND A.ACTIVE_FLAG = 'Y'
419 """
420
421 log("SQL: ${sqlString}")
422 List<TableData> result = []
423 Sql sql = getSqlConnection()
424 sql.eachRow(sqlString) { row ->
425 TableData labData = new TableData()
426 labData.assocRec = row.ASSOC_REC?.trim()
427 labData.tableDesc = row.TABLE_DESC?.trim()
428 labData.tableCode = row.TABLE_CODE?.trim()
429 result.add(labData)
430 }
431
432 return result
433 }
434
435 /**
436 * This method load all the files and processes them
437 * <p>
438 * <ul>
439 * <li>Michael Kempf - Initial</li>
440 * <li>2018-10-08 - Adhitya Yudha - Move reject file creation to readFileAndPerformTasks.</li>
441 * </ul>
442 *
443 * @author Michael Kempf
444 * @author Adhitya Yudha
445 * @since September 12th, 2016
446 * @param folder
447 */
448 private void processAllFiles() {
449 info('debug: ProcessOcbcmo.processAllFiles starts')
450
451 int fileNumber = 0
452 File[] files = inputDir.listFiles()
453 if (files) {
454 files.each{
455 if (it.isFile()) {
456 String extension = FilenameUtils.getExtension(it.name)?.toLowerCase()
457 if ('csv' == extension) {
458 fileNumber++
459 measureTime.add(Calendar.SECOND, 1)
460 CmoResult result = readFileAndPerformTasks(it, fileNumber.toString().padLeft(2,'0'))
461 cmoResults.add(result)
462 archiveFile(it)
463 }
464 }
465 }
466 }
467 }
468
469 /**
470 * This method reads all file and process
471 * <p>
472 * <ul>
473 * <li>Michael Kempf - Initial</li>
474 * <li>2018-10-08 - Adhitya Yudha - Add reject file creation only for processed data</li>
475 * </ul>
476 *
477 * @author Michael Kempf
478 * @author Adhitya Yudha
479 * @since September 20th, 2016
480 * @param inputFile
481 */
482 private CmoResult readFileAndPerformTasks(File inputFile, String strFileNumber) {
483 info("debug: ProcessOcbcmo.readFileAndPerformTasks ${inputFile.name} starts")
484
485 File rejectFile = namBaseInterface.createRejectFile(inputFile, rejectDir, NamConstant.EMPTY_STRING)
486 CmoResult cmoResult = new CmoResult()
487 cmoResult.fileNumber = strFileNumber
488 cmoResult.inputFileName = inputFile.name
489 cmoResult.rejectFileName = rejectFile.name
490
491 // Validate file name format: octcmo_<labCategory>_20190527.csv
492 if (StringUtils.ordinalIndexOf(inputFile.name, '_', 2) == -1) {
493 cmoResult.categoryErrorMessages = "File category is missing in ${inputFile.name}"
494 return cmoResult
495 }
496
497 String labCategory = inputFile.name.substring(StringUtils.ordinalIndexOf(inputFile.name, '_', 1) + 1, StringUtils.ordinalIndexOf(inputFile.name, '_', 2))
498
499 // Validate category MeasurementType list
500 if (mapCatLabData[labCategory] == null) { // Load catLabData only if it hasn't been loaded, don't use !mapCatLabData[labCategory]
501 mapCatLabData[labCategory] = findLabDataWithCat(labCategory) // Find +LAB based on file category
502 }
503 if (!mapCatLabData[labCategory]) {
504 // Validate MSF010 +LAB, if MeasurementType list is empty, then the category does not exist.
505 cmoResult.categoryErrorMessages = "Measurement category ${labCategory} does not exist in MSF010 +LAB Table Type"
506 return cmoResult
507 }
508
509 // Validate category MeasurementType if exist in MSF341 COND_MON_MEAS
510 if (mapCatNotExistCondMonMeas[labCategory] == null) { // Load CatNotExistCondMonMeas only if it hasn't been loaded, don't use !mapCatNotExistCondMonMeas[labCategory]
511 mapCatNotExistCondMonMeas[labCategory] = findNotExistCondMonMeas(labCategory)
512 }
513 List<String> notExistCondMonMeas = mapCatNotExistCondMonMeas[labCategory]
514 if (notExistCondMonMeas) {
515 // If notExistCondMonMeas is not empty, then stop processing the file.
516 cmoResult.categoryErrorMessages = "Measurement type ${notExistCondMonMeas} does not exist in MSF341"
517 return cmoResult
518 }
519
520 // All above validations passed
521 try {
522 CmoFile cmo = readCsvFile(inputFile, mapCatLabData[labCategory])
523 cmoResult.readCount = cmo.dataRows.size()
524
525 [cmo.dataPairs, cmo.dataRows].transpose().each { Map<String, String> dataPair, String dataRow ->
526 try {
527 validate(dataPair)
528 callCondMeasurementMultipleCreate(createCondMeasurementDTOs(dataPair, mapCatLabData[labCategory]))
529 cmoResult.processedCount++
530 totalProcessedRecord++
531 Restart.transactionUtil.commitAndContinue()
532 } catch (NamFunctionalException e) {
533 cmoResult.rejectCount++
534 namBaseInterface.writeToFile(rejectFile, "${dataRow} ${e.message}\n")
535 }
536 }
537 } catch (NamInfrastructureException e) { //catch NamInfrastructureException to continue process other files
538 //thrown from validateHeader
539 error(e.toString())
540 cmoResult.headerErrorMessages = e.message
541 }
542
543 return cmoResult
544 }
545
546 /**
547 * Read the CSV File using OpenCSV Library
548 *
549 * @author Chaerun Muhaimin
550 * @since June 17th, 2019
551 * @param file
552 * @param labData
553 * @return
554 */
555 private CmoFile readCsvFile(File file, List<TableData> labData) {
556 info("debug: ProcessOcbcmo.readCsvFile starts")
557
558 CmoFile cmo = new CmoFile()
559 char separator = CSVParser.DEFAULT_SEPARATOR
560
561 CSVParser parser = new CSVParserBuilder()
562 .withSeparator(separator)
563 .withIgnoreQuotations(true)
564 .build()
565
566 //Read file using withReader method, to ensuring the reader is closed after the closure returns.
567 file.withReader{ reader ->
568 CSVReader csvReader = new CSVReaderBuilder(reader)
569 .withSkipLines(0)
570 .withCSVParser(parser)
571 .build()
572
573 //First line was Header
574 String[] headers = csvReader.readNext()*.toUpperCase() //Upper case the headers using spread operator
575 validateHeader(headers, labData)
576
577 //Read the rest of the lines
578 csvReader.each{ values ->
579 Map<String, String> dataPair = [:]
580
581 values = values*.trim() //trim all values
582 if (values.join()) { // Only process a line that has at least one value
583 [headers, values].transpose().each{ String header, String value ->
584 //Blank, null or zero measurement value is not to be loaded. No error will be generated.
585 dataPair[header] = value && value != '0' ? value : NamConstant.EMPTY_STRING
586 }
587
588 cmo.dataRows << values.join(separator.toString())
589 cmo.dataPairs << dataPair
590 log("Valid line: ${dataPair}")
591 }
592 }
593
594 info("Total records read (including header and empty line): ${csvReader.recordsRead}")
595 }
596
597 return cmo
598 }
599
600 /**
601 * This function validates header
602 * <p>
603 * <ul>
604 * <li>Michael Kempf - Initial</li>
605 * </ul>
606 *
607 * @author Michael Kempf
608 * @since September 11st, 2017
609 * @param header
610 * @return
611 */
612 private void validateHeader(String[] header, List<TableData> labData) {
613 info('debug: ProcessOcbcmo.validateHeader starts')
614
615 List<String> headerNotExist = []
616 labData.each{ lab ->
617 String column = header.find{ it == lab.tableCode }
618 if (!column) {
619 headerNotExist.add(lab.tableCode)
620 }
621 }
622
623 if (headerNotExist) {
624 throw new NamInfrastructureException("Columns ${headerNotExist} are missing")
625 }
626 }
627
628 /**
629 * This function validates the current object and returns status and error
630 * <p>
631 * <ul>
632 * <li>Michael Kempf - Initial</li>
633 * </ul>
634 *
635 * @author Michael Kempf
636 * @since September 11st, 2017
637 * @param dataPair
638 * @return
639 */
640 private void validate(Map<String, String> dataPair) {
641 info('debug: ProcessOcbcmo.validate starts ')
642
643 String unit = dataPair[protocolAttribs.find({ it.tableDesc == EQUIPMENT_REF }).tableCode]
644 EquipmentServiceReadReplyDTO equipment = findEquipment(unit)
645 if (!equipment) {
646 throw new NamFunctionalException("Equipment No. ${unit} does not exist")
647 }
648 }
649
650 /**
651 * Find equipment using Service
652 *
653 * @author Chaerun Muhaimin
654 * @since June 13th, 2019
655 * @param equipmentRef
656 * @return
657 */
658 private EquipmentServiceReadReplyDTO findEquipment(String equipmentRef) {
659 info("debug: ProcessOcbcmo.findEquipment [${equipmentRef}] starts")
660
661 try {
662 EquipmentServiceReadReplyDTO result = service.get('Equipment').read({EquipmentServiceReadRequestDTO req ->
663 req.equipmentRef = equipmentRef
664 })
665
666 return result
667 } catch (EnterpriseServiceOperationException e) {
668 error(e.toString())
669 return null
670 }
671 }
672
673 /**
674 * This function has the logic for creating the CondMeasurementServiceCreateRequestDTO
675 * <p>
676 * <ul>
677 * <li>Michael Kempf - Initial</li>
678 * <li>2018-10-08 - Adhitya - Add logic for create multiple CondMeasurementServiceCreateRequestDTOs</li>
679 * </ul>
680 *
681 * @author Michael Kempf
682 * @author Adhitya Yudha
683 * @since September 13th, 2017
684 * @param List<Map>row
685 * @return
686 */
687 private List<CondMeasurementServiceCreateRequestDTO> createCondMeasurementDTOs(Map<String, String> dataPair, List<TableData> labData) {
688 info('debug: ProcessOcbcmo.createCondMeasurementDTOs starts')
689 List<CondMeasurementServiceCreateRequestDTO> dtos = []
690
691 String strMeasureDate = dataPair[protocolAttribs.find({ it.tableDesc == MEASURE_DATE }).tableCode]
692 Calendar measureDate = NamDateUtil.toCalendar(strMeasureDate, NamConstant.US_DATE_FORMAT)
693
694 String equipmentRef = dataPair[protocolAttribs.find({ it.tableDesc == EQUIPMENT_REF }).tableCode]
695
696 String strCompCode = dataPair[protocolAttribs.find({ it.tableDesc == COMP_CODE }).tableCode]
697 String condMonType = NamConstant.EMPTY_STRING
698 String compCode = NamConstant.EMPTY_STRING
699 MSF010Rec msf010Rec = findMsf010Data('+CMO', strCompCode)
700 if (msf010Rec) {
701 condMonType = msf010Rec.assocRec.substring(0, 2)
702 compCode = msf010Rec.assocRec
703 }
704
705 dataPair.each{ k, v ->
706 TableData lab = labData.find{ it.tableCode == k }
707 if (lab) {
708 //Do not process LAB if is protocolWebServiceData
709 if (!protocolAttribs.find{ it.tableDesc == lab.tableDesc }) {
710 CondMeasurementServiceCreateRequestDTO dto = new CondMeasurementServiceCreateRequestDTO()
711 dto.measureDate = measureDate
712 dto.equipmentRef = equipmentRef
713 dto.condMonType = condMonType
714 dto.compCode = compCode
715
716 String strMeasureValue = v
717 if (lab.tableCode == 'LAB') {
718 strMeasureValue = v.trim().substring(4)
719 } else {
720 String crossRefMeasureValue = findCrossReferenceValue(lab.assocRec.toUpperCase(), v)
721 if (crossRefMeasureValue) {
722 strMeasureValue = crossRefMeasureValue
723 }
724 }
725
726 if (strMeasureValue && !strMeasureValue.isBigDecimal()) {
727 String message = "Measurement value ${lab.tableCode} : ${strMeasureValue} must be numeric."
728 error(message)
729 throw new NamFunctionalException(message)
730 }
731
732 if (strMeasureValue.isBigDecimal()) {
733 dto.measureValue = strMeasureValue.toBigDecimal()
734 dto.setCondMonMeas(lab.assocRec)
735 dto.setMeasureTime(measureTime)
736 dtos.add(dto)
737 }
738 }
739 }
740 }
741
742 return dtos
743 }
744
745 private MSF010Rec findMsf010Data(String tableType, String tableCode) {
746 Constraint c1 = MSF010Key.tableType.equalTo(tableType)
747 Constraint c2 = MSF010Key.tableCode.equalTo(tableCode)
748 Query query = new QueryImpl(MSF010Rec.class).and(c1).and(c2)
749 return edoi.firstRow(query)
750 }
751
752 /**
753 * This function will lookup to see if there is a cross reference to use in MSF010 +SUB
754 * <p>
755 * <ul>
756 * <li>Michael Kempf - Initial</li>
757 * <li>2018-10-08 - Adhitya - Change logic to get reference value based on new requirement</li>
758 * </ul>
759 *
760 * @author Michael Kempf
761 * @author Adhitya Yudha
762 * @since March 1st, 2018
763 * @param tableCode, value
764 * @return crossReferenceValue
765 */
766 private String findCrossReferenceValue(String tableCode, String value) {
767 Map<String, String> tableCodeMap = mapCode[tableCode]
768 if (!tableCodeMap) {
769 tableCodeMap = [:]
770 MSF010Rec msf010SUBData = findMsf010Data('+SUB', tableCode)
771 if (msf010SUBData) {
772 log("debug: msf010SUBData.assocRec : [${msf010SUBData.assocRec}]")
773 msf010SUBData.assocRec.trim().replace('^', '#').split('#').each{
774 String[] pair = it.split(':')
775 if (pair.size() == 2) {
776 tableCodeMap[pair[0]] = pair[1]
777 }
778 }
779 mapCode[tableCode] = tableCodeMap
780 }
781 }
782
783 return tableCodeMap[value]
784 }
785
786 /**
787 * This Webservice call uses a list of CondMeasurementMultipleCreate to create multiple
788 * CondMeasurementServiceCreateRequestDTO Items for each row data
789 * <p>
790 * <ul>
791 * <li>Adhitya Yudha - Initial</li>
792 * </ul>
793 *
794 * @author Adhitya Yudha
795 * @since October 8th, 2018
796 * @param cmoDTOList
797 * @return
798 */
799 private void callCondMeasurementMultipleCreate(List<CondMeasurementServiceCreateRequestDTO> dtoList) {
800 info('debug: ProcessOcbcmo.callCondMeasurementMultipleCreate starts')
801
802 try {
803 service.get('CondMeasurement').multipleCreate(dtoList as CondMeasurementServiceCreateRequestDTO[], false)
804 } catch(EnterpriseServiceOperationException e) {
805 throw new NamFunctionalException(e.errorMessages.collect{ it.message?.trim() }.join(';'))
806 }
807 }
808
809 private void createBatchRequest(String progName, String requestedBy, Calendar deferCal, String... params) {
810 info('debug: ProcessOcbwoc.createBatchRequest starts')
811
812 service.get('BatchRequest').create({ BatchRequestServiceCreateRequestDTO req ->
813 req.programName = progName
814 req.batchSubmission = true
815 req.deferDate = deferCal
816 req.deferTime = deferCal
817 req.requestedBy = requestedBy
818 req.districtCode = msf080Rec.dstrctCode
819 req.medium = msf080Rec.medium
820
821 List<String> requestParams = params.toList()
822 if (requestParams) {
823 List<MSF081Rec> paramList = findRequestParameters(progName)
824 int idx = 0
825 paramList.each{
826 int startRow = it.startRow.toInteger()
827 String reqParam = requestParams[idx] ?: NamConstant.EMPTY_STRING
828 req."setRequestParameter${startRow.toString()}"(reqParam)
829 log("debug: requestParameter${startRow.toString()} : [${reqParam}]")
830 idx++
831 }
832 }
833 })
834
835 createMailRecipients(progName, deferCal)
836 }
837
838 private List<MSF081Rec> findRequestParameters(String progName) {
839 info('debug: ProcessOcbwoc.findRequestParameters starts')
840
841 Constraint c1 = MSF081Key.progName.equalTo(progName)
842 Query query = new QueryImpl(MSF081Rec.class).and(c1)
843
844 List<MSF081Rec> mapList = edoi.search(query).getResults().findAll({ MSF081Rec msf081Rec ->
845 msf081Rec.fieldSize.toInteger() > 0
846 })
847
848 return mapList
849 }
850
851 private void createMailRecipients(String progName, Calendar deferCal) {
852 info('debug: ProcessOcbcmo.createMailRecipients starts ')
853 if (msf080Rec.medium == 'M') {
854 List<MSF08BRec> listMsf08b = fetchRequestDestination()
855 if (listMsf08b) {
856 MSF08BKey msf08bKey = new MSF08BKey()
857 MSF08BRec msf08bRec = new MSF08BRec()
858
859 // Re-read new batch request to obtain new UUID to be passed to MSF08B
860 Constraint c1 = MSF080Key.progName.equalTo(progName)
861 Constraint c2 = MSF080Key.deferDate.equalTo(deferCal?.format(NamConstant.DATE_FORMAT))
862 Constraint c3 = MSF080Key.deferTime.equalTo(deferCal?.format(NamConstant.TIME_FORMAT))
863 Query query = new QueryImpl(MSF080Rec.class).and(c1).and(c2).and(c3)
864 MSF080Rec newMsf080Rec = edoi.firstRow(query)
865 if (newMsf080Rec) {
866 listMsf08b.each{ it ->
867 msf08bKey.rqstUuid = newMsf080Rec.uuid
868 msf08bKey.seqNo = it.primaryKey.seqNo
869 msf08bRec.primaryKey = msf08bKey
870 msf08bRec.rptDestType = it.rptDestType
871 msf08bRec.rptDestCode = it.rptDestCode
872 msf08bRec.rptDestination = it.rptDestination
873 msf08bRec.lastModDate = deferCal?.format(NamConstant.DATE_FORMAT)
874 msf08bRec.lastModTime = deferCal?.format(NamConstant.TIME_FORMAT)
875 msf08bRec.lastModUser = it.lastModUser
876 msf08bRec.lastModEmp = it.lastModEmp
877 edoi.create(msf08bRec)
878 }
879 }
880 }
881 }
882 info('debug: ProcessOcbcmo.createMailRecipients ends ')
883 }
884
885 private List<MSF08BRec> fetchRequestDestination() {
886 info("debug: ProcessOcbcmo.fetchRequestDestination ${uuid} starts")
887
888 List<MSF08BRec> result = []
889 Constraint c1 = MSF08BKey.rqstUuid.equalTo(uuid)
890 Query query = new QueryImpl(MSF08BRec.class).and(c1)
891 edoi.search(query, { MSF08BRec msf08bRec ->
892 result.add(msf08bRec)
893 })
894
895 return result
896 }
897
898 private MSF012Rec findMsf012(String keyValue) {
899 Constraint c1 = MSF012Key.dataType.equalTo('C')
900 Constraint c2 = MSF012Key.keyValue.equalTo(keyValue)
901 Query query = new QueryImpl(MSF012Rec.class).and(c1).and(c2)
902 return edoi.firstRow(query)
903 }
904
905 private void updateMsf012(String keyValue, String dataArea) {
906 MSF012Key key = new MSF012Key('C', keyValue)
907 MSF012Rec rec = new MSF012Rec(key)
908 if (edoi.exists(key)) {
909 rec = (MSF012Rec) edoi.get(key)
910 rec.dataArea = dataArea
911 edoi.update(rec)
912 } else {
913 rec.primaryKey = key
914 rec.dataArea = dataArea
915 edoi.create(rec)
916 }
917 }
918
919 private String nextSequence(String lastValue) {
920 return SEQ_PREFIX + NamBase36Util.next(lastValue.substring(1))
921 }
922
923 /*----- Method from New Framework -----*/
924
925 private void initVariables() {
926 info('debug: ProcessOcbcmo.initVariables starts')
927
928 msf080Rec = getSuperBatchDetails()
929 if (msf080Rec) {
930 // Get batch details
931 Constraint c1 = MSF081Key.progName.equalTo(msf080Rec.primaryKey.progName)
932 Query query = new QueryImpl(MSF081Rec.class).and(c1)
933 msf081Rec = edoi.firstRow(query)
934
935 progName = msf081Rec.primaryKey.progName?.toUpperCase()
936 progReportId = msf081Rec.primaryKey.progReportId
937 progTitle = [msf081Rec.descLine_1?.trim(), msf081Rec.descLine_2?.trim()].findAll({it}).join(' ')
938 }
939 }
940
941 private Sql getSqlConnection() {
942 info('debug: ProcessOcbcmo.getSqlConnection starts')
943 return new Sql(dataSource)
944 }
945
946 /**
947 * Archiving an input file.
948 * @param inputFile
949 * @return
950 */
951 private File archiveFile(File inputFile) {
952 String baseName = FilenameUtils.getBaseName(inputFile.name)
953 String extension = FilenameUtils.getExtension(inputFile.name)
954 File archiveFile = namBaseInterface.createFile(archiveDir, baseName, extension)
955
956 Files.copy(inputFile.toPath(), archiveFile.toPath())
957 info("debug: inputFile: [${inputFile.name}] archiveFile: [${archiveFile.name}]")
958
959 return archiveFile
960 }
961
962 /*----- Method to keep compatibility to OCTA Framework -----*/
963
964 private void initialise(Binding binding) {
965 info('debug: ProcessOcbcmo.initialise starts')
966
967 init(binding)
968 initVariables()
969 dataSource = binding.getVariable('dataSource')
970
971 printSuperBatchVersion()
972 namBaseInterface = new NamBaseInterface(progName, binding)
973 namBatchCommands = new NamBatchCommands(binding, namBaseInterface.debugEnabled, progName)
974 districtCode = request.getDistrict()
975 openReportFile()
976
977 startDateTime = Calendar.getInstance()
978 fileTimeStamp = startDateTime.format(NamConstant.FILE_DATE_FORMAT)
979 }
980
981 private void createNextRequest() {
982 info('debug: ProcessOcbcmo.createNextRequest starts')
983
984 schedReturnMessages = namBaseInterface.reschedule()
985 reportString << " Batch scheduling messages: ${schedReturnMessages[1]}\n"
986 }
987
988 private void interfaceProcessHouseKeeping(String controlReportString) {
989 info('debug ProcessOcbcmo.interfaceProcessHouseKeeping starts')
990
991 String subjectStr = namBaseInterface.msf081Rec.getDescLine_1().trim()
992 bFileOpenReport = namBaseInterface.closeReportFile(textReport, bFileOpenReport)
993
994 if (alert) {
995 subjectStr = "** ALERT - ${subjectStr}"
996 }
997 namBaseInterface.createAndEmailCustomReport(progName, controlReportString, false, CUSTOMREPORTFOLDER, progDir.absolutePath, subjectStr, NamConstant.EMPTY_STRING)
998 moveControlReportAndFilesToRemoteServer()
999 }
1000
1001 /**
1002 * This method moves control report, all reject and archive files and
1003 * remove the input files on remote server
1004 *
1005 * @author Michael Kempf
1006 * @since September 15th, 2016
1007 */
1008 private void moveControlReportAndFilesToRemoteServer() {
1009 info('debug: ProcessOcbcmo.moveControlReportAndFilesToRemoteServer starts')
1010
1011 if (namBaseInterface.sMTPEnabled.toUpperCase().equals('Y')) {
1012 File controlReportFile = new File(controlReportDir, namBaseInterface.controlReportFilename)
1013 if (controlReportFile.exists()) {
1014 StringBuilder commands = new StringBuilder()
1015 commands << 'config_filename:groovy_sftp_config\n'
1016 commands << 'sftp:connect --host ${HOST} --port ${PORT} --user ${USERNAME}\n'
1017 commands << 'sftp:cd ${3}/maintenance/ocbcmo/report\n'
1018 commands << "sftp:lcd ${controlReportDir.absolutePath}\n"
1019 commands << "sftp:put ${controlReportFile.name}\n"
1020 commands << 'sftp:close'
1021 namBatchCommands.runFTPCommandsDirect(binding, commands.toString(), namBaseInterface.ftpInterfaceParentDir)
1022
1023 //Delete controlReport file on localhost
1024 controlReportFile.delete()
1025 }
1026 }
1027
1028 File[] inputFiles = inputDir.listFiles()
1029 if (inputFiles) {
1030 StringBuilder commands = new StringBuilder()
1031 commands << 'config_filename:groovy_sftp_config\n'
1032 commands << 'sftp:connect --host ${HOST} --port ${PORT} --user ${USERNAME}\n'
1033 commands << 'sftp:cd ${3}/maintenance/ocbcmo/input\n'
1034 inputFiles.each{
1035 commands << "sftp:rm ${it.name}\n"
1036 }
1037 commands << 'sftp:close'
1038 namBatchCommands.runFTPCommandsDirect(binding, commands.toString(), namBaseInterface.ftpInterfaceParentDir)
1039
1040 //Delete input files on localhost
1041 inputFiles.each{
1042 it.delete()
1043 }
1044 }
1045
1046 File[] archiveFiles = archiveDir.listFiles()
1047 if (archiveFiles) {
1048 StringBuilder commands = new StringBuilder()
1049 commands << 'config_filename:groovy_sftp_config\n'
1050 commands << 'sftp:connect --host ${HOST} --port ${PORT} --user ${USERNAME}\n'
1051 commands << 'sftp:cd ${3}/maintenance/ocbcmo/archive\n'
1052 commands << "sftp:lcd ${archiveDir.absolutePath}\n"
1053 archiveFiles.each{
1054 commands << "sftp:put ${it.name}\n"
1055 }
1056 commands << 'sftp:close'
1057 namBatchCommands.runFTPCommandsDirect(binding, commands.toString(), namBaseInterface.ftpInterfaceParentDir)
1058
1059 //Delete archive files on localhost
1060 archiveFiles.each{
1061 it.delete()
1062 }
1063 }
1064
1065 File[] rejectFiles = rejectDir.listFiles()
1066 if (rejectFiles) {
1067 StringBuilder commands = new StringBuilder()
1068 commands << 'config_filename:groovy_sftp_config\n'
1069 commands << 'sftp:connect --host ${HOST} --port ${PORT} --user ${USERNAME}\n'
1070 commands << 'sftp:cd ${3}/maintenance/ocbcmo/reject\n'
1071 commands << "sftp:lcd ${rejectDir.absolutePath}\n"
1072 rejectFiles.each{
1073 commands << "sftp:put ${it.name}\n"
1074 }
1075 commands << 'sftp:close'
1076 namBatchCommands.runFTPCommandsDirect(binding, commands.toString(), namBaseInterface.ftpInterfaceParentDir)
1077
1078 //Delete reject files on localhost
1079 rejectFiles.each{
1080 it.delete()
1081 }
1082 }
1083 }
1084
1085 /**
1086 * This method will move the leftover files in the local input directory to the
1087 * archive directory with a "NP" as part of the file name.
1088 * This is don because we cannot have duplicate invoice numbers
1089 * <p>
1090 * <ul>
1091 * <li>Michael Kempf - Initial</li>
1092 * <li>2018-08-27 - Chaerun Muhaimin - Handle NullPointerException, use FilenameUtils to get file extension.</li>
1093 * </ul>
1094 *
1095 * @author Michael Kempf
1096 * @author Chaerun Muhaimin
1097 * @since September 15th, 2016
1098 * @param folder
1099 * @param newDirectory
1100 */
1101 private void archiveNotProcessedFiles() {
1102 info('debug: ProcessOcbcmo.archiveNotProcessedFiles starts')
1103
1104 File[] files = inputDir.listFiles()
1105 if (files) {
1106 if (!archiveDir.exists()) {
1107 archiveDir.mkdirs()
1108 }
1109
1110 files.each{
1111 if (it.isFile()) {
1112 String baseName = FilenameUtils.getBaseName(it.name)
1113 String extension = FilenameUtils.getExtension(it.name)?.toLowerCase()
1114 it.renameTo(new File(archiveDir, "${baseName}_NP.${extension}"))
1115 }
1116 }
1117 }
1118 }
1119
1120 private void getFilesFromFTP() {
1121 info("debug: ProcessOcbcmo.getFilesFromFTP ${namBaseInterface.ftpInterfaceParentDir} ... ${progName.toLowerCase()}")
1122
1123 //move all the available files from FTP to the InterfaceParentDir/Interface/<InterfaceName>/input/ directory.
1124 //For each file moved to the input dir
1125 //Move the file form FTPinput to FTP archive.
1126 String ftpPath = namBaseInterface.ftpInterfaceParentDir // remove toLowerCase() function since UNIX is case sensitive
1127 String mseprfId = "acs.${progName.toLowerCase()}.ftp"
1128 namBatchCommands.runFTPCommands(binding, mseprfId, ftpPath)
1129 }
1130
1131 private void updateLastRunDate() {
1132 info('debug: ProcessOcbcmo.updateLastRunDate starts')
1133
1134 String lastStartDateTimeStr = namBaseInterface.lastStartDateTime.format(NamConstant.LAST_RUN_DATE_FORMAT)
1135 namBaseInterface.setLastRunDatetime(progName.toUpperCase(), lastStartDateTimeStr)
1136 }
1137
1138 private boolean openReportFile() {
1139 info('debug: ProcessOcbcmo.openReportFile starts')
1140 boolean bOk = true
1141 try {
1142 List <String> headingsA = new ArrayList <String>()
1143 headingsA.add('Summary Report'.center(132))
1144 headingsA.add(String.format('%132s', ' ').replace(' ', '-'))
1145 textReport = report.open(progName + progReportId, version, headingsA)
1146 bFileOpenReport = true
1147 } catch (IOException e) {
1148 info ('debug: ##### ERROR: Unable to create report #####')
1149 info ('debug: ##### PROCESS TERMINATED #####')
1150 bOk = false
1151 throw e
1152 }
1153 if (!bOk) {
1154 bFileOpenReport = namBaseInterface.closeReportFile(textReport, bFileOpenReport)
1155 }
1156 return bOk
1157 }
1158
1159 private void logFunctionalException(String message) {
1160 exceptionExists = true
1161 exceptionMessages << message
1162 exceptionMessages << '\n'
1163 }
1164
1165 private void writeToReport(String sMessage) {
1166 info('debug: ProcessOcbcmo.writeToReport starts')
1167 if (bFileOpenReport) {
1168 textReport.write(sMessage)
1169 }
1170 }
1171
1172 private void writeControlReport() {
1173 if (cmoResults) {
1174 cmoResults.each{
1175 reportString << '\n'
1176 reportString << "File ${it.fileNumber}: ${it.inputFileName}\n"
1177 reportString << " Number of Records Read : ${it.readCount}\n"
1178 reportString << " Number of Records Rejected : ${it.rejectCount}\n"
1179 reportString << " Number of Processed Records : ${it.processedCount}\n"
1180
1181 if (it.rejectCount) {
1182 alert = true
1183 reportString << " Rejection Filename : ${it.rejectFileName}\n"
1184 }
1185 if (it.headerErrorMessages) {
1186 alert = true
1187 reportString << " Column Headers Error : ${it.headerErrorMessages}\n"
1188 }
1189 if (it.categoryErrorMessages) {
1190 alert = true
1191 reportString << " File Definition Error : ${it.categoryErrorMessages}\n"
1192 }
1193
1194 if (it.rejectCount) {
1195 reportString << '\n'
1196 reportString << ' Please review rejection file for further details\n'
1197 }
1198
1199 if (exceptionExists) {
1200 reportString << '\n'
1201 reportString << exceptionMessages.toString()
1202 }
1203
1204 if (!batchParams.submitMsb340?.toBoolean()) {
1205 reportString << '\n'
1206 reportString << ' MSB340 was not submitted in this run\n'
1207 }
1208 }
1209 } else {
1210 reportString << '\n'
1211 reportString << " Input file(s) do not exist on ${FilenameUtils.separatorsToUnix(inputDir.absolutePath)}.\n"
1212 reportString << ' Nothing to process. Program terminated normally.'
1213 reportString << '\n'
1214 }
1215 }
1216
1217 private String findBatchUuid(String progName, String requestedBy) {
1218 info("debug: ProcessOcbwoc.findBatchUuid ${progName}|${requestedBy} starts")
1219
1220 int iteration = batchParams.iteration.toInteger()
1221 int timer = batchParams.timer.toInteger()
1222
1223 for (int i = 0; i < iteration; i++) {
1224 sleep(TimeUnit.SECONDS.toMillis(timer))
1225 MSF085Rec result = findBatchResult(progName, requestedBy)
1226 if (result) {
1227 return result.primaryKey.uuid
1228 }
1229 }
1230
1231 return null
1232 }
1233
1234 private MSF085Rec findBatchResult(String progName, String requestedBy) {
1235 Constraint c1 = MSF085Key.dstrctCode.equalTo(districtCode)
1236 Constraint c2 = MSF085Key.progName.equalTo(progName)
1237 Constraint c3 = MSF085Rec.requestBy.equalTo(requestedBy)
1238 Query query = new QueryImpl(MSF085Rec.class).and(c1).and(c2).and(c3)
1239 return edoi.firstRow(query)
1240 }
1241
1242 private String createTimeoutMessage(String progName, String requestedBy) {
1243 StringBuilder sb = new StringBuilder()
1244 sb << " ${progName} TIMEOUT ERROR:\n"
1245 sb << " ${progName} was not completed after ${batchParams.iteration.toInteger() * batchParams.timer.toInteger()} seconds.\n"
1246 sb << " Plase run MSE086 and search report with Requested By: ${requestedBy}"
1247 return sb.toString()
1248 }
1249
1250 private File findBatchReportFile(String progName, String uuid) {
1251 info('debug: ProcessOcbwoc.findBatchReportFile starts')
1252
1253 EllipseDBFileSystemImpl efs = EFSHelper.getEllipseFileSystem(EFSTypes.DB)
1254 List<EFSFile> efsFiles = efs.listGroup(uuid)
1255 EFSFile efsFile = efsFiles.find{
1256 it.file.startsWith(progName)
1257 }
1258
1259 return new File(efs.root + File.separator + efs.hash(efsFile.group), efsFile.file)
1260 }
1261
1262 private void log(String msg) {
1263 if (namBaseInterface?.debugEnabled != 'N') {
1264 Logger logObject = LoggerFactory.getLogger(getClass())
1265 logObject.info("------------- ${msg}")
1266 }
1267 }
1268
1269 private void error(String msg) {
1270 Logger logObject = LoggerFactory.getLogger(getClass())
1271 logObject.error("------------- ${msg}")
1272 }
1273
1274 private void error(String msg, Object arg) {
1275 Logger logger = LoggerFactory.getLogger(getClass())
1276 logger.error("------------- ${msg}", arg)
1277 }
1278
1279}
1280
1281new ProcessOcbcmo(binding).runBatch()