package com.mes.service;
|
|
import com.fasterxml.jackson.core.type.TypeReference;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.github.xingshuangs.iot.protocol.s7.enums.EPlcType;
|
import com.github.xingshuangs.iot.protocol.s7.service.S7PLC;
|
import com.mes.device.entity.DeviceConfig;
|
import com.mes.device.service.DeviceConfigService;
|
import com.mes.device.util.ConfigJsonHelper;
|
import com.mes.entity.PlcBaseData;
|
import com.mes.entity.PlcAddress;
|
import com.mes.service.PlcDynamicDataService;
|
import com.mes.s7.enhanced.EnhancedS7Serializer;
|
import lombok.extern.slf4j.Slf4j;
|
import org.springframework.stereotype.Service;
|
|
import javax.annotation.Resource;
|
import java.util.Collections;
|
import java.util.Map;
|
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentMap;
|
|
/**
|
* PLC测试写入服务
|
* 模拟PLC行为,向PLC写入测试数据,用于测试MES程序
|
*
|
* @author huang
|
* @date 2025/10/29
|
*/
|
@Slf4j
|
@Service
|
public class PlcTestWriteService {
|
|
@Resource
|
private PlcAddressService plcAddressService;
|
|
@Resource
|
private DeviceConfigService deviceConfigService;
|
|
@Resource
|
private PlcDynamicDataService plcDynamicDataService;
|
|
private final ObjectMapper objectMapper = new ObjectMapper();
|
private static final TypeReference<Map<String, Object>> MAP_TYPE = new TypeReference<Map<String, Object>>() {};
|
|
private static final int ON = 1;
|
private static final int OFF = 0;
|
|
// 当前使用的项目标识
|
private String currentProjectId = "vertical";
|
|
// 缓存不同项目的S7Serializer实例
|
private final ConcurrentMap<String, EnhancedS7Serializer> serializerCache = new ConcurrentHashMap<>();
|
|
/**
|
* 模拟PLC发送请求字(触发MES任务下发)
|
*/
|
public boolean simulatePlcRequest() {
|
return simulatePlcRequest(currentProjectId);
|
}
|
|
/**
|
* 模拟PLC发送请求字(触发MES任务下发)- 支持指定项目
|
*/
|
public boolean simulatePlcRequest(String projectId) {
|
try {
|
PlcAddress config = plcAddressService.getProjectConfigWithMapping(projectId);
|
if (config == null) {
|
log.error("项目配置不存在: projectId={}", projectId);
|
return false;
|
}
|
EnhancedS7Serializer s7Serializer = getSerializerForProject(projectId, config);
|
if (s7Serializer == null) {
|
log.error("无法创建S7Serializer: projectId={}", projectId);
|
return false;
|
}
|
return simulatePlcRequestInternal(projectId, config, s7Serializer);
|
} catch (Exception e) {
|
log.error("模拟PLC请求字失败", e);
|
return false;
|
}
|
}
|
|
private boolean simulatePlcRequestInternal(String projectId, PlcAddress config, EnhancedS7Serializer s7Serializer) throws Exception {
|
PlcBaseData currentData = s7Serializer.read(PlcBaseData.class, config.getDbArea(), config.getBeginIndex());
|
if (currentData == null) {
|
log.error("读取PLC数据失败,返回null: projectId={}, dbArea={}, beginIndex={}",
|
projectId, config.getDbArea(), config.getBeginIndex());
|
return false;
|
}
|
|
if (currentData.getOnlineState() == OFF) {
|
log.info("当前PLC联机模式为0,停止联机");
|
return false;
|
} else if (currentData.getPlcReport() == ON) {
|
log.info("当前上片PLC汇报字为1,重置为0");
|
currentData.setPlcReport(OFF);
|
}
|
currentData.setPlcRequest(ON);
|
s7Serializer.write(currentData, config.getDbArea(), config.getBeginIndex());
|
log.info("模拟PLC发送请求字成功:plcRequest=1, projectId={}, dbArea={}, beginIndex={}",
|
projectId, config.getDbArea(), config.getBeginIndex());
|
return true;
|
}
|
|
/**
|
* 模拟PLC任务完成汇报
|
*/
|
public boolean simulatePlcReport() {
|
return simulatePlcReport(currentProjectId);
|
}
|
|
/**
|
* 模拟PLC任务完成汇报 - 支持指定项目
|
*/
|
public boolean simulatePlcReport(String projectId) {
|
try {
|
// 获取项目配置(数据库实体)
|
PlcAddress config = plcAddressService.getProjectConfigWithMapping(projectId);
|
if (config == null) {
|
log.error("项目配置不存在: projectId={}", projectId);
|
return false;
|
}
|
|
// 获取对应的S7Serializer
|
EnhancedS7Serializer s7Serializer = getSerializerForProject(projectId, config);
|
if (s7Serializer == null) {
|
log.error("无法创建S7Serializer: projectId={}", projectId);
|
return false;
|
}
|
|
return simulatePlcReportInternal(projectId, config, s7Serializer);
|
} catch (Exception e) {
|
log.error("模拟PLC任务完成汇报失败", e);
|
return false;
|
}
|
}
|
|
private boolean simulatePlcReportInternal(String projectId, PlcAddress config, EnhancedS7Serializer s7Serializer) throws Exception {
|
PlcBaseData currentData = s7Serializer.read(PlcBaseData.class, config.getDbArea(), config.getBeginIndex());
|
if (currentData == null) {
|
log.error("读取PLC数据失败,返回null: projectId={}, dbArea={}, beginIndex={}",
|
projectId, config.getDbArea(), config.getBeginIndex());
|
return false;
|
}
|
|
currentData.setPlcReport(ON);
|
currentData.setPlcRequest(OFF);
|
currentData.setMesGlassCount(10);
|
|
s7Serializer.write(currentData, config.getDbArea(), config.getBeginIndex());
|
log.info("模拟PLC任务完成汇报:plcReport=1, mesGlassCount=10, projectId={}, dbArea={}, beginIndex={}",
|
projectId, config.getDbArea(), config.getBeginIndex());
|
return true;
|
}
|
|
/**
|
* 模拟PLC发送联机状态
|
*/
|
public boolean simulateOnlineStatus(int onlineState) {
|
return simulateOnlineStatus(onlineState, currentProjectId);
|
}
|
|
/**
|
* 模拟PLC发送联机状态 - 支持指定项目
|
*/
|
public boolean simulateOnlineStatus(int onlineState, String projectId) {
|
try {
|
// 获取项目配置(数据库实体)
|
PlcAddress config = plcAddressService.getProjectConfigWithMapping(projectId);
|
if (config == null) {
|
log.error("项目配置不存在: projectId={}", projectId);
|
return false;
|
}
|
|
// 获取对应的S7Serializer
|
EnhancedS7Serializer s7Serializer = getSerializerForProject(projectId, config);
|
if (s7Serializer == null) {
|
log.error("无法创建S7Serializer: projectId={}", projectId);
|
return false;
|
}
|
|
return simulateOnlineStatusInternal(onlineState, projectId, config, s7Serializer);
|
} catch (Exception e) {
|
log.error("模拟PLC联机状态失败", e);
|
return false;
|
}
|
}
|
|
private boolean simulateOnlineStatusInternal(int onlineState, String projectId, PlcAddress config, EnhancedS7Serializer s7Serializer) throws Exception {
|
PlcBaseData currentData = s7Serializer.read(PlcBaseData.class, config.getDbArea(), config.getBeginIndex());
|
if (currentData == null) {
|
log.error("读取PLC数据失败,返回null: projectId={}, dbArea={}, beginIndex={}",
|
projectId, config.getDbArea(), config.getBeginIndex());
|
return false;
|
}
|
|
currentData.setOnlineState(onlineState);
|
s7Serializer.write(currentData, config.getDbArea(), config.getBeginIndex());
|
log.info("模拟PLC联机状态:onlineState={}, projectId={}, dbArea={}, beginIndex={}",
|
onlineState, projectId, config.getDbArea(), config.getBeginIndex());
|
return true;
|
}
|
|
/**
|
* 重置PLC所有状态
|
*/
|
public boolean resetPlc() {
|
return resetPlc(currentProjectId);
|
}
|
|
/**
|
* 重置PLC所有状态 - 支持指定项目
|
*/
|
public boolean resetPlc(String projectId) {
|
try {
|
// 获取项目配置(数据库实体)
|
PlcAddress config = plcAddressService.getProjectConfigWithMapping(projectId);
|
if (config == null) {
|
log.error("项目配置不存在: projectId={}", projectId);
|
return false;
|
}
|
|
EnhancedS7Serializer s7Serializer = getSerializerForProject(projectId, config);
|
if (s7Serializer == null) {
|
log.error("无法创建S7Serializer: projectId={}", projectId);
|
return false;
|
}
|
return resetPlcInternal(projectId, config, s7Serializer);
|
} catch (Exception e) {
|
log.error("重置PLC状态失败", e);
|
return false;
|
}
|
}
|
|
private boolean resetPlcInternal(String projectId, PlcAddress config, EnhancedS7Serializer s7Serializer) throws Exception {
|
PlcBaseData resetData = new PlcBaseData();
|
resetData.setPlcRequest(OFF);
|
resetData.setPlcReport(OFF);
|
resetData.setMesSend(OFF);
|
resetData.setMesConfirm(OFF);
|
resetData.setOnlineState(ON);
|
resetData.setMesGlassCount(0);
|
resetData.setAlarmInfo(OFF);
|
|
s7Serializer.write(resetData, config.getDbArea(), config.getBeginIndex());
|
log.info("PLC状态已重置, projectId={}, dbArea={}, beginIndex={}",
|
projectId, config.getDbArea(), config.getBeginIndex());
|
return true;
|
}
|
|
/**
|
* 读取PLC当前状态
|
*/
|
public PlcBaseData readPlcStatus() {
|
return readPlcStatus(currentProjectId);
|
}
|
|
/**
|
* 读取PLC当前状态 - 支持指定项目
|
*/
|
public PlcBaseData readPlcStatus(String projectId) {
|
try {
|
// 获取项目配置(数据库实体)
|
PlcAddress config = plcAddressService.getProjectConfigWithMapping(projectId);
|
if (config == null) {
|
log.error("项目配置不存在: projectId={}", projectId);
|
return null;
|
}
|
|
EnhancedS7Serializer s7Serializer = getSerializerForProject(projectId, config);
|
if (s7Serializer == null) {
|
log.error("无法创建S7Serializer: projectId={}", projectId);
|
return null;
|
}
|
|
return readPlcStatusInternal(projectId, config, s7Serializer);
|
} catch (Exception e) {
|
log.error("读取PLC状态失败", e);
|
return null;
|
}
|
}
|
|
private PlcBaseData readPlcStatusInternal(String projectId, PlcAddress config, EnhancedS7Serializer s7Serializer) throws Exception {
|
PlcBaseData data = s7Serializer.read(PlcBaseData.class, config.getDbArea(), config.getBeginIndex());
|
if (data == null) {
|
log.error("读取PLC状态返回null: projectId={}, dbArea={}, beginIndex={}",
|
projectId, config.getDbArea(), config.getBeginIndex());
|
}
|
return data;
|
}
|
|
/**
|
* 设置当前项目标识
|
*/
|
public void setCurrentProjectId(String projectId) {
|
this.currentProjectId = projectId;
|
}
|
|
/**
|
* 获取当前项目标识
|
*/
|
public String getCurrentProjectId() {
|
return this.currentProjectId;
|
}
|
|
/**
|
* 获取项目对应的S7Serializer实例
|
* 如果不存在,则创建一个新的实例并缓存
|
*
|
* @param projectId 项目标识
|
* @param config 项目配置
|
* @return S7Serializer实例
|
*/
|
private EnhancedS7Serializer getSerializerForProject(String projectId, PlcAddress config) {
|
return serializerCache.computeIfAbsent(projectId, id -> {
|
// 解析PLC类型
|
EPlcType plcType = EPlcType.S1200; // 默认值
|
if (config != null && config.getPlcType() != null) {
|
try {
|
plcType = EPlcType.valueOf(config.getPlcType());
|
} catch (IllegalArgumentException e) {
|
log.warn("未知的PLC类型: {}, 使用默认类型 S1200", config.getPlcType());
|
}
|
}
|
|
// 创建S7PLC实例
|
String plcIp = (config != null && config.getPlcIp() != null) ? config.getPlcIp() : "192.168.10.21";
|
S7PLC s7Plc = new S7PLC(plcType, plcIp);
|
|
// 创建并返回EnhancedS7Serializer实例
|
return EnhancedS7Serializer.newInstance(s7Plc);
|
});
|
}
|
|
/**
|
* 清除指定项目的S7Serializer缓存
|
*
|
* @param projectId 项目标识
|
*/
|
public void clearSerializerCache(String projectId) {
|
serializerCache.remove(projectId);
|
log.info("已清除项目 {} 的S7Serializer缓存", projectId);
|
}
|
|
/**
|
* 清除所有S7Serializer缓存
|
*/
|
public void clearAllSerializerCache() {
|
serializerCache.clear();
|
log.info("已清除所有S7Serializer缓存");
|
}
|
|
/**
|
* 根据设备ID模拟PLC发送请求字
|
*
|
* @param deviceId 设备ID
|
* @return 是否成功
|
*/
|
public boolean simulatePlcRequestByDevice(Long deviceId) {
|
DeviceConfig device = deviceConfigService.getDeviceById(deviceId);
|
if (device == null) {
|
log.error("设备不存在: deviceId={}", deviceId);
|
return false;
|
}
|
try {
|
String projectId = resolveProjectId(device);
|
PlcAddress config = buildPlcAddressFromDevice(device);
|
EnhancedS7Serializer s7Serializer = getSerializerForDevice(device);
|
return simulatePlcRequestInternal(projectId, config, s7Serializer);
|
} catch (Exception e) {
|
log.error("根据设备模拟PLC请求字失败: deviceId={}", deviceId, e);
|
return false;
|
}
|
}
|
|
/**
|
* 根据设备ID模拟PLC任务完成汇报
|
*
|
* @param deviceId 设备ID
|
* @return 是否成功
|
*/
|
public boolean simulatePlcReportByDevice(Long deviceId) {
|
DeviceConfig device = deviceConfigService.getDeviceById(deviceId);
|
if (device == null) {
|
log.error("设备不存在: deviceId={}", deviceId);
|
return false;
|
}
|
try {
|
String projectId = resolveProjectId(device);
|
PlcAddress config = buildPlcAddressFromDevice(device);
|
EnhancedS7Serializer s7Serializer = getSerializerForDevice(device);
|
return simulatePlcReportInternal(projectId, config, s7Serializer);
|
} catch (Exception e) {
|
log.error("根据设备模拟PLC汇报失败: deviceId={}", deviceId, e);
|
return false;
|
}
|
}
|
|
/**
|
* 根据设备ID重置PLC所有状态
|
*
|
* @param deviceId 设备ID
|
* @return 是否成功
|
*/
|
public boolean resetPlcByDevice(Long deviceId) {
|
DeviceConfig device = deviceConfigService.getDeviceById(deviceId);
|
if (device == null) {
|
log.error("设备不存在: deviceId={}", deviceId);
|
return false;
|
}
|
try {
|
String projectId = resolveProjectId(device);
|
PlcAddress config = buildPlcAddressFromDevice(device);
|
EnhancedS7Serializer s7Serializer = getSerializerForDevice(device);
|
return resetPlcInternal(projectId, config, s7Serializer);
|
} catch (Exception e) {
|
log.error("根据设备重置PLC状态失败: deviceId={}", deviceId, e);
|
return false;
|
}
|
}
|
|
/**
|
* 根据设备ID读取PLC当前状态
|
*
|
* @param deviceId 设备ID
|
* @return PLC状态数据
|
*/
|
public Map<String, Object> readPlcStatusByDevice(Long deviceId) {
|
DeviceConfig device = deviceConfigService.getDeviceById(deviceId);
|
if (device == null) {
|
log.error("设备不存在: deviceId={}", deviceId);
|
return null;
|
}
|
try {
|
String projectId = resolveProjectId(device);
|
PlcAddress config = buildPlcAddressFromDevice(device);
|
EnhancedS7Serializer s7Serializer = getSerializerForDevice(device);
|
PlcBaseData data = readPlcStatusInternal(projectId, config, s7Serializer);
|
if (data == null) {
|
return null;
|
}
|
String json = objectMapper.writeValueAsString(data);
|
return objectMapper.readValue(json, MAP_TYPE);
|
} catch (Exception e) {
|
log.error("读取设备PLC状态失败: deviceId={}", deviceId, e);
|
return null;
|
}
|
}
|
|
/**
|
* 根据设备ID写入PLC字段
|
*
|
* @param deviceId 设备ID
|
* @param fieldValues 字段名->值 的Map
|
* @return 是否成功
|
*/
|
public boolean writeFieldsByDevice(Long deviceId, Map<String, Object> fieldValues) {
|
DeviceConfig device = deviceConfigService.getDeviceById(deviceId);
|
if (device == null) {
|
log.error("设备不存在: deviceId={}", deviceId);
|
return false;
|
}
|
|
try {
|
// 从设备配置中获取项目标识
|
String projectId = resolveProjectId(device);
|
|
// 获取对应的S7Serializer(使用设备配置)
|
EnhancedS7Serializer s7Serializer = getSerializerForDevice(device);
|
|
// 使用动态数据服务写入字段(基于DeviceConfig)
|
plcDynamicDataService.writePlcData(device, fieldValues, s7Serializer);
|
|
log.info("写入PLC字段成功: deviceId={}, projectId={}, fields={}", deviceId, projectId, fieldValues.keySet());
|
return true;
|
} catch (Exception e) {
|
log.error("写入PLC字段失败: deviceId={}", deviceId, e);
|
return false;
|
}
|
}
|
|
/**
|
* 获取设备对应的S7Serializer实例
|
*
|
* @param device 设备配置
|
* @return S7Serializer实例
|
*/
|
private EnhancedS7Serializer getSerializerForDevice(DeviceConfig device) {
|
String cacheKey = "device:" + (device.getId() != null ? device.getId() : resolveProjectId(device));
|
return serializerCache.computeIfAbsent(cacheKey, id -> {
|
// 解析PLC类型(仅取实体字段)
|
EPlcType plcType = EPlcType.S1200;
|
String plcTypeValue = device.getPlcType();
|
if (plcTypeValue == null || plcTypeValue.isEmpty()) {
|
log.warn("设备未配置PLC类型,使用默认类型S1200, deviceId={}", device.getId());
|
} else {
|
try {
|
plcType = EPlcType.valueOf(plcTypeValue);
|
} catch (IllegalArgumentException e) {
|
log.warn("未知的PLC类型: {}, 使用默认类型 S1200", plcTypeValue);
|
}
|
}
|
|
// 创建S7PLC实例(仅取实体字段)
|
String plcIp = device.getPlcIp();
|
if (plcIp == null || plcIp.isEmpty()) {
|
log.warn("设备未配置PLC IP,使用默认 192.168.10.21, deviceId={}", device.getId());
|
plcIp = "192.168.10.21";
|
}
|
S7PLC s7Plc = new S7PLC(plcType, plcIp);
|
|
// 创建并返回EnhancedS7Serializer实例
|
return EnhancedS7Serializer.newInstance(s7Plc);
|
});
|
}
|
|
private PlcAddress buildPlcAddressFromDevice(DeviceConfig device) {
|
Map<String, Object> plcConfig = getPlcConfigParams(device);
|
String dbArea = plcConfig.get("dbArea") != null ? String.valueOf(plcConfig.get("dbArea")) : "DB12";
|
int beginIndex = plcConfig.get("beginIndex") != null ? parseInteger(plcConfig.get("beginIndex")) : 0;
|
|
String plcIp = device.getPlcIp();
|
if (plcIp == null || plcIp.isEmpty()) {
|
log.warn("设备未配置PLC IP,使用默认 192.168.10.21, deviceId={}", device.getId());
|
plcIp = "192.168.10.21";
|
}
|
|
String plcType = device.getPlcType();
|
if (plcType == null || plcType.isEmpty()) {
|
log.warn("设备未配置PLC类型,使用默认S1200, deviceId={}", device.getId());
|
plcType = EPlcType.S1200.name();
|
}
|
|
String addressMapping = resolveAddressMapping(device);
|
|
PlcAddress config = new PlcAddress();
|
config.setProjectId(resolveProjectId(device));
|
config.setDbArea(dbArea);
|
config.setBeginIndex(beginIndex);
|
config.setPlcIp(plcIp);
|
config.setPlcType(plcType);
|
config.setAddressMapping(addressMapping);
|
return config;
|
}
|
|
private String resolveAddressMapping(DeviceConfig device) {
|
Map<String, Object> mapping = ConfigJsonHelper.parseToMap(device.getConfigJson(), objectMapper);
|
if (!mapping.isEmpty()) {
|
try {
|
return objectMapper.writeValueAsString(mapping);
|
} catch (Exception e) {
|
log.warn("序列化configJson字段映射失败, deviceId={}", device.getId(), e);
|
}
|
}
|
Map<String, Object> extraParams = parseExtraParams(device);
|
Object addressMapping = extraParams.get("addressMapping");
|
if (addressMapping instanceof String) {
|
return (String) addressMapping;
|
}
|
if (addressMapping != null) {
|
try {
|
return objectMapper.writeValueAsString(addressMapping);
|
} catch (Exception e) {
|
log.warn("序列化extraParams.addressMapping失败, deviceId={}", device.getId(), e);
|
}
|
}
|
throw new IllegalStateException("设备未配置PLC字段映射, deviceId=" + device.getId());
|
}
|
|
private Map<String, Object> parseExtraParams(DeviceConfig device) {
|
if (device.getExtraParams() == null || device.getExtraParams().trim().isEmpty()) {
|
return Collections.emptyMap();
|
}
|
try {
|
return objectMapper.readValue(device.getExtraParams(), MAP_TYPE);
|
} catch (Exception e) {
|
log.warn("解析设备extraParams失败, deviceId={}", device.getId(), e);
|
return Collections.emptyMap();
|
}
|
}
|
|
@SuppressWarnings("unchecked")
|
private Map<String, Object> getPlcConfigParams(DeviceConfig device) {
|
Map<String, Object> extraParams = parseExtraParams(device);
|
Object plcConfig = extraParams.get("plcConfig");
|
if (plcConfig instanceof Map) {
|
return (Map<String, Object>) plcConfig;
|
}
|
if (plcConfig instanceof String) {
|
try {
|
return objectMapper.readValue((String) plcConfig, MAP_TYPE);
|
} catch (Exception e) {
|
log.warn("解析extraParams.plcConfig失败, deviceId={}", device.getId(), e);
|
}
|
}
|
return Collections.emptyMap();
|
}
|
|
private int parseInteger(Object value) {
|
if (value instanceof Number) {
|
return ((Number) value).intValue();
|
}
|
try {
|
return Integer.parseInt(String.valueOf(value));
|
} catch (NumberFormatException ex) {
|
log.warn("无法解析整型值: {}", value);
|
return 0;
|
}
|
}
|
|
/**
|
* 从设备配置中解析项目标识
|
*
|
* @param device 设备配置
|
* @return 项目标识
|
*/
|
private String resolveProjectId(DeviceConfig device) {
|
if (device == null) {
|
throw new IllegalArgumentException("设备信息为空");
|
}
|
|
// 1. 优先使用实体上的projectId
|
if (device.getProjectId() != null) {
|
return String.valueOf(device.getProjectId());
|
}
|
|
// 2. 从extraParams中读取
|
Map<String, Object> extraParams = parseExtraParams(device);
|
Object plcProjectId = extraParams.get("plcProjectId");
|
if (plcProjectId != null) {
|
return String.valueOf(plcProjectId);
|
}
|
|
// 3. 兼容旧结构:configJson或extraParams内嵌
|
Map<String, Object> configParams = ConfigJsonHelper.parseToMap(device.getConfigJson(), objectMapper);
|
Object legacyProjectId = configParams.get("plcProjectId");
|
if (legacyProjectId != null) {
|
return String.valueOf(legacyProjectId);
|
}
|
|
// 最后使用设备编号
|
if (device.getDeviceCode() != null && !device.getDeviceCode().isEmpty()) {
|
return device.getDeviceCode();
|
}
|
|
throw new IllegalStateException("无法解析设备的PLC项目标识, deviceId=" + device.getId());
|
}
|
}
|