/*
* FirmwareUtils.java
*
* Created on April 2, 2013
*/
/*
Copywrite 2012-2016 Will Winder
This file is part of Universal Gcode Sender (UGS).
UGS is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
UGS is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with UGS. If not, see <http://www.gnu.org/licenses/>.
*/
package com.willwinder.universalgcodesender.utils;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonIOException;
import com.google.gson.JsonObject;
import com.google.gson.JsonSyntaxException;
import com.willwinder.universalgcodesender.AbstractController;
import com.willwinder.universalgcodesender.gcode.processors.ICommandProcessor;
import com.willwinder.universalgcodesender.i18n.Localization;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URI;
import java.nio.file.FileSystem;
import java.nio.file.FileSystemNotFoundException;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.stream.Stream;
import javax.swing.JOptionPane;
import org.apache.commons.io.FileUtils;
/**
*
* @author wwinder
*/
public class FirmwareUtils {
private static final Logger logger = Logger.getLogger(FirmwareUtils.class.getName());
final private static String FIRMWARE_CONFIG_DIRNAME = "firmware_config";
/**
* Need a simple way to map the config loader (JSON in POJO format) to the
* file it was generated from.
*/
public static class ConfigTuple {
public ControllerSettings loader;
public File file;
public ConfigTuple(ControllerSettings l, File f) {
this.loader = l;
this.file = f;
}
public void reload() {
try {
loader = new Gson().fromJson(new FileReader(file), ControllerSettings.class);
} catch (FileNotFoundException ex) {
logger.log(Level.SEVERE, null, ex);
}
}
}
private static Map<String,ConfigTuple> configFiles = new HashMap<>();
public static Map<String,ConfigTuple> getConfigFiles() {
return configFiles;
}
static {
initialize();
}
public static ArrayList<String> getFirmwareList() {
ArrayList<String> ret = new ArrayList<>();
for (String fw : configFiles.keySet()) {
ret.add(fw);
}
return ret;
}
/**
* Gets a list of command processors initialized with user settings.
*/
public static Optional<List<ICommandProcessor>> getParserFor(String firmware, Settings settings) {
if (!configFiles.containsKey(firmware)) {
return Optional.empty();
}
try {
return Optional.of(configFiles.get(firmware).loader.getProcessors());
} catch (Exception e) {
return Optional.empty();
}
}
public static void addPatternRemoverForFirmware(String firmware, String pattern) throws IOException {
if (!configFiles.containsKey(firmware)) {
return;
}
ConfigTuple tuple = configFiles.get(firmware);
JsonObject args = new JsonObject();
args.addProperty("pattern", pattern);
tuple.loader.GcodeProcessors.Custom.add(
new ControllerSettings.ProcessorConfig("PatternRemover",
true, true, args));
save(tuple.file, tuple.loader);
}
/**
* Gets a new controller object from a firmware config.
* @param firmware
* @return
*/
public static Optional<AbstractController> getControllerFor(String firmware) {
if (!configFiles.containsKey(firmware)) {
return Optional.empty();
}
/*
ConfigLoader config = new Gson().fromJson(new FileReader(configFiles.get(firmware).configFile), ConfigLoader.class);
File f = configFiles.get(firmware).configFile;
File next = new File(f.getParent(), f.getName() + ".out");
try (FileWriter fileWriter = new FileWriter(next)) {
Gson gson = new GsonBuilder().setPrettyPrinting().create();
fileWriter.write(gson.toJson(config, ConfigLoader2.class));
} catch (IOException ex) {
logger.log(Level.SEVERE, null, ex);
}
System.out.println("Have config: " + config.toString());
*/
return Optional.of(configFiles.get(firmware).loader.getController());
}
/**
* Deletes firmware_config file from the machine then recreate it.
*/
public static void restoreDefaults(String firmware) throws IOException {
ConfigTuple tuple = configFiles.get(firmware);
configFiles.remove(firmware);
tuple.file.delete();
// Reload missing file.
initialize();
}
private static ControllerSettings getSettingsForStream(InputStream is)
throws IOException {
try(BufferedReader br = new BufferedReader(new InputStreamReader(is))) {
return new Gson().fromJson(br, ControllerSettings.class);
}
}
/**
* Copy any missing files from the the jar's resources/firmware_config/ dir
* into the settings/firmware_config dir.
*/
static boolean userNotified = false;
static boolean overwriteOldFiles = false;
public synchronized static void initialize() {
System.out.println("Initializing firmware... ...");
File firmwareConfig = new File(SettingsFactory.getSettingsDirectory(),
FIRMWARE_CONFIG_DIRNAME);
// Create directory if it's missing.
if (!firmwareConfig.exists()) {
firmwareConfig.mkdirs();
}
FileSystem fileSystem = null;
// Copy firmware config files.
try {
final String dir = "/resources/firmware_config/";
URI location = FirmwareUtils.class.getResource(dir).toURI();
Path myPath;
if (location.getScheme().equals("jar")) {
try {
// In case the filesystem already exists.
fileSystem = FileSystems.getFileSystem(location);
} catch (FileSystemNotFoundException e) {
// Otherwise create the new filesystem.
fileSystem = FileSystems.newFileSystem(location,
Collections.<String, String>emptyMap());
}
myPath = fileSystem.getPath(dir);
} else {
myPath = Paths.get(location);
}
Stream<Path> files = Files.walk(myPath, 1);
for (Path path : (Iterable<Path>) () -> files.iterator()) {
System.out.println(path);
final String name = path.getFileName().toString();
File fwConfig = new File(firmwareConfig, name);
if (name.endsWith(".json")) {
boolean copyFile = !fwConfig.exists();
ControllerSettings jarSetting =
getSettingsForStream(Files.newInputStream(path));
// If the file is outdated... ask the user (once).
if (fwConfig.exists()) {
ControllerSettings current =
getSettingsForStream(new FileInputStream(fwConfig));
boolean outOfDate =
current.getVersion() < jarSetting.getVersion();
if (outOfDate && !userNotified && !overwriteOldFiles) {
int result = NarrowOptionPane.showNarrowConfirmDialog(
200,
Localization.getString("settings.file.outOfDate.message"),
Localization.getString("settings.file.outOfDate.title"),
JOptionPane.YES_NO_OPTION,
JOptionPane.QUESTION_MESSAGE);
overwriteOldFiles = result == JOptionPane.OK_OPTION;
userNotified = true;
}
if (overwriteOldFiles) {
copyFile = true;
jarSetting.getProcessorConfigs().Custom
= current.getProcessorConfigs().Custom;
}
}
// Copy file from jar to firmware_config directory.
if (copyFile) {
try {
save(fwConfig, jarSetting);
} catch (IOException ex) {
logger.log(Level.SEVERE, null, ex);
}
}
}
}
} catch (Exception ex) {
String errorMessage = String.format("%s %s",
Localization.getString("settings.file.generalError"),
ex.getLocalizedMessage());
GUIHelpers.displayErrorDialog(errorMessage);
logger.log(Level.SEVERE, errorMessage, ex);
} finally {
if (fileSystem != null) {
try {
fileSystem.close();
} catch (IOException ex) {
logger.log(Level.SEVERE, "Problem closing filesystem.", ex);
}
}
}
configFiles.clear();
for (File f : firmwareConfig.listFiles()) {
try {
ControllerSettings config = new Gson().fromJson(new FileReader(f), ControllerSettings.class);
//ConfigLoader config = new ConfigLoader(f);
configFiles.put(config.getName(), new ConfigTuple(config, f));
} catch (FileNotFoundException | JsonSyntaxException | JsonIOException ex) {
GUIHelpers.displayErrorDialog("Unable to load configuration files: " + f.getAbsolutePath());
}
}
}
public static void save(File f, ControllerSettings cs) throws IOException {
if (f.exists()) {
f.delete();
}
Gson gson = new GsonBuilder().setPrettyPrinting().create();
String json = gson.toJson(cs, ControllerSettings.class);
FileUtils.writeStringToFile(f, json);
}
}