|
| 1 | +/******************************************************************************* |
| 2 | + * Copyright (c) 2025 Oak Ridge National Laboratory. |
| 3 | + * All rights reserved. This program and the accompanying materials |
| 4 | + * are made available under the terms of the Eclipse Public License v1.0 |
| 5 | + * which accompanies this distribution, and is available at |
| 6 | + * http://www.eclipse.org/legal/epl-v10.html |
| 7 | + ******************************************************************************/ |
| 8 | +package org.csstudio.trends.databrowser3.export; |
| 9 | + |
| 10 | +import java.io.PrintStream; |
| 11 | +import java.text.MessageFormat; |
| 12 | +import java.time.Instant; |
| 13 | +import java.time.LocalDateTime; |
| 14 | +import java.time.ZoneId; |
| 15 | +import java.util.ArrayList; |
| 16 | +import java.util.List; |
| 17 | +import java.util.Objects; |
| 18 | +import java.util.function.Consumer; |
| 19 | + |
| 20 | +import org.csstudio.trends.databrowser3.Messages; |
| 21 | +import org.csstudio.trends.databrowser3.model.ArchiveDataSource; |
| 22 | +import org.csstudio.trends.databrowser3.model.Model; |
| 23 | +import org.csstudio.trends.databrowser3.model.ModelItem; |
| 24 | +import org.csstudio.trends.databrowser3.model.PVItem; |
| 25 | +import org.epics.vtype.VEnum; |
| 26 | +import org.epics.vtype.VNumber; |
| 27 | +import org.epics.vtype.VString; |
| 28 | +import org.epics.vtype.VType; |
| 29 | +import org.phoebus.archive.reader.SpreadsheetIterator; |
| 30 | +import org.phoebus.archive.reader.ValueIterator; |
| 31 | +import org.phoebus.framework.jobs.JobMonitor; |
| 32 | +import org.phoebus.util.time.SecondsParser; |
| 33 | +import org.phoebus.util.time.TimestampFormats; |
| 34 | +import org.apache.poi.hssf.usermodel.HSSFWorkbook; |
| 35 | +import org.apache.poi.ss.usermodel.Cell; |
| 36 | +import org.apache.poi.ss.usermodel.CellStyle; |
| 37 | +import org.apache.poi.ss.usermodel.CellType; |
| 38 | +import org.apache.poi.ss.usermodel.FillPatternType; |
| 39 | +import org.apache.poi.ss.usermodel.Font; |
| 40 | +import org.apache.poi.ss.usermodel.IndexedColors; |
| 41 | +import org.apache.poi.ss.usermodel.Row; |
| 42 | +import org.apache.poi.ss.usermodel.Sheet; |
| 43 | +import org.apache.poi.ss.usermodel.Workbook; |
| 44 | + |
| 45 | +/** Export Job for exporting data from Model as Excel file |
| 46 | + * @author Kay Kasemir |
| 47 | + */ |
| 48 | +@SuppressWarnings("nls") |
| 49 | +public class ExcelExportJob extends ExportJob |
| 50 | +{ |
| 51 | + private Workbook wb = null; |
| 52 | + private CellStyle comment_style, header_style, timestamp_style; |
| 53 | + private Sheet sheet; |
| 54 | + private Row row; |
| 55 | + private ZoneId zone = ZoneId.systemDefault(); |
| 56 | + private final boolean tabular, min_max, sevr_stat; |
| 57 | + |
| 58 | + /** @param model Model |
| 59 | + * @param start Start time |
| 60 | + * @param end End time |
| 61 | + * @param source Data source |
| 62 | + * @param optimize_parameter Bin count |
| 63 | + * @param filename Export file name |
| 64 | + * @param error_handler Error handler |
| 65 | + * @param unixTimeStamp Use UNIX time stamp epoch? |
| 66 | + */ |
| 67 | + public ExcelExportJob(final Model model, |
| 68 | + final Instant start, final Instant end, final Source source, |
| 69 | + final boolean tabular, final boolean min_max, final boolean sevr_stat, |
| 70 | + final double optimize_parameter, |
| 71 | + final String filename, |
| 72 | + final Consumer<Exception> error_handler, |
| 73 | + final boolean unixTimeStamp) |
| 74 | + { |
| 75 | + super("", model, start, end, source, optimize_parameter, filename, error_handler, unixTimeStamp); |
| 76 | + this.tabular = tabular; |
| 77 | + this.min_max = min_max; |
| 78 | + this.sevr_stat = sevr_stat; |
| 79 | + } |
| 80 | + |
| 81 | + private void addComment(final Row row, final String label, final String text) |
| 82 | + { |
| 83 | + Cell cell = row.createCell(0, CellType.STRING); |
| 84 | + cell.setCellValue(label); |
| 85 | + cell.setCellStyle(comment_style); |
| 86 | + if (text != null) |
| 87 | + { |
| 88 | + cell = row.createCell(1, CellType.STRING); |
| 89 | + cell.setCellValue(text); |
| 90 | + cell.setCellStyle(comment_style); |
| 91 | + } |
| 92 | + } |
| 93 | + |
| 94 | + @Override |
| 95 | + protected void printExportInfo(final PrintStream out) throws Exception |
| 96 | + { |
| 97 | + wb = new HSSFWorkbook(); |
| 98 | + |
| 99 | + comment_style = wb.createCellStyle(); |
| 100 | + Font font = wb.createFont(); |
| 101 | + font.setBold(true); |
| 102 | + font.setColor(IndexedColors.DARK_BLUE.getIndex()); |
| 103 | + comment_style.setFont(font); |
| 104 | + |
| 105 | + header_style = wb.createCellStyle(); |
| 106 | + font = wb.createFont(); |
| 107 | + font.setItalic(true); |
| 108 | + header_style.setFont(font); |
| 109 | + header_style.setFillForegroundColor(IndexedColors.GREY_25_PERCENT.getIndex()); |
| 110 | + header_style.setFillPattern(FillPatternType.SOLID_FOREGROUND); |
| 111 | + |
| 112 | + timestamp_style = wb.createCellStyle(); |
| 113 | + timestamp_style.setDataFormat( |
| 114 | + wb.getCreationHelper() |
| 115 | + .createDataFormat() |
| 116 | + .getFormat("yyyy-mm-dd hh:mm:ss.000")); |
| 117 | + |
| 118 | + sheet = wb.createSheet("Archive Data"); |
| 119 | + |
| 120 | + addComment(row = sheet.createRow(0), "Created by CS-Studio Data Browser", null); |
| 121 | + addComment(row = sheet.createRow(row.getRowNum() + 2), "Start time", TimestampFormats.MILLI_FORMAT.format(start)); |
| 122 | + addComment(row = sheet.createRow(row.getRowNum() + 1), "End time", TimestampFormats.MILLI_FORMAT.format(end)); |
| 123 | + addComment(row = sheet.createRow(row.getRowNum() + 1), "Source", source.toString()); |
| 124 | + |
| 125 | + if (source == Source.OPTIMIZED_ARCHIVE) |
| 126 | + addComment(row = sheet.createRow(row.getRowNum() + 1), "Desired Value Count", Double.toString(optimize_parameter)); |
| 127 | + else if (source == Source.LINEAR_INTERPOLATION) |
| 128 | + addComment(row = sheet.createRow(row.getRowNum() + 1), "Interpolation Interval", SecondsParser.formatSeconds(optimize_parameter)); |
| 129 | + } |
| 130 | + |
| 131 | + private Cell createCell(final Row row, final int column, final VType value) |
| 132 | + { |
| 133 | + final Cell cell; |
| 134 | + if (value instanceof VNumber v) |
| 135 | + { |
| 136 | + cell = row.createCell(column, CellType.NUMERIC); |
| 137 | + cell.setCellValue(v.getValue().doubleValue()); |
| 138 | + } |
| 139 | + else if (value instanceof VEnum v) |
| 140 | + { |
| 141 | + cell = row.createCell(column, CellType.NUMERIC); |
| 142 | + cell.setCellValue(v.getIndex()); |
| 143 | + } |
| 144 | + else if (value instanceof VString v) |
| 145 | + { |
| 146 | + cell = row.createCell(column, CellType.STRING); |
| 147 | + cell.setCellValue(v.getValue()); |
| 148 | + } |
| 149 | + else if (value == null) |
| 150 | + cell = row.createCell(column, CellType.BLANK); |
| 151 | + else |
| 152 | + { |
| 153 | + cell = row.createCell(column, CellType.STRING); |
| 154 | + cell.setCellValue(Objects.toString(value)); |
| 155 | + } |
| 156 | + return cell; |
| 157 | + } |
| 158 | + |
| 159 | + @Override |
| 160 | + protected void performExport(final JobMonitor monitor, |
| 161 | + final PrintStream out) throws Exception |
| 162 | + { |
| 163 | + // Item header |
| 164 | + for (ModelItem item : model.getItems()) |
| 165 | + { |
| 166 | + addComment(row = sheet.createRow(row.getRowNum() + 2), "Channel", item.getResolvedName()); |
| 167 | + if (! item.getName().equals(item.getDisplayName())) |
| 168 | + addComment(row = sheet.createRow(row.getRowNum() + 1), "Name", item.getResolvedDisplayName()); |
| 169 | + |
| 170 | + if (item instanceof PVItem) |
| 171 | + { |
| 172 | + final PVItem pv = (PVItem) item; |
| 173 | + addComment(row = sheet.createRow(row.getRowNum() + 1), "Archives:", null); |
| 174 | + |
| 175 | + int i=1; |
| 176 | + for (ArchiveDataSource archive : pv.getArchiveDataSources()) |
| 177 | + { |
| 178 | + addComment(row = sheet.createRow(row.getRowNum() + 1), |
| 179 | + i + ") " + archive.getName(), |
| 180 | + "URL " + archive.getUrl()); |
| 181 | + ++i; |
| 182 | + } |
| 183 | + } |
| 184 | + } |
| 185 | + |
| 186 | + // Spreadsheet data header |
| 187 | + row = sheet.createRow(row.getRowNum() + 2); |
| 188 | + Cell cell = row.createCell(0, CellType.STRING); |
| 189 | + cell.setCellStyle(header_style); |
| 190 | + cell.setCellValue(Messages.TimeColumn); |
| 191 | + for (ModelItem item : model.getItems()) |
| 192 | + { |
| 193 | + cell = row.createCell(cell.getColumnIndex()+1, CellType.STRING); |
| 194 | + cell.setCellStyle(header_style); |
| 195 | + cell.setCellValue(item.getResolvedName()); |
| 196 | + } |
| 197 | + |
| 198 | + // Create spreadsheet interpolation |
| 199 | + final List<ValueIterator> iters = new ArrayList<>(); |
| 200 | + for (ModelItem item : model.getItems()) |
| 201 | + { |
| 202 | + monitor.beginTask(MessageFormat.format("Fetching data for {0}", item.getName())); |
| 203 | + iters.add(createValueIterator(item)); |
| 204 | + } |
| 205 | + final SpreadsheetIterator iter = new SpreadsheetIterator(iters.toArray(new ValueIterator[iters.size()])); |
| 206 | + // Dump the spreadsheet lines |
| 207 | + long line_count = 0; |
| 208 | + while (iter.hasNext() && !monitor.isCanceled()) |
| 209 | + { |
| 210 | + final Instant time = iter.getTime(); |
| 211 | + final VType line[] = iter.next(); |
| 212 | + |
| 213 | + row = sheet.createRow(row.getRowNum() + 1); |
| 214 | + cell = row.createCell(0, CellType.NUMERIC); |
| 215 | + if (unixTimeStamp) |
| 216 | + cell.setCellValue(time.toEpochMilli()); |
| 217 | + else |
| 218 | + { |
| 219 | + cell.setCellValue(LocalDateTime.ofInstant(time, zone)); |
| 220 | + cell.setCellStyle(timestamp_style); |
| 221 | + } |
| 222 | + |
| 223 | + for (int i=0; i<line.length; ++i) |
| 224 | + { |
| 225 | + cell = createCell(row, cell.getColumnIndex()+1, line[i]); |
| 226 | + } |
| 227 | + ++line_count; |
| 228 | + if ((line_count % PROGRESS_UPDATE_LINES) == 0) |
| 229 | + monitor.beginTask(MessageFormat.format("Wrote {0} samples", line_count)); |
| 230 | + if (monitor.isCanceled()) |
| 231 | + break; |
| 232 | + } |
| 233 | + iter.close(); |
| 234 | + |
| 235 | + wb.write(out); |
| 236 | + } |
| 237 | +} |
0 commit comments