diff --git a/.github/workflows/device-client.yaml b/.github/workflows/device-client.yaml
index b27d118d7..bb4fc4f9d 100644
--- a/.github/workflows/device-client.yaml
+++ b/.github/workflows/device-client.yaml
@@ -12,7 +12,7 @@ jobs:
- name: install flutter
uses: subosito/flutter-action@v2
with:
- flutter-version: '3.27.2'
+ flutter-version: '3.27.3'
channel: 'stable'
- name: use cache
uses: actions/cache@v3
@@ -43,7 +43,7 @@ jobs:
- name: install flutter
uses: subosito/flutter-action@v2
with:
- flutter-version: '3.27.2'
+ flutter-version: '3.27.3'
channel: 'stable'
- name: use cache
uses: actions/cache@v3
diff --git a/.github/workflows/log_file_client.yaml b/.github/workflows/log_file_client.yaml
index 25c60dbad..8f2f08d1d 100644
--- a/.github/workflows/log_file_client.yaml
+++ b/.github/workflows/log_file_client.yaml
@@ -12,7 +12,7 @@
- name: install flutter
uses: subosito/flutter-action@v2
with:
- flutter-version: '3.27.2'
+ flutter-version: '3.27.3'
channel: 'stable'
- name: use cache
uses: actions/cache@v3
@@ -43,7 +43,7 @@
- name: install flutter
uses: subosito/flutter-action@v2
with:
- flutter-version: '3.27.2'
+ flutter-version: '3.27.3'
channel: 'stable'
- name: use cache
uses: actions/cache@v3
diff --git a/.github/workflows/log_file_server.yaml b/.github/workflows/log_file_server.yaml
index a9e091180..75a346b86 100644
--- a/.github/workflows/log_file_server.yaml
+++ b/.github/workflows/log_file_server.yaml
@@ -12,7 +12,7 @@
- name: install flutter
uses: subosito/flutter-action@v2
with:
- flutter-version: '3.27.2'
+ flutter-version: '3.27.3'
channel: 'stable'
- name: use cache
uses: actions/cache@v3
@@ -42,7 +42,7 @@
- name: install flutter
uses: subosito/flutter-action@v2
with:
- flutter-version: '3.27.2'
+ flutter-version: '3.27.3'
channel: 'stable'
- name: use cache
uses: actions/cache@v3
@@ -60,7 +60,6 @@
mkdir -p test/logs
touch test/logs/empty.log
echo "123456789" > test/logs/ten.log
- echo "This is data for line 1\nThis is data for line 2" > test/logs/deleteMe.log
dart run "bin/log_file_server.dart" "test/logs" &
dart test --concurrency=1
jobs
diff --git a/Makefile b/Makefile
index fc02c98f0..d9965622b 100644
--- a/Makefile
+++ b/Makefile
@@ -109,9 +109,6 @@ GPP_TEST=g++ $(FLAGS) -L$(BIN) $(INCLUDE)
$(BIN)/PHCalibrationWarningTest.cpp.bin: $(BIN)/libarduino.so $(TEST)/PHCalibrationWarningTest.cpp $(HEADERS)
$(GPP_TEST) -o $(BIN)/PHCalibrationWarningTest.cpp.bin $(TEST)/PHCalibrationWarningTest.cpp -larduino
-$(BIN)/RemoteLogPusherTest.cpp.bin: $(BIN)/libarduino.so $(TEST)/RemoteLogPusherTest.cpp $(HEADERS)
- $(GPP_TEST) -o $(BIN)/RemoteLogPusherTest.cpp.bin $(TEST)/RemoteLogPusherTest.cpp -larduino
-
$(BIN)/BlinkTest.cpp.bin: $(BIN)/libarduino.so $(TEST)/BlinkTest.cpp $(HEADERS)
$(GPP_TEST) -o $(BIN)/BlinkTest.cpp.bin $(TEST)/BlinkTest.cpp -larduino
@@ -431,9 +428,6 @@ $(BIN)/TC_util.o: $(SRC)/model/TC_util.cpp $(HEADERS)
$(BIN)/TankController.o: $(SRC)/TankController.cpp $(HEADERS)
g++ -c $(FLAGS) $(INCLUDE) -o $(BIN)/TankController.o $(SRC)/TankController.cpp
-$(BIN)/RemoteLogPusher.o: $(SRC)/model/RemoteLogPusher.cpp $(HEADERS)
- g++ -c $(FLAGS) $(INCLUDE) -o $(BIN)/RemoteLogPusher.o $(SRC)/model/RemoteLogPusher.cpp
-
$(BIN)/DataLogger.o: $(SRC)/model/DataLogger.cpp $(HEADERS)
g++ -c $(FLAGS) $(INCLUDE) -o $(BIN)/DataLogger.o $(SRC)/model/DataLogger.cpp
diff --git a/examples/TankController/TankController.ino b/examples/TankController/TankController.ino
index fe71c8481..224dc83be 100644
--- a/examples/TankController/TankController.ino
+++ b/examples/TankController/TankController.ino
@@ -14,6 +14,7 @@ const char pushingBoxID[] = "";
// If it remains empty, the MAC address will be used. Keep in mind that
// the name should be unique across all devices, not just your devices.
// So "tank-1" is not a good name, but "Onthank-tank-1" is.
+// The name will have ".log" added to it (so don't include it!).
const char remoteLogName[] = "";
// We query a web server for GMT time and then adjust for local time
diff --git a/extras/device_client/lib/model/version.dart b/extras/device_client/lib/model/version.dart
index d0555af25..ddec32c9a 100644
--- a/extras/device_client/lib/model/version.dart
+++ b/extras/device_client/lib/model/version.dart
@@ -1 +1 @@
-const String gitVersion = 'v24.10.2-14-gf3+';
+const String gitVersion = 'v24.10.2-105-g8+';
diff --git a/extras/device_client/pubspec.lock b/extras/device_client/pubspec.lock
index 0f6049428..13a0d8af9 100644
--- a/extras/device_client/pubspec.lock
+++ b/extras/device_client/pubspec.lock
@@ -5,10 +5,10 @@ packages:
dependency: transitive
description:
name: _fe_analyzer_shared
- sha256: "88399e291da5f7e889359681a8f64b18c5123e03576b01f32a6a276611e511c3"
+ sha256: "03f6da266a27a4538a69295ec142cb5717d7d4e5727b84658b63e1e1509bac9c"
url: "https://pub.dev"
source: hosted
- version: "78.0.0"
+ version: "79.0.0"
_macros:
dependency: transitive
description: dart
@@ -18,10 +18,10 @@ packages:
dependency: transitive
description:
name: analyzer
- sha256: "62899ef43d0b962b056ed2ebac6b47ec76ffd003d5f7c4e4dc870afe63188e33"
+ sha256: c9040fc56483c22a5e04a9f6a251313118b1a3c42423770623128fa484115643
url: "https://pub.dev"
source: hosted
- version: "7.1.0"
+ version: "7.2.0"
args:
dependency: transitive
description:
@@ -313,10 +313,10 @@ packages:
dependency: "direct main"
description:
name: http_parser
- sha256: "76d306a1c3afb33fe82e2bbacad62a61f409b5634c915fceb0d799de1a913360"
+ sha256: "178d74305e7866013777bab2c3d8726205dc5a4dd935297175b19a23a2e66571"
url: "https://pub.dev"
source: hosted
- version: "4.1.1"
+ version: "4.1.2"
io:
dependency: transitive
description:
@@ -545,26 +545,26 @@ packages:
dependency: transitive
description:
name: pubspec_parse
- sha256: "81876843eb50dc2e1e5b151792c9a985c5ed2536914115ed04e9c8528f6647b0"
+ sha256: "0560ba233314abbed0a48a2956f7f022cce7c3e1e73df540277da7544cad4082"
url: "https://pub.dev"
source: hosted
- version: "1.4.0"
+ version: "1.5.0"
shared_preferences:
dependency: "direct main"
description:
name: shared_preferences
- sha256: "3c7e73920c694a436afaf65ab60ce3453d91f84208d761fbd83fc21182134d93"
+ sha256: a752ce92ea7540fc35a0d19722816e04d0e72828a4200e83a98cf1a1eb524c9a
url: "https://pub.dev"
source: hosted
- version: "2.3.4"
+ version: "2.3.5"
shared_preferences_android:
dependency: transitive
description:
name: shared_preferences_android
- sha256: "02a7d8a9ef346c9af715811b01fbd8e27845ad2c41148eefd31321471b41863d"
+ sha256: "138b7bbbc7f59c56236e426c37afb8f78cbc57b094ac64c440e0bb90e380a4f5"
url: "https://pub.dev"
source: hosted
- version: "2.4.0"
+ version: "2.4.2"
shared_preferences_foundation:
dependency: transitive
description:
@@ -774,18 +774,18 @@ packages:
dependency: transitive
description:
name: url_launcher_web
- sha256: "772638d3b34c779ede05ba3d38af34657a05ac55b06279ea6edd409e323dca8e"
+ sha256: "3ba963161bd0fe395917ba881d320b9c4f6dd3c4a233da62ab18a5025c85f1e9"
url: "https://pub.dev"
source: hosted
- version: "2.3.3"
+ version: "2.4.0"
url_launcher_windows:
dependency: transitive
description:
name: url_launcher_windows
- sha256: "44cf3aabcedde30f2dba119a9dea3b0f2672fbe6fa96e85536251d678216b3c4"
+ sha256: "3284b6d2ac454cf34f114e1d3319866fdd1e19cdc329999057e44ffe936cfa77"
url: "https://pub.dev"
source: hosted
- version: "3.1.3"
+ version: "3.1.4"
vector_math:
dependency: transitive
description:
@@ -860,4 +860,4 @@ packages:
version: "3.1.3"
sdks:
dart: ">=3.6.0 <4.0.0"
- flutter: ">=3.24.0"
+ flutter: ">=3.27.0"
diff --git a/extras/device_client/pubspec.yaml b/extras/device_client/pubspec.yaml
index 21f0ee35e..0ac7ea629 100644
--- a/extras/device_client/pubspec.yaml
+++ b/extras/device_client/pubspec.yaml
@@ -1,7 +1,7 @@
name: device_client
description: A web UI for the Open Acidification Tank Controller.
publish_to: 'none' # Remove this line if you wish to publish to pub.dev
-version: 24.10.2
+version: 25.1.1
environment:
sdk: '>=3.3.0 <4.0.0'
diff --git a/extras/docs/Network.drawio b/extras/docs/Network.drawio
new file mode 100644
index 000000000..2ee82a56a
--- /dev/null
+++ b/extras/docs/Network.drawio
@@ -0,0 +1,49 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/extras/docs/Network.drawio.svg b/extras/docs/Network.drawio.svg
new file mode 100644
index 000000000..e7a0ad4f3
--- /dev/null
+++ b/extras/docs/Network.drawio.svg
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/extras/docs/Network.md b/extras/docs/Network.md
new file mode 100644
index 000000000..c004c7106
--- /dev/null
+++ b/extras/docs/Network.md
@@ -0,0 +1,55 @@
+# Architecture
+
+## Machines
+
+* **traefik** is a virtual machine that acts as the public-facing server for [oap.cs.wallawalla.edu](oap.cs.wallawalla.edu). It acts as a firewall and reverse-proxy, forwarding requests to oap-vm.
+* **oap-vm** is a virtual machine in our CS Lab that supports various services for the Open Acidification Project (OAP).
+* A **Tank Controller** is an Arduino-based device controller that monitors and manages temperature and pH for a tank (aquarium) used in ocean acidification research. It has a 4x4 keypad for input and a 16x2 LCD display for output. It has a serial port used to flash the program memory and communicate with a computer. It also has an Ethernet connection and can make and respond to HTTP requests (the system does not have adequate resources to support HTTPS).
+* A **User Agent (Web Browser)** running on a separate machine that can connect to oap-vm or the tank controller.
+
+## Applications
+
+* The original application, **TankController**, is Arduino program that runs on the Tank Controller. It consists of a trivial "sketch" ([TankController.ino](../../examples/TankController/TankController.ino)), a primary library (TankController), and a number of other device-related libraries (see [libraries.md](libraries.md)).
+* The second application, **device_client**, is a Flutter application that mimics the Tank Controller's keypad and LCD. It is a single-page application (SPA) served from http://oap.cs.wallawalla.edu. After the static files are loaded, it presents a GUI in which the user can enter the IP address of a Tank Controller and interact—using the web browser—with the Tank Controller as if the device were physically present. The fact that we use one browser to interact with multiple servers presents a couple complications.
+ * First, most browsers prevent "Cross-Origin Resource Sharing (CORS)" unless explicitly enabled. In particular, CORS is designed to prevent a malicious (primary) site from scraping content from a secondary site (say, a bank), and sending it to the user (making the user think it is interacting with the bank when it is actually interacting with the malicious site). A secondary site may, however, authorize a primary site to collect and display its data by adding `Access-Control-Allow-Origin: *` to a response header. The TankController does this, and thus solves the CORS issue.
+ * Second, if a page is served with HTTPS (typically indicated by a lock in the address bar), most browsers do not let JavaScript on the page make HTTP requests because this would violate the user's expectation of security in the content. This means that if any data is obtained using HTTP, the entire page must be loaded as HTTP (to adequately communicate "insecure" to the user). As mentioned above, the Tank Controller's Arduino processor does not have adequate resources to handle HTTPS, so the Flutter-generated files served from oap.cs.wallawalla.edu must be served using HTTP before making the HTTP requests to the Tank Controller.
+ * While the browser sees the device_client app as HTTP, it is actually served by oap-vm as HTTPS and sent on by traefik as HTTP.
+* The third application, **log_file_server**, is a Dart server application that runs on oap-vm and accepts data from a Tank Controller and stores it on disk where it can be served by an Nginx web server on oap-vm. Because the Tank Controller does not support HTTPS (either as client or server), the data upload must be done using HTTP to oap.cs.wallawalla.edu (traefik) where it is converted to HTTPS and forwarded to oap-vm. This application also generates an abridged copy of the data to be used as thumbnails showing summary information for a group of Tank Controllers.
+* The fourth application, **log_file_client**, is a Flutter SPA that takes the data saved by log_file_server (from the Tank Controllers), and presents it in a graphical format. Because it does not interact directly with a Tank Controller, it can be served from https://oap.cs.wallawalla.edu. In fact, the distinction between device_client and log_file_client is http vs. https.
+
+## Communication
+
+
+
+* **A**: traefik communicates with oap-vm on an internal network using HTTPS using a self-signed certificate generated by oap-vm and recognized by traefik.
+* **B**: The user requests device_client (http://oap.cs.wallawalla.edu) or log_file_client (https://oap.cs.wallawalla.edu) from traefik which forwards the requests to oap-vm.
+* **C**: A Tank Controller can send data to log_file_server (using HTTP) to be saved on oap-vm.
+* **D**: Using device_client, a user can interact directly with a live Tank Controller (using HTTP).
+
+## Traefik and Nginx Configuration
+
+Traefik receives several types of requests (on links B and C) that it passes on to oap-vm (on link A). Each is sent to oap-vm using HTTPS, so the request needs to be mapped to something that Nginx on oap-vm can recognize as distinct.
+
+### HTTPS
+
+1. GET https://oap.cs.wallawalla.edu/api/ requests are for recent lines from files saved by the log_file_server app (link B).
+ * Traefik forwards to https://oap.vm.cs.wallawalla.edu:443/
+ * Nginx forwards to http://localhost:8080 (log_file_server)
+1. GET https://oap.cs.wallawalla.edu/logs/ requests are for files saved by the log_file_server app (link B).
+ * Traefik forwards to https://oap.vm.cs.wallawalla.edu:443/
+ * Nginx serves `/var/www/html` with `autoindex on`
+1. GET https://oap.cs.wallawalla.edu/ requests are for the log_file_client app (link B).
+ * Traefik forwards to https://oap.vm.cs.wallawalla.edu:443/
+ * Nginx serves `/var/www/html/log_file_client/`
+
+### HTTP
+
+1. GET http://oap.cs.wallawalla.edu/ requests are for the device_client app (link B).
+ * Traefik forwards to https://oap.vm.cs.wallawalla.edu:444/
+ * Nginx serves `/var/www/html/device_client/`
+1. HEAD http://oap.cs.wallawalla.edu/logs/ requests are for the size of log files (link C).
+ * Traefik forwards to https://oap.vm.cs.wallawalla.edu:444/logs/
+ * Nginx serves `/var/www/html/`
+1. POST http://oap.cs.wallawalla.edu/api/ requests are to the log_file_server app (link C).
+ * Traefik forwards to https://oap.vm.cs.wallawalla.edu:444/api/
+ * Nginx forwards to http://localhost:8080 (log_file_server)
diff --git a/extras/log_file_client/pubspec.lock b/extras/log_file_client/pubspec.lock
index 0aab11460..fb390914e 100644
--- a/extras/log_file_client/pubspec.lock
+++ b/extras/log_file_client/pubspec.lock
@@ -244,10 +244,10 @@ packages:
dependency: transitive
description:
name: http_parser
- sha256: "76d306a1c3afb33fe82e2bbacad62a61f409b5634c915fceb0d799de1a913360"
+ sha256: "178d74305e7866013777bab2c3d8726205dc5a4dd935297175b19a23a2e66571"
url: "https://pub.dev"
source: hosted
- version: "4.1.1"
+ version: "4.1.2"
intl:
dependency: "direct main"
description:
diff --git a/extras/log_file_client/pubspec.yaml b/extras/log_file_client/pubspec.yaml
index 5b9833c55..140c61a8f 100644
--- a/extras/log_file_client/pubspec.yaml
+++ b/extras/log_file_client/pubspec.yaml
@@ -1,7 +1,7 @@
name: log_file_client
description: "A new Flutter project."
publish_to: 'none' # Remove this line if you wish to publish to pub.dev
-version: 24.10.2
+version: 25.1.1
environment:
sdk: '>=3.3.1 <4.0.0'
diff --git a/extras/log_file_server/bin/log_file_server.dart b/extras/log_file_server/bin/log_file_server.dart
index e607190c2..04c77df6a 100644
--- a/extras/log_file_server/bin/log_file_server.dart
+++ b/extras/log_file_server/bin/log_file_server.dart
@@ -1,4 +1,4 @@
-import 'dart:async' show Future, Timer;
+import 'dart:async' show Future;
import 'dart:io';
import 'package:csv/csv.dart';
@@ -12,7 +12,7 @@ String rootDir =
// Configure routes.
final _router = Router()
..get('/api/', _get)
- ..post('/logs/', _post);
+ ..post('/api/', _post);
// Get snapshots of log files
Future _get(Request req, String path) async {
@@ -22,7 +22,6 @@ Future _get(Request req, String path) async {
final snapshotLength = uri.queryParameters['length'] == null
? 720
: int.parse(uri.queryParameters['length']!);
-
if (!file.existsSync()) {
return Response.notFound(null);
}
@@ -62,12 +61,6 @@ Future _post(Request req, String path) async {
);
}
- // // get remote address
- // var connectionInfo =
- // req.context['shelf.io.connection_info'] as HttpConnectionInfo;
- // var remoteAddress = connectionInfo.remoteAddress.address;
- // print('remoteAddress = "$remoteAddress" (${remoteAddress.runtimeType})');
-
final file = File('$rootDir/$path');
file.createSync(exclusive: false);
file.writeAsStringSync(
@@ -77,33 +70,13 @@ Future _post(Request req, String path) async {
return Response.ok(null);
}
-/*
- * read the root directory and create an index.html file
- */
-Future _createIndex() async {
- final sink = File('$rootDir/index.html').openWrite();
- sink.write('
');
- await for (final file in Directory(rootDir).list()) {
- if (file is File) {
- final path = file.path.substring(rootDir.length + 1);
- sink.write('