001/* 002 * Copyright (C) Photon Vision. 003 * 004 * This program is free software: you can redistribute it and/or modify 005 * it under the terms of the GNU General Public License as published by 006 * the Free Software Foundation, either version 3 of the License, or 007 * (at your option) any later version. 008 * 009 * This program is distributed in the hope that it will be useful, 010 * but WITHOUT ANY WARRANTY; without even the implied warranty of 011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 012 * GNU General Public License for more details. 013 * 014 * You should have received a copy of the GNU General Public License 015 * along with this program. If not, see <https://www.gnu.org/licenses/>. 016 */ 017 018package org.photonvision.common.scripting; 019 020import java.io.IOException; 021import java.nio.file.Files; 022import java.nio.file.Path; 023import java.nio.file.Paths; 024import java.util.ArrayList; 025import java.util.List; 026import java.util.concurrent.LinkedBlockingDeque; 027import org.photonvision.common.hardware.Platform; 028import org.photonvision.common.logging.LogGroup; 029import org.photonvision.common.logging.Logger; 030import org.photonvision.common.util.TimedTaskManager; 031import org.photonvision.common.util.file.JacksonUtils; 032 033public class ScriptManager { 034 private static final Logger logger = new Logger(ScriptManager.class, LogGroup.General); 035 036 private ScriptManager() {} 037 038 private static final List<ScriptEvent> events = new ArrayList<>(); 039 private static final LinkedBlockingDeque<ScriptEventType> queuedEvents = 040 new LinkedBlockingDeque<>(25); 041 042 public static void initialize() { 043 ScriptConfigManager.initialize(); 044 if (ScriptConfigManager.fileExists()) { 045 for (ScriptConfig scriptConfig : ScriptConfigManager.loadConfig()) { 046 ScriptEvent scriptEvent = new ScriptEvent(scriptConfig); 047 events.add(scriptEvent); 048 } 049 050 TimedTaskManager.getInstance().addTask("ScriptRunner", new ScriptRunner(), 10); 051 052 } else { 053 logger.error("Something went wrong initializing scripts! Events will not run."); 054 } 055 } 056 057 private static class ScriptRunner implements Runnable { 058 @Override 059 public void run() { 060 try { 061 handleEvent(queuedEvents.takeFirst()); 062 } catch (InterruptedException e) { 063 logger.error("ScriptRunner queue interrupted!", e); 064 } 065 } 066 067 private void handleEvent(ScriptEventType eventType) { 068 var toRun = 069 events.parallelStream() 070 .filter(e -> e.config.eventType == eventType) 071 .findFirst() 072 .orElse(null); 073 if (toRun != null) { 074 try { 075 toRun.run(); 076 } catch (IOException e) { 077 logger.error("Failed to run script for event \"" + eventType.name() + "\"", e); 078 } 079 } 080 } 081 } 082 083 protected static class ScriptConfigManager { 084 // protected static final Path scriptConfigPath = 085 // Paths.get(ConfigManager.SettingsPath.toString(), "scripts.json"); 086 static final Path scriptConfigPath = Paths.get(""); // TODO: Waiting on config 087 088 private ScriptConfigManager() {} 089 090 static boolean fileExists() { 091 return Files.exists(scriptConfigPath); 092 } 093 094 public static void initialize() { 095 if (!fileExists()) { 096 List<ScriptConfig> eventsConfig = new ArrayList<>(); 097 for (var eventType : ScriptEventType.values()) { 098 eventsConfig.add(new ScriptConfig(eventType)); 099 } 100 101 try { 102 JacksonUtils.serialize(scriptConfigPath, eventsConfig.toArray(new ScriptConfig[0]), true); 103 } catch (IOException e) { 104 logger.error("Failed to initialize!", e); 105 } 106 } 107 } 108 109 static List<ScriptConfig> loadConfig() { 110 try { 111 var raw = JacksonUtils.deserialize(scriptConfigPath, ScriptConfig[].class); 112 if (raw != null) { 113 return List.of(raw); 114 } 115 } catch (IOException e) { 116 logger.error("Failed to load scripting config!", e); 117 } 118 return new ArrayList<>(); 119 } 120 121 protected static void deleteConfig() { 122 try { 123 Files.delete(scriptConfigPath); 124 } catch (IOException e) { 125 // 126 } 127 } 128 } 129 130 public static void queueEvent(ScriptEventType eventType) { 131 if (Platform.isLinux()) { 132 try { 133 queuedEvents.putLast(eventType); 134 logger.info("Queued event: " + eventType.name()); 135 } catch (InterruptedException e) { 136 logger.error("Failed to add event to queue: " + eventType.name(), e); 137 } 138 } 139 } 140}