diff --git a/chameleon-server/chameleon-vision.iml b/chameleon-server/chameleon-vision.iml
index 445efaa82..41084fba0 100644
--- a/chameleon-server/chameleon-vision.iml
+++ b/chameleon-server/chameleon-vision.iml
@@ -11,7 +11,6 @@
-
diff --git a/chameleon-server/pom.xml b/chameleon-server/pom.xml
index 48a596f1f..94b7b4e83 100644
--- a/chameleon-server/pom.xml
+++ b/chameleon-server/pom.xml
@@ -6,7 +6,7 @@
org.chameleon-vision.main
chameleon-vision
- 2.3.1-BUGFIX
+ 2.3.2
diff --git a/chameleon-server/src/main/java/com/chameleonvision/Main.java b/chameleon-server/src/main/java/com/chameleonvision/Main.java
index 7c97b9fe9..08d68a714 100644
--- a/chameleon-server/src/main/java/com/chameleonvision/Main.java
+++ b/chameleon-server/src/main/java/com/chameleonvision/Main.java
@@ -129,6 +129,7 @@ public class Main {
}
// Attempt to load the JNI Libraries
+ System.out.println("Loading CameraServer...");
try {
CameraServerJNI.forceLoad();
CameraServerCvJNI.forceLoad();
@@ -139,9 +140,11 @@ public class Main {
throw new RuntimeException("Failed to load JNI Libraries!");
}
+ System.out.println("Checking Settings...");
ConfigManager.initializeSettings();
if (!CurrentPlatform.isWindows()) {
+ System.out.println("Initializing Script Manager...");
ScriptManager.initialize();
} else {
System.out.println("Scripts not yet supported on Windows. ScriptEvents will be ignored.");
@@ -159,16 +162,17 @@ public class Main {
boolean visionSourcesOk = VisionManager.initializeSources();
if (!visionSourcesOk) {
- System.out.println("No cameras connected!");
+ System.err.println("No cameras connected!");
return;
}
boolean visionProcessesOk = VisionManager.initializeProcesses();
if (!visionProcessesOk) {
- System.err.println("Failed to start threads!");
+ System.err.println("Failed to initialize vision processes!");
return;
}
+ System.out.println("Starting vision processes...");
VisionManager.startProcesses();
System.out.printf("Starting Web server at port %d\n", uiPort);
diff --git a/chameleon-server/src/main/java/com/chameleonvision/config/CameraConfig.java b/chameleon-server/src/main/java/com/chameleonvision/config/CameraConfig.java
index b4176e3fa..7c5b1e0e7 100644
--- a/chameleon-server/src/main/java/com/chameleonvision/config/CameraConfig.java
+++ b/chameleon-server/src/main/java/com/chameleonvision/config/CameraConfig.java
@@ -84,7 +84,7 @@ public class CameraConfig {
void saveConfig(CameraJsonConfig config) {
try {
- JacksonHelper.serializer(configPath, config);
+ JacksonHelper.serializer(configPath, config, true);
FileHelper.setFilePerms(configPath);
} catch (IOException e) {
System.err.println("Failed to save camera config file: " + configPath.toString());
@@ -97,7 +97,7 @@ public class CameraConfig {
public void saveDriverMode(CVPipelineSettings driverMode) {
try {
- JacksonHelper.serializer(driverModePath, driverMode);
+ JacksonHelper.serializer(driverModePath, driverMode, true);
FileHelper.setFilePerms(driverModePath);
} catch (IOException e) {
System.err.println("Failed to save camera drivermode file: " + driverModePath.toString());
@@ -108,7 +108,7 @@ public class CameraConfig {
public void saveCalibration(List cal) {
CameraCalibrationConfig[] configs = cal.toArray(new CameraCalibrationConfig[0]);
try {
- JacksonHelper.serializer(calibrationPath, configs);
+ JacksonHelper.serializer(calibrationPath, configs, true);
FileHelper.setFilePerms(calibrationPath);
} catch (IOException e) {
System.err.println("Failed to save camera calibration file: " + calibrationPath.toString());
@@ -122,7 +122,7 @@ public class CameraConfig {
System.err.println("Failed to create camera config folder: " + configFolderPath.toString());
}
FileHelper.setFilePerms(configFolderPath);
- } catch(Exception e) {
+ } catch (Exception e) {
System.err.println("Failed to create camera config folder: " + configFolderPath.toString());
}
}
@@ -131,7 +131,7 @@ public class CameraConfig {
private void checkConfig() {
if (!configExists()) {
try {
- JacksonHelper.serializer(configPath, preliminaryConfig);
+ JacksonHelper.serializer(configPath, preliminaryConfig, true);
FileHelper.setFilePerms(configPath);
} catch (IOException e) {
System.err.println("Failed to create camera config file: " + configPath.toString());
@@ -144,7 +144,7 @@ public class CameraConfig {
try {
CVPipelineSettings newDriverModeSettings = new CVPipelineSettings();
newDriverModeSettings.nickname = "DRIVERMODE";
- JacksonHelper.serializer(driverModePath, newDriverModeSettings);
+ JacksonHelper.serializer(driverModePath, newDriverModeSettings, true);
FileHelper.setFilePerms(driverModePath);
} catch (IOException e) {
System.err.println("Failed to create camera drivermode file: " + driverModePath.toString());
@@ -156,7 +156,7 @@ public class CameraConfig {
if (!calibrationExists()) {
try {
List calibrations = new ArrayList<>();
- JacksonHelper.serializer(calibrationPath, calibrations.toArray());
+ JacksonHelper.serializer(calibrationPath, calibrations.toArray(), true);
} catch (IOException e) {
System.err.println("Failed to create camera calibration file: " + calibrationPath.toString());
}
diff --git a/chameleon-server/src/main/java/com/chameleonvision/config/ConfigManager.java b/chameleon-server/src/main/java/com/chameleonvision/config/ConfigManager.java
index fd0f5dc5d..5acc18625 100644
--- a/chameleon-server/src/main/java/com/chameleonvision/config/ConfigManager.java
+++ b/chameleon-server/src/main/java/com/chameleonvision/config/ConfigManager.java
@@ -13,7 +13,8 @@ import java.util.LinkedHashMap;
import java.util.List;
public class ConfigManager {
- private ConfigManager() {}
+ private ConfigManager() {
+ }
public static final Path SettingsPath = Paths.get(ProgramDirectoryUtilities.getProgramDirectory(), "settings");
private static final Path settingsFilePath = Paths.get(SettingsPath.toString(), "settings.json");
@@ -22,13 +23,18 @@ public class ConfigManager {
public static GeneralSettings settings = new GeneralSettings();
- private static boolean settingsFolderExists() { return Files.exists(SettingsPath); }
- private static boolean settingsFileExists() { return settingsFolderExists() && Files.exists(settingsFilePath); }
+ private static boolean settingsFolderExists() {
+ return Files.exists(SettingsPath);
+ }
+
+ private static boolean settingsFileExists() {
+ return settingsFolderExists() && Files.exists(settingsFilePath);
+ }
private static void checkSettingsFolder() {
if (!settingsFolderExists()) {
try {
- if( !(new File(SettingsPath.toUri()).mkdirs()) ) {
+ if (!(new File(SettingsPath.toUri()).mkdirs())) {
System.err.println("Failed to create settings folder: " + SettingsPath.toString());
}
Files.createDirectory(SettingsPath);
@@ -36,7 +42,7 @@ public class ConfigManager {
new ShellExec().executeBashCommand("sudo chmod -R 0777 " + SettingsPath.toString());
}
} catch (IOException e) {
- if(!(e instanceof java.nio.file.FileAlreadyExistsException))
+ if (!(e instanceof java.nio.file.FileAlreadyExistsException))
e.printStackTrace();
}
}
@@ -46,7 +52,7 @@ public class ConfigManager {
boolean settingsFileEmpty = settingsFileExists() && new File(settingsFilePath.toString()).length() == 0;
if (settingsFileEmpty || !settingsFileExists()) {
try {
- JacksonHelper.serializer(settingsFilePath, settings);
+ JacksonHelper.serializer(settingsFilePath, settings, true);
FileHelper.setFilePerms(settingsFilePath);
} catch (IOException e) {
e.printStackTrace();
@@ -69,7 +75,7 @@ public class ConfigManager {
private static void saveSettingsFile() {
try {
- JacksonHelper.serializer(settingsFilePath, settings);
+ JacksonHelper.serializer(settingsFilePath, settings, true);
FileHelper.setFilePerms(settingsFilePath);
} catch (IOException e) {
System.err.println("Failed to save settings.json!");
diff --git a/chameleon-server/src/main/java/com/chameleonvision/config/PipelineConfig.java b/chameleon-server/src/main/java/com/chameleonvision/config/PipelineConfig.java
index 256c6b784..2b7b2074e 100644
--- a/chameleon-server/src/main/java/com/chameleonvision/config/PipelineConfig.java
+++ b/chameleon-server/src/main/java/com/chameleonvision/config/PipelineConfig.java
@@ -21,6 +21,7 @@ public class PipelineConfig {
/**
* Construct a new PipelineConfig
+ *
* @param cameraConfig the CameraConfig (parent folder, kinda?)
*/
PipelineConfig(CameraConfig cameraConfig) {
@@ -28,7 +29,7 @@ public class PipelineConfig {
}
private void checkFolder() {
- if ( !(new File(cameraConfig.pipelineFolderPath.toUri()).mkdirs())) {
+ if (!(new File(cameraConfig.pipelineFolderPath.toUri()).mkdirs())) {
if (Files.notExists(cameraConfig.pipelineFolderPath)) {
System.err.println("Failed to create pipelines folder.");
}
@@ -46,7 +47,7 @@ public class PipelineConfig {
private boolean folderHasPipelines() {
File[] folderContents = getPipelineFiles();
- if(folderContents == null) return false;
+ if (folderContents == null) return false;
return folderContents.length > 0;
}
@@ -75,14 +76,14 @@ public class PipelineConfig {
if (settings instanceof StandardCVPipelineSettings) {
try {
- JacksonHelper.serialize(path, (StandardCVPipelineSettings)settings, StandardCVPipelineSettings.class, new StandardCVPipelineSettingsSerializer());
+ JacksonHelper.serialize(path, (StandardCVPipelineSettings) settings, StandardCVPipelineSettings.class, new StandardCVPipelineSettingsSerializer(), true);
FileHelper.setFilePerms(path);
} catch (IOException e) {
e.printStackTrace();
}
} else {
try {
- JacksonHelper.serializer(path, settings);
+ JacksonHelper.serializer(path, settings, true);
FileHelper.setFilePerms(path);
} catch (IOException e) {
e.printStackTrace();
@@ -91,13 +92,13 @@ public class PipelineConfig {
}
public void save(List settings) {
- for(CVPipelineSettings setting : settings) {
+ for (CVPipelineSettings setting : settings) {
save(setting);
}
}
public void delete(CVPipelineSettings setting) {
- if(pipelineExists(setting)) {
+ if (pipelineExists(setting)) {
try {
Files.delete(getPipelinePath(setting));
} catch (IOException e) {
@@ -124,17 +125,17 @@ public class PipelineConfig {
File[] pipelineFiles = getPipelineFiles();
List deserializedList = new ArrayList<>();
- if(pipelineFiles == null || pipelineFiles.length < 1) {
+ if (pipelineFiles == null || pipelineFiles.length < 1) {
// TODO handle no pipelines to load
System.err.println("no pipes to load! loading default");
} else {
- for(File pipelineFile : pipelineFiles) {
- try {
- var pipe = JacksonHelper.deserialize(Paths.get(pipelineFile.getPath()), StandardCVPipelineSettings.class, new StandardCVPipelineSettingsDeserializer());
- deserializedList.add(pipe);
- } catch (IOException e) {
- System.err.println("couldn't load cvpipeline2d");
- }
+ for (File pipelineFile : pipelineFiles) {
+ try {
+ var pipe = JacksonHelper.deserialize(Paths.get(pipelineFile.getPath()), StandardCVPipelineSettings.class, new StandardCVPipelineSettingsDeserializer());
+ deserializedList.add(pipe);
+ } catch (IOException e) {
+ System.err.println("couldn't load cvpipeline2d");
+ }
}
}
diff --git a/chameleon-server/src/main/java/com/chameleonvision/networktables/NetworkTablesManager.java b/chameleon-server/src/main/java/com/chameleonvision/networktables/NetworkTablesManager.java
index de020466c..aab2df65c 100644
--- a/chameleon-server/src/main/java/com/chameleonvision/networktables/NetworkTablesManager.java
+++ b/chameleon-server/src/main/java/com/chameleonvision/networktables/NetworkTablesManager.java
@@ -13,7 +13,7 @@ public class NetworkTablesManager {
private NetworkTablesManager() {}
- private static final NetworkTableInstance NTInst = NetworkTableInstance.getDefault();
+ private static final NetworkTableInstance ntInstance = NetworkTableInstance.getDefault();
public static final String kRootTableName = "/chameleon-vision";
public static final NetworkTable kRootTable = NetworkTableInstance.getDefault().getTable(kRootTableName);
@@ -48,11 +48,16 @@ public class NetworkTablesManager {
public static void setClientMode(String host) {
isServer = false;
System.out.println("Starting NT Client");
- NTInst.stopServer();
+ ntInstance.stopServer();
if (host != null) {
- NTInst.startClient(host);
+ ntInstance.startClient(host);
} else {
- NTInst.startClientTeam(getTeamNumber());
+ ntInstance.startClientTeam(getTeamNumber());
+ if(ntInstance.isConnected()) {
+ System.out.println("[NetworkTablesManager] Connected to the robot!");
+ } else {
+ System.out.println("[NetworkTablesManager] Could NOT to the robot! Will retry in the background...");
+ }
}
}
@@ -63,7 +68,7 @@ public class NetworkTablesManager {
public static void setServerMode() {
isServer = true;
System.out.println("Starting NT Server");
- NTInst.stopClient();
- NTInst.startServer();
+ ntInstance.stopClient();
+ ntInstance.startServer();
}
}
diff --git a/chameleon-server/src/main/java/com/chameleonvision/scripting/ScriptManager.java b/chameleon-server/src/main/java/com/chameleonvision/scripting/ScriptManager.java
index f85e3fb61..0e2633658 100644
--- a/chameleon-server/src/main/java/com/chameleonvision/scripting/ScriptManager.java
+++ b/chameleon-server/src/main/java/com/chameleonvision/scripting/ScriptManager.java
@@ -16,7 +16,8 @@ import java.util.concurrent.LinkedBlockingDeque;
public class ScriptManager {
- private ScriptManager() {}
+ private ScriptManager() {
+ }
private static final List events = new ArrayList<>();
private static final LinkedBlockingDeque queuedEvents = new LinkedBlockingDeque<>(25);
@@ -67,9 +68,12 @@ public class ScriptManager {
protected static final Path scriptConfigPath = Paths.get(ConfigManager.SettingsPath.toString(), "scripts.json");
- private ScriptConfigManager() {}
+ private ScriptConfigManager() {
+ }
- static boolean fileExists() { return Files.exists(scriptConfigPath); }
+ static boolean fileExists() {
+ return Files.exists(scriptConfigPath);
+ }
public static void initialize() {
if (!fileExists()) {
@@ -79,7 +83,7 @@ public class ScriptManager {
}
try {
- JacksonHelper.serializer(scriptConfigPath, eventsConfig.toArray(new ScriptConfig[0]));
+ JacksonHelper.serializer(scriptConfigPath, eventsConfig.toArray(new ScriptConfig[0]), true);
} catch (IOException e) {
e.printStackTrace();
}
diff --git a/chameleon-server/src/main/java/com/chameleonvision/util/Helpers.java b/chameleon-server/src/main/java/com/chameleonvision/util/Helpers.java
index 646ae1f56..a4de4acce 100644
--- a/chameleon-server/src/main/java/com/chameleonvision/util/Helpers.java
+++ b/chameleon-server/src/main/java/com/chameleonvision/util/Helpers.java
@@ -27,6 +27,8 @@ public class Helpers {
"WantedBy=multi-user.target\n" +
"\n";
+
+
private Helpers() {
}
@@ -55,4 +57,5 @@ public class Helpers {
Process p = Runtime.getRuntime().exec("systemctl enable chameleonVision.service");
p.waitFor();
}
+
}
diff --git a/chameleon-server/src/main/java/com/chameleonvision/util/JacksonHelper.java b/chameleon-server/src/main/java/com/chameleonvision/util/JacksonHelper.java
index 65d4a55d0..da45fdaa9 100644
--- a/chameleon-server/src/main/java/com/chameleonvision/util/JacksonHelper.java
+++ b/chameleon-server/src/main/java/com/chameleonvision/util/JacksonHelper.java
@@ -9,16 +9,24 @@ import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.ser.std.StdSerializer;
import java.io.File;
+import java.io.FileDescriptor;
+import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.file.Path;
public class JacksonHelper {
- private JacksonHelper() {} // no construction, utility class
+ private JacksonHelper() {
+ } // no construction, utility class
public static void serializer(Path path, T object) throws IOException {
+ serializer(path, object, false);
+ }
+
+ public static void serializer(Path path, T object, boolean forceSync) throws IOException {
PolymorphicTypeValidator ptv = BasicPolymorphicTypeValidator.builder().allowIfBaseType(object.getClass()).build();
ObjectMapper objectMapper = JsonMapper.builder().activateDefaultTyping(ptv, ObjectMapper.DefaultTyping.JAVA_LANG_OBJECT).build();
- objectMapper.writerWithDefaultPrettyPrinter().writeValue(new File(path.toString()), object);
+ String json = objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(object);
+ saveJsonString(json, path, forceSync);
}
public static T deserialize(Path path, Class ref) throws IOException {
@@ -43,13 +51,27 @@ public class JacksonHelper {
}
return null;
}
-
public static void serialize(Path path, T object, Class ref, StdSerializer serializer) throws IOException {
+ serialize(path, object, ref, serializer, false);
+ }
+
+ public static void serialize(Path path, T object, Class ref, StdSerializer serializer, boolean forceSync) throws IOException {
ObjectMapper objectMapper = new ObjectMapper();
SimpleModule module = new SimpleModule();
module.addSerializer(ref, serializer);
objectMapper.registerModule(module);
+ String json = objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(object);
+ saveJsonString(json, path, forceSync);
+ }
- objectMapper.writerWithDefaultPrettyPrinter().writeValue(new File(path.toString()), object);
+ private static void saveJsonString(String json, Path path, boolean forceSync) throws IOException {
+ FileOutputStream fileOutputStream = new FileOutputStream(path.toFile());
+ fileOutputStream.write(json.getBytes());
+ fileOutputStream.flush();
+ if (forceSync) {
+ FileDescriptor fileDescriptor = fileOutputStream.getFD();
+ fileDescriptor.sync();
+ }
+ fileOutputStream.close();
}
}
diff --git a/chameleon-server/src/main/java/com/chameleonvision/vision/VisionManager.java b/chameleon-server/src/main/java/com/chameleonvision/vision/VisionManager.java
index fdf464a13..b37c58b0f 100644
--- a/chameleon-server/src/main/java/com/chameleonvision/vision/VisionManager.java
+++ b/chameleon-server/src/main/java/com/chameleonvision/vision/VisionManager.java
@@ -57,6 +57,7 @@ public class VisionManager {
if (usbCameraInfosByCameraName.isEmpty()) {
return false;
}
+ System.out.printf("[VisionManager] Found %s cameras!\n", usbCameraInfosByCameraName.size());
// load the config
List preliminaryConfigs = new ArrayList<>();
@@ -74,7 +75,7 @@ public class VisionManager {
});
loadedCameraConfigs.addAll(ConfigManager.initializeCameras(preliminaryConfigs));
-
+ System.out.printf("[VisionManager] Loaded %s cameras!\n", loadedCameraConfigs.size());
return true;
}
@@ -91,6 +92,8 @@ public class VisionManager {
}
currentUIVisionProcess = getVisionProcessByIndex(0);
ConfigManager.settings.currentCamera = visionProcesses.get(0).name;
+
+ System.out.printf("[VisionManager] Loaded %s vision processes! Current process: %s\n", visionProcesses.size(), visionProcesses.get(0).name);
return true;
}
diff --git a/chameleon-server/src/main/java/com/chameleonvision/vision/VisionProcess.java b/chameleon-server/src/main/java/com/chameleonvision/vision/VisionProcess.java
index f09019afc..bb0e474e7 100644
--- a/chameleon-server/src/main/java/com/chameleonvision/vision/VisionProcess.java
+++ b/chameleon-server/src/main/java/com/chameleonvision/vision/VisionProcess.java
@@ -86,18 +86,13 @@ public class VisionProcess {
}
public void start() {
- System.out.println("Starting NetworkTables.");
+ System.out.printf("[%s Process] Creating network table...\n", getCamera().getProperties().getNickname());
initNT(defaultTable);
- System.out.println("Starting vision thread.");
+ System.out.printf("[%s Process] Starting vision thread...\n", getCamera().getProperties().getNickname());
var visionThread = new Thread(visionRunnable);
visionThread.setName(getCamera().getProperties().name + " - Vision Thread");
visionThread.start();
-
-// System.out.println("Starting stream thread.");
-// var streamThread = new Thread(streamRunnable);
-// streamThread.setName(getCamera().getProperties().name + " - Stream Thread");
-// streamThread.start();
}
/**
@@ -299,7 +294,7 @@ public class VisionProcess {
public void addCalibration(CameraCalibrationConfig cal) {
cameraCapture.addCalibrationData(cal);
System.out.println("saving to file");
- fileConfig.saveCalibration(cameraCapture.getConfig());
+ fileConfig.saveCalibration(cameraCapture.getAllCalibrationData());
}
public void setIs3d(Boolean value) {
@@ -329,6 +324,9 @@ public class VisionProcess {
public void run() {
var lastUpdateTimeNanos = System.nanoTime();
var lastStreamTimeMs = System.currentTimeMillis();
+
+ System.out.printf("[%s Process] Vision Process Thread -- first run!\n", getCamera().getProperties().getNickname());
+
while (!Thread.interrupted()) {
// blocking call, will block until camera has a new frame.
@@ -357,14 +355,19 @@ public class VisionProcess {
try {
var currentTime = System.currentTimeMillis();
if ((currentTime - lastStreamTimeMs) / 1000d > 1.0 / 30.0) {
- cameraStreamer.runStream(lastPipelineResult.outputMat);
-// System.out.println("Ran stream in " + (System.currentTimeMillis() - currentTime) + "ms!");
- lastStreamTimeMs = currentTime;
- lastPipelineResult.outputMat.release();
+ if(lastPipelineResult != null) {
+ cameraStreamer.runStream(lastPipelineResult.outputMat);
+ lastStreamTimeMs = currentTime;
+ lastPipelineResult.outputMat.release();
+ } else {
+ System.err.printf("[%s Process] Last pipeline result was null!\n", getCamera().getProperties().getNickname());
+ }
}
} catch (Exception e) {
- Debug.printInfo("Vision running faster than stream.");
+// Debug.printInfo("Vision running faster than stream.");
+ System.err.printf("[%s Process] Exception in vision thread!\n", getCamera().getProperties().getNickname());
+ e.printStackTrace();
}
var deltaTimeNanos = System.nanoTime() - lastUpdateTimeNanos;
diff --git a/chameleon-server/src/main/java/com/chameleonvision/vision/camera/USBCameraCapture.java b/chameleon-server/src/main/java/com/chameleonvision/vision/camera/USBCameraCapture.java
index 1d8e4cb85..a881e81eb 100644
--- a/chameleon-server/src/main/java/com/chameleonvision/vision/camera/USBCameraCapture.java
+++ b/chameleon-server/src/main/java/com/chameleonvision/vision/camera/USBCameraCapture.java
@@ -58,10 +58,6 @@ public class USBCameraCapture implements CameraCapture {
calibrationList.add(newConfig);
}
- public List getConfig() {
- return calibrationList;
- }
-
@Override
public USBCaptureProperties getProperties() {
return properties;
diff --git a/chameleon-server/src/main/resources/readonly.sh b/chameleon-server/src/main/resources/readonly.sh
new file mode 100644
index 000000000..b214555c1
--- /dev/null
+++ b/chameleon-server/src/main/resources/readonly.sh
@@ -0,0 +1,129 @@
+#!/bin/bash
+
+function is_pi() {
+ ARCH=$(dpkg --print-architecture)
+ if [ "$ARCH" = "armhf" ] ; then
+ echo 0
+ else
+ echo 1
+ fi
+}
+
+function is_pione() {
+ if grep -q "^Revision\s*:\s*00[0-9a-fA-F][0-9a-fA-F]$" /proc/cpuinfo; then
+ echo 0
+ elif grep -q "^Revision\s*:\s*[ 123][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F]0[0-36][0-9a-fA-F]$" /proc/cpuinfo ; then
+ echo 0
+ else
+ echo 1
+ fi
+}
+
+function is_pitwo() {
+ if grep -q "^Revision\s*:\s*[ 123][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F]04[0-9a-fA-F]$" /proc/cpuinfo; then
+ echo 0
+ else
+ echo 1
+ fi
+}
+
+function is_pizero() {
+ if grep -q "^Revision\s*:\s*[ 123][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F]0[9cC][0-9a-fA-F]$" /proc/cpuinfo; then
+ echo 0
+ else
+ echo 1
+ fi
+}
+
+function is_pifour() {
+ if grep -q "^Revision\s*:\s*[ 123][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F]11[0-9a-fA-F]$" /proc/cpuinfo; then
+ echo 0
+ else
+ echo 1
+ fi
+}
+
+function get_pi_type() {
+ if [ $(is_pi) ]; then
+ if [ $(is_pione) -eq 0 ]; then
+ echo 1
+ elif [ $(is_pitwo) -eq 0 ]; then
+ echo 2
+ elif [ $(is_pizero) -eq 0 ]; then
+ echo 0
+ elif [ $(is_pifour) -eq 0 ]; then
+ echo 4
+ else
+ echo 3
+ fi
+ else
+ echo -1
+ fi
+}
+
+pi_type=$(get_pi_type)
+
+if [ $pi_type -ne 3 ] && [ $pi_type -ne 4 ]
+then
+ echo "This script is only for Raspberry Pi 3 and 4!"
+ exit 1
+fi
+
+if [[ $EUID -ne 0 ]]; then
+ echo "This script must be run as root"
+ exit 1
+fi
+
+echo -e "GET http://google.com HTTP/1.0\n\n" | nc google.com 80 > /dev/null 2>&1
+
+if [ $? -eq 0 ]; then
+ echo "Internet connection good! Proceding..."
+else
+ echo "Can't connect to the internet! Internet is needed for this operation. Try again with internet connection!"
+ exit 1
+fi
+
+#
+# From https://medium.com/swlh/make-your-raspberry-pi-file-system-read-only-raspbian-buster-c558694de79
+#
+
+apt-get update && apt-get upgrade
+apt-get remove --purge triggerhappy logrotate dphys-swapfile
+apt-get autoremove --purge
+
+echo ' fastboot noswap ro' >> /boot/cmdline.txt
+
+sudo apt-get install busybox-syslogd
+sudo apt-get remove --purge rsyslog
+
+sed -i 's/vfat\s*defaults/vfat defaults,ro' /etc/fstab
+sed -i 's/ext4\s*defaults,noatime/ext4\s*defaults,noatime,ro' /etc/fstab
+
+echo '\ntmpfs /tmp tmpfs nosuid,nodev 0 0\ntmpfs /var/log tmpfs nosuid,nodev\n 0 0\ntmpfs /var/tmp tmpfs nosuid,nodev 0 0' >> /etc/fstab
+
+sudo rm -rf /var/lib/dhcp /var/lib/dhcpcd5 /var/spool /etc/resolv.conf
+sudo ln -s /tmp /var/lib/dhcp
+sudo ln -s /tmp /var/lib/dhcpcd5
+sudo ln -s /tmp /var/spool
+sudo touch /tmp/dhcpcd.resolv.conf
+sudo ln -s /tmp/dhcpcd.resolv.conf /etc/resolv.conf
+
+sudo rm /var/lib/systemd/random-seed
+sudo ln -s /tmp/random-seed /var/lib/systemd/random-seed
+
+[Service]
+Type=oneshot
+RemainAfterExit=yes
+ExecStartPre=/bin/echo "" >/tmp/random-seed
+
+sed -i 's/\[Service\]\nType=oneshot\nRemainAfterExit=yes/\[Service\]\nType=oneshot\nRemainAfterExit=yes\nExecStartPre=/bin/echo "" >/tmp/random-seed' /lib/systemd/system/systemd-random-seed.service
+
+# add ro and rw alianses
+
+echo 'set_bash_prompt() {\n\n fs_mode=$(mount | sed -n -e "s/^\/dev\/.* on \/ .*(\(r[w|o]\).*/\1/p") \n\nPS1=\'\[\033[01;32m\]\u@\h${fs_mode:+($fs_mode)}\[\033[00m\]:\[\033[01;34m\]\w\[\033[00m\]\$ \'
+}\nalias ro=\'sudo mount -o remount,ro / ; sudo mount -o remount,ro /boot\'alias rw=\'sudo mount -o remount,rw / ; sudo mount -o remount,rw /boot\'\nPROMPT_COMMAND=set_bash_prompt' >> /etc/bash.bashrc
+
+echo 'mount -o remount,ro /\nmount -o remount,ro /boot' >> /etc/bash.bash_logout
+
+echo "System going down for reboot!"
+reboot
\ No newline at end of file