package de.opcua.app.service; import de.opcua.app.model.NodeAction; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; import java.io.File; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Timer; import java.util.TimerTask; /** * Persists Actions to JSON file. */ public class ActionPersistenceService { private static final String CONFIG_DIR = System.getProperty("user.home") + "/.opcua-gui"; private static final String ACTIONS_FILE = CONFIG_DIR + "/actions.json"; private final ObjectMapper mapper; public ActionPersistenceService() { this.mapper = new ObjectMapper(); this.mapper.enable(SerializationFeature.INDENT_OUTPUT); ensureConfigDir(); } private void ensureConfigDir() { try { Path dir = Paths.get(CONFIG_DIR); if (!Files.exists(dir)) { Files.createDirectories(dir); System.out.println("[Persistence] Created config directory: " + CONFIG_DIR); } } catch (IOException e) { System.err.println("[Persistence] Failed to create config directory: " + e.getMessage()); } } public void saveActions(Map> actions) { try { List dataList = new ArrayList<>(); for (Map.Entry> entry : actions.entrySet()) { String nodeId = entry.getKey(); for (NodeAction action : entry.getValue()) { ActionData data = new ActionData( nodeId, action.getActionName(), action.getTriggerType().name(), action.getScript(), action.getTriggerValue(), action.getIntervalMs(), action.isEnabled() ); data.conditionEnabled = action.isConditionEnabled(); data.conditionScript = action.getConditionScript(); data.conditionNodeIds = new ArrayList<>(action.getConditionNodeIds()); data.nodeAliases = new LinkedHashMap<>(action.getNodeAliases()); dataList.add(data); } } mapper.writeValue(new File(ACTIONS_FILE), dataList); System.out.println("[Persistence] Saved " + dataList.size() + " actions to " + ACTIONS_FILE); } catch (IOException e) { System.err.println("[Persistence] Failed to save actions: " + e.getMessage()); } } public Map> loadActions() { Map> result = new HashMap<>(); File file = new File(ACTIONS_FILE); if (!file.exists()) { System.out.println("[Persistence] No saved actions found"); return result; } try { ActionData[] dataArray = mapper.readValue(file, ActionData[].class); for (ActionData data : dataArray) { NodeAction action = new NodeAction(); action.setNodeId(data.nodeId); action.setActionName(data.actionName); action.setTriggerType(NodeAction.TriggerType.valueOf(data.triggerType)); action.setScript(data.script); action.setTriggerValue(data.triggerValue); action.setIntervalMs(data.intervalMs); action.setEnabled(data.enabled); action.setConditionEnabled(data.conditionEnabled); action.setConditionScript(data.conditionScript); action.setConditionNodeIds(data.conditionNodeIds); action.setNodeAliases(data.nodeAliases); result.computeIfAbsent(data.nodeId, k -> new ArrayList<>()).add(action); } System.out.println("[Persistence] Loaded " + dataArray.length + " actions from " + ACTIONS_FILE); } catch (IOException e) { System.err.println("[Persistence] Failed to load actions: " + e.getMessage()); } return result; } public void startAutoSave(ActionService actionService, int intervalSeconds) { Timer timer = new Timer("ActionAutoSave", true); timer.scheduleAtFixedRate(new TimerTask() { @Override public void run() { saveActions(actionService.getAllActions()); } }, intervalSeconds * 1000L, intervalSeconds * 1000L); System.out.println("[Persistence] Auto-save enabled (every " + intervalSeconds + "s)"); } public static class ActionData { public String nodeId; public String actionName; public String triggerType; public String script; public String triggerValue; public int intervalMs; public boolean enabled; public boolean conditionEnabled; public String conditionScript; public List conditionNodeIds = new ArrayList<>(); public Map nodeAliases = new LinkedHashMap<>(); public ActionData() {} public ActionData(String nodeId, String actionName, String triggerType, String script, String triggerValue, int intervalMs, boolean enabled) { this.nodeId = nodeId; this.actionName = actionName; this.triggerType = triggerType; this.script = script; this.triggerValue = triggerValue; this.intervalMs = intervalMs; this.enabled = enabled; } } }