一、需求分析
某个仪器会连续采集1~4通道的16进制的原始数据,现在需要将这些数据,先按照通道进行分类,然后将分好类的数据进行处理,转化成10进制,再最终带入到一个拟合公式,得到处理后的数据,然后再将这些数据写入到Excel表格中进行记录
写入的Excel表格有如下的要求:
四个通道的数据分别放在四个sheet中,每个sheet内的数据按照第一列放“时间”,第二列放“数据”的方式排列,即一条完整的数据占两列,如果这个sheet内不只一条数据,第二条数据就在第四列开始写入(即每条完整的数据之间空一列)最终将这些数据绘成散点图(绘图不做要求)
二、定义实体类
@Data
public class MdBean {/*** prodInst 仪器编号*/private String prodInst;/*** inTime 录入时间*/private Date inTime;/*** currentChannel 通道 ic1 ic2 ic3 ic4*/private String currentChannel;/*** 16 进制原始数据值*/private String data;/*** 添加转换后的数据列表*/private List<List<BigDecimal>> convertedData;// 其他属性的getter和setter方法public List<List<BigDecimal>> getConvertedData() {return convertedData;}public void setConvertedData(List<List<BigDecimal>> convertedData) {this.convertedData = convertedData;}
}
三、实现逻辑
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import java.io.FileOutputStream;
import java.io.IOException;
import java.math.BigDecimal;
import java.util.*;public class Run {/*** Column indexes for each channel*/private static final int[] COLUMN_INDEXES = {0, 1, 3, 4};/*** 要生成的示例数据数量*/private static final int DATA_SIZE = 4;/*** 每个通道要生成的数据数量*/private static final int DATA_SIZE_PER_CHANNEL = 3;public static void main(String[] args) {List<MdBean> dataList = new ArrayList<>();// 创建示例数据// 循环四次,分别代表四个通道for (int i = 0; i < DATA_SIZE; i++) {// 每个通道生成指定数量的数据for (int j = 0; j < DATA_SIZE_PER_CHANNEL; j++) {// 根据循环索引生成ProdInstString prodInst = String.format("%03d", j + 1);// 当前时间Date inTime = new Date();// 当前通道类型String currentChannel = "ic" + (i + 1);String data = generateData(6);// 创建MdBean对象并设置属性MdBean bean = new MdBean();bean.setProdInst(prodInst);bean.setInTime(inTime);bean.setCurrentChannel(currentChannel);bean.setData(data);dataList.add(bean);}}// 获取分类后的数据MapMap<String, List<MdBean>> classifiedDataMap = groupAndConvertData(dataList);for (Map.Entry<String, List<MdBean>> entry : classifiedDataMap.entrySet()) {String channel = entry.getKey();List<MdBean> dataLists = entry.getValue();System.out.println("Channel: " + channel);for (MdBean bean : dataLists) {System.out.println("ProdInst: " + bean.getProdInst());System.out.println("InTime: " + bean.getInTime());System.out.println("ConvertedData: " + bean.getConvertedData());System.out.println("-------------------------------------");}System.out.println("-------------------------------------");}// 将数据写入ExcelwriteDataToExcel(classifiedDataMap, "output10.xlsx");}/*** 它接收一个 List类型的参数 dataList,该列表包含了MdBean对象。* 创建一个HashMap类型的classifiedDataMap,用于存储按照通道分类后的数据。* 方法遍历输入的dataList列表中的每个MdBean对象。* 对于每个MdBean对象,方法获取其当前通道currentChannel。* 如果classifiedDataMap中已经存在该通道对应的列表,则直接获取;否则,创建一个新的空列表。* 方法调用convertAndCalculateNewData方法,将该MdBean对象的原始数据转换为新的数据列表,并将转换后的数据设置到MdBean对象中。* 将该MdBean对象添加到通道对应的列表中。* 最后,将通道对应的列表存储到classifiedDataMap中。* 方法返回classifiedDataMap,其中包含了按照通道分类并转换数据后的结果。*/private static Map<String, List<MdBean>> groupAndConvertData(List<MdBean> dataList) {Map<String, List<MdBean>> classifiedDataMap = new HashMap<>();// 指定要处理的通道列表List<String> specifiedChannels = Arrays.asList("ic1", "ic2", "ic3", "ic4");for (MdBean data : dataList) {String currentChannel = data.getCurrentChannel();// 检查currentChannel是否在指定的通道列表中if (!specifiedChannels.contains(currentChannel)) {// 如果不在指定的通道列表中,则跳过当前数据continue;}// 如果在指定的通道列表中,则继续处理当前数据List<MdBean> channelDataList = classifiedDataMap.getOrDefault(currentChannel, new ArrayList<>());List<BigDecimal> newDataList = convertAndCalculateNewData(data.getData());List<List<BigDecimal>> lists = generateArray(newDataList);data.setConvertedData(lists);channelDataList.add(data);classifiedDataMap.put(currentChannel, channelDataList);}return classifiedDataMap;}private static List<List<BigDecimal>> generateArray(List<BigDecimal> newDataList) {List<List<BigDecimal>> channelArray = new ArrayList<>();// 设置初始时间为 -40.8 增长步径为 0.16BigDecimal currentTime = new BigDecimal("-40.8");BigDecimal step = new BigDecimal("0.16");for (BigDecimal data : newDataList) {List<BigDecimal> row = new ArrayList<>();row.add(currentTime);row.add(data);channelArray.add(row);// 使用BigDecimal的加法运算currentTime = currentTime.add(step);}return channelArray;}private static List<BigDecimal> convertAndCalculateNewData(String hexData) {List<BigDecimal> newDataList = new ArrayList<>();hexData = hexData.replaceAll("\\s", "");for (int i = 0; i < hexData.length(); i += 2) {String hexByte = hexData.substring(i, i + 2);int decimalValue = Integer.parseInt(hexByte, 16);BigDecimal y = BigDecimal.valueOf((1.0 / 64) * decimalValue - 2);newDataList.add(y);}return newDataList;}/*** 首先,创建一个XSSFWorkbook对象,表示一个Excel工作簿。* 然后,使用for循环遍历dataMap中的每个条目。dataMap的每个条目由通道名称作为键和该通道对应的MdBean对象列表作为值组成。* 在循环中,为每个通道创建一个工作表(Sheet),工作表的名称为通道名称。* 接着,获取该通道对应的MdBean对象列表,并使用另一个for循环遍历列表中的每个MdBean对象。* 对于每个MdBean对象,创建一行(Row),并为该行的每列设置值。第一列设置为prodInst属性的值,第二列设置为inTime属性的字符串表示形式,后续列设置为convertedData属性中的转换后数据。* 最后,将工作簿写入指定的文件路径中。** @param dataMap* @param filePath*/private static void writeDataToExcel(Map<String, List<MdBean>> dataMap, String filePath) {try (Workbook workbook = new XSSFWorkbook()) {for (Map.Entry<String, List<MdBean>> entry : dataMap.entrySet()) {String channel = entry.getKey();Sheet sheet = workbook.createSheet(channel);List<MdBean> dataList = entry.getValue();if (dataList != null) {for (MdBean bean : dataList) {System.out.println(bean.getProdInst());int i = 0;int columnIndex = getColumnIndex(sheet, bean);for (List<BigDecimal> entryData : bean.getConvertedData()) {Row r = sheet.getRow(i);if (r == null) {r = sheet.createRow(i);}Cell cell1 = r.createCell(columnIndex );Cell cell2 = r.createCell(columnIndex + 1);BigDecimal val1 = entryData.get(0);BigDecimal val2 = entryData.get(1);cell1.setCellValue(val1.doubleValue());cell2.setCellValue(val2.doubleValue());++i;}}}}try (FileOutputStream outputStream = new FileOutputStream(filePath)) {workbook.write(outputStream);}System.out.println("Data has been written to Excel successfully!");} catch (IOException e) {e.printStackTrace();}}private static int getColumnIndex(Sheet sheet, MdBean bean) {// 获取第一行Row firstRow = sheet.getRow(0);if (firstRow == null) {// 如果第一行为空,则直接返回0return 0;}// 获取 ProdInstint prodInstNumber = Integer.parseInt(bean.getProdInst());// 根据 ProdInst 计算列索引int columnIndex = (prodInstNumber - 1) * 3;return columnIndex;}/*** 生成指定长度的随机16进制字符串*/private static String generateData(int length) {StringBuilder sb = new StringBuilder();Random random = new Random();for (int i = 0; i < length; i++) {// 生成0-15之间的随机数int hexDigit = random.nextInt(16);// 转换为16进制字符并追加到字符串中sb.append(Integer.toHexString(hexDigit));}return sb.toString();}
}