1. 新增多项yml配置文件
2. 新增导入静安监测数据功能模块(未完成)
| | |
| | | </plugins> |
| | | </build> |
| | | |
| | | <profiles> |
| | | <profile> |
| | | <id>dev</id> |
| | | <properties> |
| | | <profileActive>dev</profileActive> |
| | | </properties> |
| | | <activation> |
| | | <activeByDefault>true</activeByDefault> |
| | | </activation> |
| | | </profile> |
| | | <profile> |
| | | <id>pro</id> |
| | | <properties> |
| | | <profileActive>pro</profileActive> |
| | | </properties> |
| | | </profile> |
| | | <profile> |
| | | <id>test</id> |
| | | <properties> |
| | | <profileActive>test</profileActive> |
| | | </properties> |
| | | </profile> |
| | | </profiles> |
| | | |
| | | <!--ä¾èµä¸è½½å°å--> |
| | | <repositories> |
| | | <repository> |
¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.flightfeather.uav.common.exception |
| | | |
| | | /** |
| | | * å
许æ¥å£è¿åçä¸å¡å±é¢çé误 |
| | | */ |
| | | class ResponseErrorException : Exception { |
| | | constructor():super() |
| | | constructor(message: String) : super(message) |
| | | constructor(message: String, cause: Throwable) : super(message, cause) |
| | | constructor(cause: Throwable) : super(cause) |
| | | constructor(message: String, cause: Throwable, enableSuppression: Boolean, writableStackTrace: Boolean) |
| | | : super(message, cause, enableSuppression, writableStackTrace) |
| | | } |
| | |
| | | package com.flightfeather.uav.common.utils |
| | | |
| | | import org.apache.poi.hssf.usermodel.HSSFWorkbook |
| | | import com.flightfeather.uav.common.exception.ResponseErrorException |
| | | import org.apache.poi.ss.usermodel.Row |
| | | import org.apache.poi.ss.util.CellRangeAddress |
| | | import org.apache.poi.xssf.streaming.SXSSFWorkbook |
| | | import org.apache.poi.xssf.usermodel.XSSFRow |
| | | import org.apache.poi.xssf.usermodel.XSSFWorkbook |
| | | import org.jetbrains.kotlin.incremental.isJavaFile |
| | | import java.io.ByteArrayInputStream |
| | | import java.io.File |
| | | import java.io.FileInputStream |
| | | import java.io.InputStream |
| | | import java.time.LocalDate |
| | | import java.util.* |
| | | import kotlin.math.max |
| | |
| | | private const val isLog = false |
| | | |
| | | class MyCell( |
| | | var text: String, |
| | | var rowSpan: Int = 1, |
| | | var colSpan: Int = 1, |
| | | var fontColor: Short? = null |
| | | var text: String, |
| | | var rowSpan: Int = 1, |
| | | var colSpan: Int = 1, |
| | | var fontColor: Short? = null, |
| | | ) |
| | | |
| | | /** |
| | | * 读åæä»¶ |
| | | * @param file è¦è¯»åçæä»¶ |
| | | * @param headerCheck æä»¶é¦è¡æ£æ¥åè° |
| | | * @param onRow åè¡å
容åè°ï¼ä»ç¬¬äºè¡å¼å§ |
| | | */ |
| | | fun read(file: File, headerCheck: (header: XSSFRow) -> Boolean, onRow: (row: Row) -> Unit): Boolean { |
| | | if (!file.exists() || !file.isFile) throw ResponseErrorException("it's not a normal file!") |
| | | if (!file.extension.equals("xls", ignoreCase = true) || !file.extension.equals("xlsx", ignoreCase = true)) { |
| | | throw ResponseErrorException("file's extension name should be xls or xlsx!") |
| | | } |
| | | return read(FileInputStream(file), headerCheck, onRow) |
| | | } |
| | | |
| | | fun read(input: InputStream, headerCheck: (header: XSSFRow) -> Boolean, onRow: (row: Row) -> Unit): Boolean { |
| | | val workbook = XSSFWorkbook(input) |
| | | val sheet1 = workbook.getSheetAt(0) |
| | | val header = sheet1.getRow(sheet1.topRow.toInt()) |
| | | return if (headerCheck(header)) { |
| | | // è·åè¿ä»£å¨å¹¶å»é¤ç¬¬ä¸è¡æ é¢ |
| | | val iterator = sheet1.rowIterator().also { it.next() } |
| | | iterator.forEach { onRow(it) } |
| | | true |
| | | } else { |
| | | false |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * èªå¨å¤çè¡åå¹¶æ°æ® |
| | | */ |
| | | fun write2(heads: List<String>, contents: List<Array<Any>>, workbook: SXSSFWorkbook, sheetName:String) { |
| | | fun write2(heads: List<String>, contents: List<Array<Any>>, workbook: SXSSFWorkbook, sheetName: String) { |
| | | |
| | | val sheet = workbook.createSheet(sheetName) |
| | | |
| | |
| | | var rowspan = maxRow//åå¹¶çè¡ç跨度 |
| | | |
| | | val c = |
| | | if (cell is Array<*>) { |
| | | //彿°æ®ä¸ºæ°ç»æ¶ï¼éè¦æ ¹æ®æå¤§è¡æ°éæ°è®¡ç®è¯¥åå
æ ¼çè¡è·¨åº¦ |
| | | arrayMap[i] = cell |
| | | rowspan = maxRow / if (cell.size == 0) 1 else cell.size |
| | | val c = cell[0] |
| | | if (c is MyCell) { |
| | | rowspan = c.rowSpan |
| | | } |
| | | if (rowspan > 1) { |
| | | log("åå¹¶1-1ï¼$rowIndex;${rowIndex + rowspan - 1};$i") |
| | | sheet.addMergedRegion(CellRangeAddress(rowIndex, rowIndex + rowspan - 1, i, i)) |
| | | } |
| | | if (cell.isEmpty()) { |
| | | "" |
| | | } else { |
| | | cell[0] |
| | | } |
| | | } else { |
| | | //彿°æ®ä¸æ¯æ°ç»æ¶ï¼éè¦ææå¤§è¡æ°åå¹¶åå
æ ¼ |
| | | if (rowspan > 1) { |
| | | log("åå¹¶1-2ï¼$rowIndex;${rowIndex + rowspan - 1};$i") |
| | | sheet.addMergedRegion(CellRangeAddress(rowIndex, rowIndex + rowspan - 1, i, i)) |
| | | } |
| | | cell |
| | | if (cell is Array<*>) { |
| | | //彿°æ®ä¸ºæ°ç»æ¶ï¼éè¦æ ¹æ®æå¤§è¡æ°éæ°è®¡ç®è¯¥åå
æ ¼çè¡è·¨åº¦ |
| | | arrayMap[i] = cell |
| | | rowspan = maxRow / if (cell.size == 0) 1 else cell.size |
| | | val c = cell[0] |
| | | if (c is MyCell) { |
| | | rowspan = c.rowSpan |
| | | } |
| | | if (rowspan > 1) { |
| | | log("åå¹¶1-1ï¼$rowIndex;${rowIndex + rowspan - 1};$i") |
| | | sheet.addMergedRegion(CellRangeAddress(rowIndex, rowIndex + rowspan - 1, i, i)) |
| | | } |
| | | if (cell.isEmpty()) { |
| | | "" |
| | | } else { |
| | | cell[0] |
| | | } |
| | | } else { |
| | | //彿°æ®ä¸æ¯æ°ç»æ¶ï¼éè¦ææå¤§è¡æ°åå¹¶åå
æ ¼ |
| | | if (rowspan > 1) { |
| | | log("åå¹¶1-2ï¼$rowIndex;${rowIndex + rowspan - 1};$i") |
| | | sheet.addMergedRegion(CellRangeAddress(rowIndex, rowIndex + rowspan - 1, i, i)) |
| | | } |
| | | cell |
| | | } |
| | | when (c) { |
| | | is MyCell -> { |
| | | rows.createCell(i).setCellValue(c.text) |
| | |
| | | if (c is MyCell) { |
| | | rowspan = c.rowSpan |
| | | } |
| | | val _c = array[i-1] |
| | | val _c = array[i - 1] |
| | | if (_c is MyCell) { |
| | | lastRowSpan = _c.rowSpan |
| | | } |
| | |
| | | /** |
| | | * èªå¨å¤çè¡åå¹¶æ°æ® |
| | | */ |
| | | fun write(heads: List<Array<String>>, contents: List<Array<Any>>, workbook: SXSSFWorkbook, sheetName: String = "sheet1", row: Int = 0): Int { |
| | | fun write( |
| | | heads: List<Array<String>>, |
| | | contents: List<Array<Any>>, |
| | | workbook: SXSSFWorkbook, |
| | | sheetName: String = "sheet1", |
| | | row: Int = 0, |
| | | ): Int { |
| | | |
| | | val sheet = workbook.getSheet(sheetName)?: workbook.createSheet(sheetName) |
| | | val sheet = workbook.getSheet(sheetName) ?: workbook.createSheet(sheetName) |
| | | // println("sheet: $sheetName") |
| | | |
| | | var rowIndex = row |
| | |
| | | } |
| | | if (rowspan > 1 || colSpan > 1) { |
| | | log("åå¹¶1-1ï¼$rowIndex;${rowIndex + rowspan - 1};$col") |
| | | sheet.addMergedRegion(CellRangeAddress(rowIndex, rowIndex + rowspan - 1, col, col + colSpan - 1)) |
| | | sheet.addMergedRegion(CellRangeAddress(rowIndex, |
| | | rowIndex + rowspan - 1, |
| | | col, |
| | | col + colSpan - 1)) |
| | | } |
| | | if (cell.isEmpty()) { |
| | | "" |
| | |
| | | } |
| | | if (rowspan > 1 || colSpan > 1) { |
| | | log("åå¹¶1-2ï¼$rowIndex;${rowIndex + rowspan - 1};$col") |
| | | sheet.addMergedRegion(CellRangeAddress(rowIndex, rowIndex + rowspan - 1, col, col + colSpan - 1)) |
| | | sheet.addMergedRegion(CellRangeAddress(rowIndex, |
| | | rowIndex + rowspan - 1, |
| | | col, |
| | | col + colSpan - 1)) |
| | | } |
| | | cell |
| | | } |
| | |
| | | } |
| | | if (rowspan > 1 || colSpan > 1) { |
| | | log("åå¹¶2ï¼${index};${index + rowspan - 1};${map.key}") |
| | | sheet.addMergedRegion(CellRangeAddress(index, index + rowspan - 1, map.key, map.key + colSpan - 1)) |
| | | sheet.addMergedRegion(CellRangeAddress(index, |
| | | index + rowspan - 1, |
| | | map.key, |
| | | map.key + colSpan - 1)) |
| | | } |
| | | |
| | | when (c) { |
| | |
| | | package com.flightfeather.uav.common.utils |
| | | |
| | | import com.alibaba.fastjson.JSONObject |
| | | import com.flightfeather.uav.common.exception.ResponseErrorException |
| | | import com.flightfeather.uav.domain.entity.RealTimeData |
| | | import com.flightfeather.uav.socket.bean.AirData |
| | | import org.apache.poi.hssf.usermodel.HSSFWorkbook |
| | |
| | | import java.util.* |
| | | |
| | | /** |
| | | * æ 人è¹ééæ°æ®æ ¼å¼è½¬æ¢ |
| | | * ééæ°æ®æ ¼å¼è½¬æ¢ |
| | | */ |
| | | class FileExchange { |
| | | |
| | |
| | | private val format = SimpleDateFormat("yyyy-MM-dd HH:mm:ss") |
| | | } |
| | | |
| | | fun exchangeJinanData(file: File) { |
| | | val headers = listOf( |
| | | "longitude", |
| | | "latitude", |
| | | "data_time", |
| | | "NO2", |
| | | "CO", |
| | | "H2S", |
| | | "SO2", |
| | | "O3", |
| | | "PM25", |
| | | "PM10", |
| | | "temperature", |
| | | "humidity", |
| | | "VOC", |
| | | "NOI", |
| | | "velocity", |
| | | "wind_speed", |
| | | "wind_direction" |
| | | ) |
| | | try { |
| | | ExcelUtil.read(file, headerCheck = { |
| | | val cellIterator = it.cellIterator() |
| | | val headIterator = headers.iterator() |
| | | while (headIterator.hasNext()) { |
| | | val head = headIterator.next() |
| | | if (cellIterator.hasNext()) { |
| | | val cellText = cellIterator.next().stringCellValue |
| | | if (!cellText.equals(head)) { |
| | | throw ResponseErrorException("æä»¶æ ¼å¼é误, 表头[${head}]åºè¯¥ä¸º[${cellText}]") |
| | | } |
| | | } else { |
| | | throw ResponseErrorException("æä»¶æ ¼å¼é误, 表头[${head}]缺失") |
| | | } |
| | | } |
| | | true |
| | | }, onRow = { |
| | | it.cellIterator().forEach { |
| | | it.numericCellValue |
| | | } |
| | | }) |
| | | } catch (e: Exception) { |
| | | throw ResponseErrorException("excelæä»¶å
容éè¯¯ï¼æ°æ®è½¬æ¢å¤±è´¥ï¼", e) |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * è½¬æ¢æ 人è¹çæ°´è´¨çæµæ°æ® |
| | | */ |
| | | fun exchangeBoatData(deviceCode: String, file: InputStream): List<RealTimeData> { |
| | | val workbook = HSSFWorkbook(file) |
| | | val sheet = workbook.getSheetAt(0) |
| | |
| | | val filePath = "e:/$fileName" |
| | | val out = FileOutputStream(File(filePath)) |
| | | |
| | | val heads = listOf("id", "device_code", "latitude", "longitude", "altitude", "height", "factors", "data_time", "create_time") |
| | | val heads = listOf("id", |
| | | "device_code", |
| | | "latitude", |
| | | "longitude", |
| | | "altitude", |
| | | "height", |
| | | "factors", |
| | | "data_time", |
| | | "create_time") |
| | | val contents = mutableListOf<Array<Any>>() |
| | | |
| | | for (i in 1..sheet.lastRowNum) { |
| | |
| | | fun getResult() { |
| | | // ç¨çµé |
| | | plPower = round(plPower * 100) / 100 |
| | | pfPower = round(pfPower * 100) / 100 |
| | | |
| | | pfPower = round(pfPower * 100) / 100 |
| | | // å¼å
³æ¶é´æ ¼å¼å |
| | | plRTimeStr = DateUtil.instance.dateToString(plRTime, DateUtil.DateStyle.YYYY_MM_DD_HH_MM_SS) |
| | | plETimeStr = DateUtil.instance.dateToString(plETime, DateUtil.DateStyle.YYYY_MM_DD_HH_MM_SS) |
| | |
| | | |
| | | fun importData(file: MultipartFile): BaseResponse<DataImportResult> |
| | | |
| | | fun importJinanData(file: MultipartFile): BaseResponse<DataImportResult> |
| | | |
| | | fun outToWorkbook(deviceCode: String, startTime: String, endTime: String): SXSSFWorkbook |
| | | |
| | | fun outToExcel(deviceCode: String, startTime: String, endTime: String, response: HttpServletResponse): HttpServletResponse |
| | |
| | | return BaseResponse(true, data = DataImportResult("")) |
| | | } |
| | | |
| | | override fun importJinanData(file: MultipartFile): BaseResponse<DataImportResult> { |
| | | TODO("Not yet implemented") |
| | | } |
| | | |
| | | override fun outToWorkbook(deviceCode: String, startTime: String, endTime: String): SXSSFWorkbook { |
| | | val sTime = dateFormatter.parse(startTime) |
| | | val eTime = dateFormatter.parse(endTime) |
¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.flightfeather.uav.lightshare.web |
| | | |
| | | import com.flightfeather.uav.common.exception.ResponseErrorException |
| | | import com.flightfeather.uav.lightshare.bean.BaseResponse |
| | | import com.flightfeather.uav.lightshare.bean.DataHead |
| | | |
| | | |
| | | /** |
| | | * å
è£
æ¥å£è¿åç»æ |
| | | */ |
| | | fun resPack(service: () -> Any?): BaseResponse<Any> { |
| | | return try { |
| | | val res = service() |
| | | if (res is Pair<*, *>) { |
| | | val head = res.first |
| | | if (head is DataHead) { |
| | | BaseResponse(true, head = head, data = res.second) |
| | | } else { |
| | | BaseResponse(true, data = res) |
| | | } |
| | | } else { |
| | | BaseResponse(true, data = res) |
| | | } |
| | | } catch (e: ResponseErrorException) { |
| | | BaseResponse(false, message = e.message ?: "") |
| | | } |
| | | } |
| | |
| | | |
| | | import com.flightfeather.uav.lightshare.service.RealTimeDataService |
| | | import io.swagger.annotations.Api |
| | | import io.swagger.annotations.ApiOperation |
| | | import io.swagger.annotations.ApiParam |
| | | import org.springframework.web.bind.annotation.* |
| | | import org.springframework.web.multipart.MultipartFile |
| | |
| | | fun importData( |
| | | @RequestPart("excel") file: MultipartFile |
| | | ) = realTimeDataService.importData(file) |
| | | |
| | | @ApiOperation(value = "导å
¥éå®åºçæç¯å¢çæµç«çèµ°è¡æ°æ®") |
| | | @PostMapping("/import/jinan") |
| | | fun importJinanData( |
| | | @RequestPart("excel") file: MultipartFile |
| | | ) = realTimeDataService.importJinanData(file) |
| | | } |
| | |
| | | |
| | | val range = FactorType.getRange(a.factorName) ?: return@v |
| | | // å¤ææ°æ®æ¯å¦å¨åçèå´å
|
| | | if (a.factorData ?: 0.0 < range.first || a.factorData ?: 0.0 > range.second) { |
| | | if (a.factorData != null && (a.factorData!! < range.first || a.factorData!! > range.second)) { |
| | | a.factorData = null |
| | | } |
| | | } |
| | |
| | | if (lastData.isNotEmpty() && newList.isNotEmpty()) { |
| | | val lastTime = DateUtil.instance.StringToDate(lastData.last().time) |
| | | val thisTime = DateUtil.instance.StringToDate(newList.first().time) |
| | | if (thisTime?.time?.minus(lastTime?.time ?: 0) ?: 0 >= (60 * 1000)) { |
| | | if ((thisTime?.time?.minus(lastTime?.time ?: 0) ?: 0) >= (60 * 1000)) { |
| | | lastData.clear() |
| | | } |
| | | } |
| | |
| | | val f = it.values?.get(i) |
| | | if (f?.factorName == factorName) { |
| | | val range = FactorType.getRange(f?.factorName) ?: continue |
| | | if (f?.factorData ?: 0.0 in range.first..range.second) { |
| | | if ((f?.factorData ?: 0.0) in range.first..range.second) { |
| | | t += (f?.factorData!! - avg) * (f.factorData!! - avg) |
| | | c++ |
| | | } |
| | |
| | | private fun getAngle(p1: Pair<Double, Double>, p2: Pair<Double, Double>, wd: Double): Double { |
| | | val dx = p2.first - p1.first |
| | | val dy = p2.second - p1.second |
| | | var x1 = atan2(dy, dx) * 180 / PI |
| | | var x1 = atan2(dy, dx) * 180 / PI + 180 |
| | | if (x1 < 0) x1 += 360 |
| | | var x2 = 270 - wd |
| | | if (x2 < 0) x2 += 360 |
| | | x1 = abs(x2 - x1) |
| | | if (x1 > 180) x1 = 360 - x1 |
| | | // println("夹è§ï¼$x1") |
| | | x1 = 180 - x1 |
| | | return x1 |
| | | } |
| | | } |
| | |
| | | } |
| | | |
| | | private fun saveToDataBase(message: BaseMessage, msg: String) { |
| | | if (message is ElectricMessage) { |
| | | if (message is ElectricMessage && message.mn.isNotBlank()) { |
| | | instance.electricRepository.saveData(message) |
| | | } |
| | | } |
| | |
| | | spring: |
| | | datasource: |
| | | # å¼åæ¬å°æå¡å¨ |
| | | url: jdbc:mysql://localhost:3306/dronemonitor?serverTimezone=Asia/Shanghai&prepStmtCacheSize=517&cachePrepStmts=true&autoReconnect=true&characterEncoding=utf-8&allowMultiQueries=true&useSSL=false |
| | | username: root |
| | | password: 123456 |
| | | |
| | | springfox: |
| | | documentation: |
| | | swagger: |
| | |
| | | spring: |
| | | datasource: |
| | | # çº¿ä¸æå¡å¨ |
| | | url: jdbc:mysql://localhost:3306/dronemonitor?serverTimezone=Asia/Shanghai&prepStmtCacheSize=517&cachePrepStmts=true&autoReconnect=true&characterEncoding=utf-8&allowMultiQueries=true&useSSL=false |
| | | username: dronemonitor |
| | | password: dronemonitor_hackxrnomxm |
| | | |
| | | springfox: |
| | | documentation: |
| | | swagger: |
¶Ô±ÈÐÂÎļþ |
| | |
| | | spring: |
| | | datasource: |
| | | driver-class-name: com.mysql.cj.jdbc.Driver |
| | | # å±åç½æå¡å¨ |
| | | # url: jdbc:mysql://192.168.0.200:3306/dronemonitor?serverTimezone=Asia/Shanghai&prepStmtCacheSize=517&cachePrepStmts=true&autoReconnect=true&characterEncoding=utf-8&allowMultiQueries=true&useSSL=false |
| | | # username: root |
| | | # password: cn.FLIGHTFEATHER |
| | | |
| | | # è¿ç¨æå¡å¨ |
| | | url: jdbc:mysql://47.100.191.150:3306/dronemonitor?serverTimezone=Asia/Shanghai&prepStmtCacheSize=517&cachePrepStmts=true&autoReconnect=true&characterEncoding=utf-8&allowMultiQueries=true&useSSL=false |
| | | username: remoteU1 |
| | | password: eSoF8DnzfGTlhAjE |
| | | |
| | | springfox: |
| | | documentation: |
| | | swagger: |
| | | v2: |
| | | enabled: true |
| | | |
| | | |
| | |
| | | spring: |
| | | datasource: |
| | | driver-class-name: com.mysql.cj.jdbc.Driver |
| | | # å±åç½æå¡å¨ |
| | | # url: jdbc:mysql://192.168.0.200:3306/dronemonitor?serverTimezone=Asia/Shanghai&prepStmtCacheSize=517&cachePrepStmts=true&autoReconnect=true&characterEncoding=utf-8&allowMultiQueries=true&useSSL=false |
| | | # username: root |
| | | # password: cn.FLIGHTFEATHER |
| | | |
| | | # çº¿ä¸æå¡å¨ |
| | | # url: jdbc:mysql://localhost:3306/dronemonitor?serverTimezone=Asia/Shanghai&prepStmtCacheSize=517&cachePrepStmts=true&autoReconnect=true&characterEncoding=utf-8&allowMultiQueries=true&useSSL=false |
| | | # username: dronemonitor |
| | | # password: dronemonitor_hackxrnomxm |
| | | |
| | | # å¼åæ¬å°æå¡å¨ |
| | | # url: jdbc:mysql://localhost:3306/dronemonitor?serverTimezone=Asia/Shanghai&prepStmtCacheSize=517&cachePrepStmts=true&autoReconnect=true&characterEncoding=utf-8&allowMultiQueries=true&useSSL=false |
| | | # username: root |
| | | # password: 123456 |
| | | |
| | | # å¼åè¿ç¨æå¡å¨ |
| | | url: jdbc:mysql://47.100.191.150:3306/dronemonitor?serverTimezone=Asia/Shanghai&prepStmtCacheSize=517&cachePrepStmts=true&autoReconnect=true&characterEncoding=utf-8&allowMultiQueries=true&useSSL=false |
| | | username: remoteU1 |
| | | password: eSoF8DnzfGTlhAjE |
| | | hikari: |
| | | maximum-pool-size: 500 |
| | | minimum-idle: 20 |
| | |
| | | helperDialect: mysql |
| | | reasonable: true |
| | | supportMethodsArguments: true |
| | | params: count=countSql |
| | | |
| | | springfox: |
| | | documentation: |
| | | swagger: |
| | | v2: |
| | | enabled: false |
| | | params: count=countSql |
| | |
| | | |
| | | @Test |
| | | fun foo16() { |
| | | val l = mutableListOf<BigDecimal>().apply { |
| | | add(BigDecimal.valueOf(6.23)) |
| | | add(BigDecimal("6.23")) |
| | | add(BigDecimal(6.23)) |
| | | val list = listOf(1, 2, 3, 4, 5, 6) |
| | | val iterator = list.iterator().also { it.next() } |
| | | iterator.forEach { |
| | | println(it) |
| | | } |
| | | l.forEach { println(it) } |
| | | } |
| | | } |