package com.mes.common.PlcTools; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.List; import java.util.Properties; import java.util.concurrent.ConcurrentHashMap; import java.util.regex.Matcher; import java.util.regex.Pattern; public class MockS7PLCtwo { private static volatile MockS7PLCtwo instance; // 单例实例 private ConcurrentHashMap memory = new ConcurrentHashMap<>(); private String storageFilePath = "mockPLCData.properties"; // 私有化构造函数 private MockS7PLCtwo() { // 在构造函数中尝试加载现有的模拟数据 try (FileInputStream fis = new FileInputStream(storageFilePath)) { Properties properties = new Properties(); properties.load(fis); properties.forEach((key, value) -> memory.put(String.valueOf(key), String.valueOf(value))); } catch (IOException e) { System.out.println("没有找到现有的模拟数据文件,将创建一个新的。"); } } // 公共静态方法获取类的唯一实例 public static MockS7PLCtwo getInstance() { if (instance == null) { synchronized (MockS7PLCtwo.class) { if (instance == null) instance = new MockS7PLCtwo(); } } return instance; } // 修改写入方法以持久化数据 public void writeByte(String address, byte[] data) { memory.put(address, new String(data, StandardCharsets.ISO_8859_1)); saveMemory(); } // 修改读取方法以从持久化的数据中读取 public byte[] readByte(String address,int count) { String value = memory.getOrDefault(address, ""); byte[] bytes = value.getBytes(StandardCharsets.ISO_8859_1); if (count >= 0 && count <= bytes.length) { byte[] result = new byte[count]; System.arraycopy(bytes, 0, result, 0, count); return result; } else { // 如果请求的字节数超出了实际可用的字节数,则返回全部可用的字节 return bytes; } } public void writeInt16(String address, short data) { memory.put(address, Short.toString(data)); saveMemory(); } public Short readInt16(String address) { String value = memory.get(address); return value != null ? Short.parseShort(value) : null; } public void writeBoolean(String address, boolean data) { memory.put(address, Boolean.toString(data)); saveMemory(); } public Boolean readBoolean(String address) { String value = memory.get(address); return value != null ? Boolean.parseBoolean(value) : null; } public void writeString(String address, String data) { memory.put(address, data); saveMemory(); } public String readString(String address) { return memory.getOrDefault(address, ""); } public void writeTime(String address, long data) { memory.put(address, Long.toString(data)); saveMemory(); } public Long readTime(String address) { String value = memory.get(address); return value != null ? Long.parseLong(value) : null; } // 添加一个方法来保存数据到文件 private void saveMemory() { Properties properties = new Properties(); properties.putAll(memory); try (FileOutputStream fos = new FileOutputStream(storageFilePath)) { properties.store(fos, "Mock S7 PLC Data"); } catch (IOException e) { System.out.println("保存模拟数据失败:" + e.getMessage()); } } // 连续写入多个Word public void writeword(String address, List data) { for (int i = 0; i < data.size(); i++) { String addr = calculateAddress(address, i * 2); // 假设每个word占两个地址单位 memory.put(addr, Short.toString(data.get(i))); } saveMemory(); } // 不连续地址word写入多个Word public void WriteWords(List addresses, List datas) { if (addresses.size() != datas.size()) { throw new IllegalArgumentException("地址列表和数据列表的大小必须相同。"); } for (int i = 0; i < addresses.size(); i++) { String addr = addresses.get(i); short data = datas.get(i); // 假设这里使用内存映射来模拟PLC写入操作 memory.put(addr, Short.toString(data)); } saveMemory(); // 在所有数据写入后保存更改 } // 连续读取多个Word public List readwords(String address, int count) { List result = new ArrayList<>(); for (int i = 0; i < count; i++) { String addr = calculateAddress(address, i * 2); // 同上,每个word占两个地址单位 //System.out.println(addr); String value = memory.get(addr); if (value != null) { result.add(Short.parseShort(value)); } else { result.add(null); // 或者考虑抛出异常或其他错误处理 } } return result; } public List ReadWords(List addresses) { List datas = new ArrayList<>(); for (String addr : addresses) { // 从内存映射中获取数据 String dataStr = memory.get(addr); // 将字符串转换成short类型,并添加到结果列表中 // 这里假设数据已经以适当的方式存储(例如,作为短整型的字符串表示) // 如果读取的数据为空或转换失败,你可能需要处理这些情况 try { short data = Short.parseShort(dataStr); datas.add(data); } catch (NumberFormatException e) { System.err.println("读取地址 " + addr + " 的数据时出错: " + e.getMessage()); } } return datas; } // 连续写入多个Bit public void writebits(String address, List data) { for (int i = 0; i < data.size(); i++) { String addr = calculateAddress(address, i); // 假设每个bit占一个地址单位 memory.put(addr, Boolean.toString(data.get(i))); } saveMemory(); } //bit分散地址读取 public List readBits(List addresses) { List results = new ArrayList<>(); for (String address : addresses) { // 对于每个地址,直接使用 calculateAddress 来处理可能的位偏移 // 这里假设 calculateAddress 已经足够智能以处理单个位的偏移 // 由于我们是逐个读取,每次偏移量都是0 String addr = calculateAddress(address, 0); String value = memory.get(addr); if (value != null) { results.add(Boolean.parseBoolean(value)); } else { // 如果地址对应的值不存在于内存中,可以选择添加 null 或抛出异常 // 这里选择添加 null,但在实际应用中应根据具体需求决定 results.add(null); } } return results; } // 连续读取多个Bit public List readbits(String address, int count) { List result = new ArrayList<>(); for (int i = 0; i < count; i++) { String addr = calculateAddress(address, i); // 同上,每个bit占一个地址单位 String value = memory.get(addr); if (value != null) { result.add(Boolean.parseBoolean(value)); } else { result.add(null); // 或者考虑抛出异常或其他错误处理 } } return result; } // 计算连续地址 // private String calculateAddress(String baseAddress, int offset) { // // 支持带字母的地址格式 // Pattern pattern = Pattern.compile("(\\D*)(\\d+)"); // Matcher matcher = pattern.matcher(baseAddress); // if (matcher.find()) { // String prefix = matcher.group(1); // int address = Integer.parseInt(matcher.group(2)); // return prefix + (address + offset); // } else { // throw new IllegalArgumentException("Invalid address format: " + baseAddress); // } // } private String calculateAddress(String baseAddress, int offset) { // 分割地址为数据块、字偏移和位偏移(如果有) Pattern pattern = Pattern.compile("(DB\\d+)\\.(\\d+)(?:\\.(\\d+))?"); Matcher matcher = pattern.matcher(baseAddress); if (matcher.find()) { String dbNumber = matcher.group(1); // 数据块编号,如 "DB100" int wordOffset = Integer.parseInt(matcher.group(2)); // 字偏移 String bitOffsetStr = matcher.group(3); // 位偏移,可能为空 if (bitOffsetStr != null) { // 存在位偏移,进行位操作 int bitOffset = Integer.parseInt(bitOffsetStr); int totalBitOffset = bitOffset + offset; // 计算新的字偏移和位偏移 int newWordOffset = wordOffset + (totalBitOffset / 8); int newBitOffset = totalBitOffset % 8; return String.format("%s.%d.%d", dbNumber, newWordOffset, newBitOffset); } else { // 仅存在字偏移,进行字操作 // 注意:假设每个字占用2个字节 int newWordOffset = wordOffset + (offset ); return String.format("%s.%d", dbNumber, newWordOffset); } } else { throw new IllegalArgumentException("Invalid address format: " + baseAddress); } } }