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<String, String> 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<Short> 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<String> addresses, List<Short> 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<Short> readwords(String address, int count) {
|
List<Short> 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<Short> ReadWords(List<String> addresses) {
|
List<Short> 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<Boolean> 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<Boolean> readBits(List<String> addresses) {
|
List<Boolean> 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<Boolean> readbits(String address, int count) {
|
List<Boolean> 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);
|
}
|
}
|
|
|
|
|
}
|