diff --git a/SD_ROOT/wz_mini/etc/network.d/S10httpd b/SD_ROOT/wz_mini/etc/network.d/S10httpd
index d745a7b..c0235f0 100644
--- a/SD_ROOT/wz_mini/etc/network.d/S10httpd
+++ b/SD_ROOT/wz_mini/etc/network.d/S10httpd
@@ -23,7 +23,7 @@ compose_home()
config) echo 'Wz Mini Configuration' >> $target ;;
car) echo 'Car Tool' >> $target ;;
current) echo 'Current Screen' >> $target ;;
- multicam) echo 'Multi-Cam Viewer' >> $target ;;
+ multicam) echo 'Multi-Cam Viewer' >> $target ;;
diag) echo 'Diagnostics' >> $target ;;
status) echo 'Check Recording' >> $target ;;
esac
diff --git a/SD_ROOT/wz_mini/usr/bin/app.ver b/SD_ROOT/wz_mini/usr/bin/app.ver
index 6ba35a3..451cf82 100644
--- a/SD_ROOT/wz_mini/usr/bin/app.ver
+++ b/SD_ROOT/wz_mini/usr/bin/app.ver
@@ -1 +1 @@
-Tue Sep 26 01:38:01 AM PDT 2023
+Sat Sep 30 11:46:23 AM PDT 2023
diff --git a/SD_ROOT/wz_mini/www/cgi-bin/status.cgi b/SD_ROOT/wz_mini/www/cgi-bin/status.cgi
index c2e8220..98caa9d 100644
--- a/SD_ROOT/wz_mini/www/cgi-bin/status.cgi
+++ b/SD_ROOT/wz_mini/www/cgi-bin/status.cgi
@@ -2,13 +2,56 @@
# This serves a rudimentary webpage to test different items
. /opt/wz_mini/www/cgi-bin/shared.cgi
-
+gpiopath="/sys/devices/virtual/gpio"
base='/opt/record/';
TZ=$(cat /configs/TZ)
+test_gpio()
+{
+ num=$1
+ #gpiodir=$(cat $gpiopath/gpio$num/direction)
+
+ #echo "gpiodir was $gpiodir for $num"
+
+ #newdir="in"
+ #echo $newdir > $gpiopath/gpio$num/direction
+ ##echo "set to in on $num"
+ myval=$(cat "$gpiopath/gpio$num/value")
+ #echo "read value $myval for $num"
+ if [[ "$myval" -eq "0" ]]; then
+ echo "OFF"
+ else
+ echo "ON"
+ fi
+
+ #if [[ "$gpiodir" != "in" ]]; then
+ # echo $gpiodir > $gpiopath/gpio$num/direction
+ #fi
+
+
+}
+
+
+test_irled()
+{
+ test_gpio 47
+ echo "IRLED Test"
+}
+
+test_night()
+{
+ runmode=$(cat /proc/jz/isp/isp-m0 | grep "Runing Mode" | cut -d ":" -f 2 | sed -e 's/^[[:space:]]*//' )
+ if [[ "$runmode" = "Night" ]]; then
+ echo "OK$lb"
+ else
+ echo "NG$lb"
+ fi
+
+ echo "Test Night (Running Mode: $runmode)"
+}
test_recording()
{
@@ -107,5 +150,8 @@ fi
if [[ "$GET_test" = "recording" ]]; then
test_recording
+ elif [[ "$GET_test" = "irled" ]]; then
+ test_irled
+ elif [[ "$GET_test" = "night" ]]; then
+ test_night
fi
-
diff --git a/SD_ROOT/wz_mini/www/multicam/index.css b/SD_ROOT/wz_mini/www/multicam/index.css
new file mode 100644
index 0000000..a18e43a
--- /dev/null
+++ b/SD_ROOT/wz_mini/www/multicam/index.css
@@ -0,0 +1,129 @@
+BODY {
+ --cc_bar_height: 20px;
+ --cam_height: 20%;
+}
+.za_images {
+display:flex;
+flex-direction:column;
+flex-wrap:wrap;
+height:calc(100% - var(--cc_bar_height));
+width:100%;
+background-color:#5a8dd8;
+}
+.za_images .cam_DIV {
+ border:2px solid blue;
+ border-radius:5px;
+ padding:2px;
+ margin:1px;
+ position:relative;
+}
+.za_images .cam_DIV:not(.first_CAM)
+{
+ height:calc(var(--cam_height) - 10px);
+ text-align:center;
+ width:17%;
+}
+.cam_DIV::after {
+ position:absolute;
+ bottom:0;
+ right:0;
+ color:yellow;
+ font-size:3vh;
+ font-weight:bold;
+ content:attr(cam);
+ z-index:5;
+}
+
+.cam_DIV::before
+{
+ z-index:5;
+ font-size:3vh;
+ position:absolute;
+ top:0;
+ right:0;
+}
+
+.cam_DIV[night=OK]::before {
+ color: #ccccff;
+ content:"\263d";
+}
+.cam_DIV[night=NG]::before {
+ color:yellow;
+ content:"\263c";
+}
+
+
+
+.za_images .first_CAM {
+ flex:0 0 calc(100% - 40px);
+ width:75%;
+ order:0;
+ width:calc(100% - 17% - 16px);
+}
+.broken_cam {
+ border:2px solid red;
+}
+.active_button {
+ background:green;
+ color:white;
+}
+.inactive_button {
+ background:grey;
+ color:black;
+}
+.full_CAM {
+ position:absolute;
+ top:0;
+ left:0;
+ width:100% !important;
+ height:100% !important;
+}
+.cam_DIV {
+ position:relative;
+}
+
+.za_images IMG {
+ max-height:100%;
+ max-width:100%;
+ position:absolute;
+ top:0;
+ left:0;
+}
+.cam_control {
+ width:calc(100% - 18px);
+ position:absolute;
+ bottom:0;
+ height:var(--cc_bar_height);
+ margin:1px;
+ padding:2px;
+ background-color:lightgreen;
+ border-radius:5px;
+ border:2px solid green;
+}
+.cam_control BUTTON {
+ width:calc(30% - 4px);
+}
+.cam_control DIV {
+ width:calc(20% - 4px);
+ display:inline-flex;
+ text-align:right;
+}
+
+@media (orientation: portrait) and (max-device-width: 450px) {
+ .cam_control {
+ top:0;
+ }
+ .za_images {
+ flex-direction:row;
+ flex-wrap:wrap;
+ margin-top:30px;
+ }
+ .za_images .cam_DIV:not(.first_CAM) {
+ width:calc(50% - 10px);
+ }
+ .za_images .cam_DIV.first_CAM {
+ width:calc(100% - 3px) !important;
+ flex:0 0 calc(100% - 5px) !important;
+ height:30%;
+ }
+}
diff --git a/SD_ROOT/wz_mini/www/multicam/index.html b/SD_ROOT/wz_mini/www/multicam/index.html
new file mode 100644
index 0000000..28456c1
--- /dev/null
+++ b/SD_ROOT/wz_mini/www/multicam/index.html
@@ -0,0 +1,39 @@
+
+
+
+
+
+
0
+
+
+
+
+
+
diff --git a/SD_ROOT/wz_mini/www/multicam/index.js b/SD_ROOT/wz_mini/www/multicam/index.js
new file mode 100644
index 0000000..706e5fb
--- /dev/null
+++ b/SD_ROOT/wz_mini/www/multicam/index.js
@@ -0,0 +1,342 @@
+const canWakeLock = () => 'wakeLock' in navigator;
+
+
+function releaseWakeState() {
+ if(wakelock) wakelock.release();
+ wakelock = null;
+}
+
+let wakelock;
+const requestWakeLock = async () => {
+ try {
+ const wakeLock = await navigator.wakeLock.request("screen");
+ } catch (err) {
+ // The wake lock request fails - usually system-related, such as low battery.
+
+ console.log(`${err.name}, ${err.message}`);
+ }
+};
+
+
+
+class helper_dad
+{
+ me;
+ obj;
+ context = "";
+ set_me(x)
+ {
+ this.me = x;
+ }
+ get_me()
+ {
+ return this.me;
+ }
+ test()
+ {
+ console.log("parent");
+ }
+
+ log(x)
+ {
+ console.log( this.context + ' - ' + x)
+ }
+}
+
+class cam_obj extends helper_dad
+{
+ jpeg_url = "";
+ status_url = false;
+ cam_id = 0;
+
+ set_obj(obj)
+ {
+ var me = super.get_me();
+ me.obj = obj;
+ me.obj.addEventListener('click',(e) => { me.click(); } );
+ }
+
+ mark_status(test,text)
+ {
+ var me = super.get_me();
+ var test_val = "OFF";
+ me.log(me.cam_id + ' - ' + text);
+ if (text.indexOf("ON") !== -1) { test_val = "ON"; }
+ else if (text.indexOf("OK") !== -1) { test_val = "OK"; }
+ me.obj.setAttribute(test,test_val);
+ }
+
+ first_init()
+ {
+ this.set_me(this);
+ }
+
+ constructor(cam_id,jpeg_url)
+ {
+ super();
+ this.set_me(this);
+ this.jpeg_url = jpeg_url;
+ this.cam_id = cam_id;
+ this.context = "cam " + cam_id;
+ }
+
+ status_test(test_type) {
+ return false;
+ }
+
+ night_test()
+ {
+ var me = super.get_me();
+ me.status_test('night');
+ }
+
+ irled_test()
+ {
+ var me = super.get_me();
+ me.status_test('irled');
+ }
+
+ recording_test()
+ {
+ var me = super.get_me();
+ me.status_test('recording');
+ }
+
+ unfocus()
+ {
+ var me = super.get_me();
+ me.obj.classList.remove("first_CAM");
+ me.obj.style.order = me.cam_id;
+ }
+
+
+ click() {
+ var me = super.get_me();
+ if (me.obj.classList.contains('first_CAM')) {
+ return cam_tool.focus_click(this);
+ }
+
+ for (var i = 0; i < cam_tool.cams.length; i++) {
+ cam_tool.cams[i].unfocus();
+ }
+ me.obj.classList.add("first_CAM");
+ me.obj.style.order = 0;
+ }
+
+ update()
+ {
+ var me = super.get_me();
+ var i = me.cam_id -1;
+ var cam1 = cam_tool.feeds[i];
+ if (cam1.getAttribute('current') == "y") {
+ cam1.setAttribute('current','n');
+ var cam = document.querySelector('img.cam_img2[cam="' + (i + 1) + '"]');
+ cam1.classList.remove('broken_cam');
+ } else if (document.querySelector('img.cam_img2[cam="' + (i + 1) + '"]').getAttribute('current') == "y")
+ {
+ var cam = cam_tool.feeds[i];
+ document.querySelector('img.cam_img2[cam="' + (i + 1) + '"]').setAttribute('current','n');
+ } else {
+ cam1.classList.add('broken_cam');
+ cam_tool.notify(i,'neither is loaded on camera ');
+ return false;
+ }
+ var load_start = new Date().getTime();
+ cam.setAttribute('load_start',load_start);
+ cam.src = cam_tool.feeds[i].src.split("&")[0] + "&load=" + load_start;
+ }
+
+}
+
+
+class v3_cam_obj extends cam_obj
+{
+ constructor(cam_id,jpeg_url)
+ {
+ super(cam_id,jpeg_url);
+ super.set_me(this);
+ this.status_url = jpeg_url.substring(0,this.jpeg_url.lastIndexOf("/")+1) + 'status.cgi';
+
+ }
+
+ status_test(test_type) {
+ var me = super.get_me();
+ fetch(me.status_url + '?test=' + test_type)
+ .then( (response) => response.text())
+ .then( (text) => me.mark_status(test_type,text) );
+ }
+
+}
+
+
+const cam_tool = {
+ cam_count : 0,
+ feed_interval_frequency : 3000,
+ spacing:false,
+ feed_interval : false,
+ tick: 0,
+ feeds : false,
+ buttons: [],
+ loads: [],
+ cams: [],
+ load_sum :0,
+ load_fails:0,
+ focus_click:function(me) {
+ me.classList.toggle('full_CAM');
+ return false;
+ },
+ run_mode: function(e) {
+ e.preventDefault();
+ e.stopPropagation();
+
+ for (var i = 0; i < cam_tool.modes.length; i++) {
+ cam_tool.modes[i].classList.remove('active_button');
+ }
+
+ e.target.classList.add('active_button');
+ if (e.target.getAttribute("mode") == 'auto') {
+ var average = cam_tool.load_sum / cam_tool.loads.length;
+ var use = parseInt(average * 2 / 10) * 10;
+ cam_tool.feed_interval_frequency = use;
+ cam_tool.calc_spacing();
+ }
+ if (e.target.getAttribute("mode") == 'manual') {
+ cam_tool.feed_interval_frequency = document.querySelector("DIV.load_mode[mode='manual'] INPUT").value;
+ cam_tool.calc_spacing();
+ }
+ },
+ load_cam : function() {
+ var cam = this.getAttribute('cam');
+ if (this.classList.contains('cam_img2')) {
+ var cam_obj = cam_tool.feeds[cam -1];
+ } else {
+ var cam_obj = document.querySelector('img.cam_img2[cam="' + cam + '"]');
+ }
+ cam_obj.style.zIndex = 1;
+ cam_obj.setAttribute('current','n');
+ this.setAttribute('current','y');
+ this.style.zIndex = 2;
+
+
+ var load_time = new Date().getTime() - this.getAttribute('load_start');
+ if (load_time < 100000) {
+ cam_tool.load_sum += load_time;
+ cam_tool.loads.push(load_time);
+ var average = cam_tool.load_sum / cam_tool.loads.length;
+ document.getElementById('load_average').innerHTML = parseInt(average);
+ }
+ },
+ init : function(feed_interval_frequency) {
+ this.feed_interval_frequency = feed_interval_frequency;
+ var divs = document.getElementsByClassName('cam_DIV');
+ for (var i = 0; i < divs.length; i++) {
+ cam_tool.cams[i].set_obj(divs[i]);
+ }
+
+ cam_tool.feeds = document.getElementsByClassName('cam_img');
+ for (var i = 0; i < cam_tool.feeds.length; i++) {
+ cam_tool.feeds[i].addEventListener('load', cam_tool.load_cam);
+ }
+ var img2 = document.getElementsByClassName('cam_img2');
+ for (var i = 0; i < img2.length; i++) {
+ img2[i].addEventListener('load', cam_tool.load_cam);
+ }
+
+
+ cam_tool.buttons["start"] = document.querySelector("BUTTON[action='start']");
+ cam_tool.buttons["pause"] = document.querySelector("BUTTON[action='pause']");
+
+ cam_tool.buttons["start"].addEventListener('click',cam_tool.start);
+ cam_tool.buttons["pause"].addEventListener('click',cam_tool.pause);
+
+
+ cam_tool.modes = document.getElementsByClassName('load_mode');
+ for (var i = 0; i < cam_tool.modes.length; i++) {
+ cam_tool.modes[i].addEventListener('click', cam_tool.run_mode);
+ }
+
+ document.querySelector("DIV.load_mode[mode='manual'] INPUT").value = cam_tool.feed_interval_frequency;
+
+ cam_tool.cams[0].obj.click();
+ cam_tool.calc_spacing();
+ cam_tool.start();
+
+
+ },
+ notify:function(cam,message) {
+ console.log("camera " + cam + " - " + message);
+ },
+ update: function() {
+ for (var i= 0; i < cam_tool.feeds.length; i++) {
+ var current_tick = cam_tool.tick * 10;
+ var target_tick = cam_tool.spacing * i;
+ if (((current_tick) % cam_tool.feed_interval_frequency) == (target_tick )) {
+ cam_tool.cams[i].update();
+ console.log("i" + i + " tick " + cam_tool.tick + " % interval (" + cam_tool.feed_interval_frequency + ") " + ((current_tick) % cam_tool.feed_interval_frequency));
+ }
+ if (((current_tick) % (cam_tool.feed_interval_frequency * 10)) == (target_tick )) {
+ cam_tool.cams[i].night_test();
+ }
+
+ }
+ cam_tool.tick++;
+ },
+ calc_spacing:function() {
+ cam_tool.spacing = Math.floor( cam_tool.feed_interval_frequency / (cam_tool.cam_count *10 ) ) * 10;
+ console.log('updated frequency to ' + cam_tool.spacing );
+ },
+ update_freq(event) {
+ console.log('set frequency to ' + event.target.value);
+ cam_tool.feed_interval_frequency = event.target.value;
+ cam_tool.calc_spacing();
+ },
+ set_buttons:function(active)
+ {
+ for (let key in cam_tool.buttons) {
+ cam_tool.buttons[key].classList.remove('active_button');
+ cam_tool.buttons[key].classList.add('inactive_button');
+ }
+ cam_tool.buttons[active].classList.add("active_button");
+ cam_tool.buttons[active].classList.remove("inactive_button");
+},
+ pause: function() {
+ cam_tool.set_buttons('pause');
+ clearInterval(cam_tool.feed_interval);
+ cam_tool.tick = 0;
+ releaseWakeState();
+ },
+ start : function() {
+ if (cam_tool.feed_interval) { cam_tool.pause(); }
+ cam_tool.set_buttons('start');
+ cam_tool.feed_interval = setInterval(cam_tool.update, 10);
+ requestWakeLock();
+ },
+
+
+ compose:function(cam_count,url,cam_type='other') {
+ for (var i = 1; i <= cam_count; i++)
+ {
+ this.add_camera(i,url.replace('%d',i) ,cam_type);
+ }
+
+ },
+ add_camera:function(i,url,cam_type='other')
+ {
+ cam_tool.cam_count++;
+ var zam = document.getElementById("za_images");
+ var id = document.createElement('DIV');
+ id.innerHTML = '
';
+ id.setAttribute('cam',i);
+ id.className = "cam_DIV";
+ zam.appendChild(id);
+ cam_tool.calc_spacing();
+ if (cam_type == 'v3') {
+ var nc = new v3_cam_obj(i,url);
+ } else {
+ var nc = new cam_obj(i,url);
+ }
+ nc.first_init();
+ cam_tool.cams.push(nc)
+ }
+
+}
diff --git a/file.chk b/file.chk
index fdac45c..4eb0a15 100644
--- a/file.chk
+++ b/file.chk
@@ -5,7 +5,7 @@ dbf932aa317b95a18cb7df4516086a40 SD_ROOT/wz_mini/www/cgi-bin/cam-bitRate.md
23964b5a0dcf4863e94a4b0fce65e8b2 SD_ROOT/wz_mini/www/cgi-bin/diagnostics.cgi
e17a4aeac39b2dbb5a71d05c4ba0a067 SD_ROOT/wz_mini/www/cgi-bin/WEB_CAM_ENABLE.md
149ec81cc0bf19d7811d58a3b61c04ec SD_ROOT/wz_mini/www/cgi-bin/jpeg.cgi
-fb32e872d57f1fe59b652933c06a934a SD_ROOT/wz_mini/www/cgi-bin/status.cgi
+9c3f64781075d22bf8f0cb3e10ec8a73 SD_ROOT/wz_mini/www/cgi-bin/status.cgi
78e6caa4aa6a870f7cb58ce71265c2da SD_ROOT/wz_mini/www/cgi-bin/cam-NIGHT_LED_flag.md
42178fc8e88339b884463a1ce9caeaa9 SD_ROOT/wz_mini/www/cgi-bin/GO2RTC_SERVER_ENABLED.md
16394ca5c27c34b68142adb8e47a4434 SD_ROOT/wz_mini/www/cgi-bin/cam-drawBoxSwitch.md
@@ -32,6 +32,9 @@ efcb5cd9751e6a7c1730e0116046ae70 SD_ROOT/wz_mini/www/cgi-bin/config.cgi
9e78c01564ae0c2c627f9f5c9ae4df91 SD_ROOT/wz_mini/www/cgi-bin/cam-res.md
9be803465aa134d010c79622bdae44c6 SD_ROOT/wz_mini/www/cgi-bin/cam-watermark_flag.md
042848359c3840f5973baffca2457a5d SD_ROOT/wz_mini/www/cgi-bin/ENABLE_USB_DIRECT.md
+0a31310f2b8a824170b97750d525f887 SD_ROOT/wz_mini/www/multicam/index.css
+f0df9f6d87b0751ff2a68a30a7c1451b SD_ROOT/wz_mini/www/multicam/index.js
+698c24126b58704f3fbdd92b39a390f8 SD_ROOT/wz_mini/www/multicam/index.html
c4846ffbc108bfe26abfc0be9579ee82 SD_ROOT/wz_mini/www/feed.js
5e0bf72e53baf044b3cd89a3f5b120b5 SD_ROOT/wz_mini/www/config.js
e344ffc55222c029c5d324037a5aa1f3 SD_ROOT/wz_mini/www/car/car.js
@@ -48,7 +51,7 @@ b77697b15e4d6e32e6e2c8c07cbceee2 SD_ROOT/wz_mini/etc/network.d/S04wireguard
720d337f4fd4cecdf63cfbdf5476cc14 SD_ROOT/wz_mini/etc/network.d/S12remoteaccessory
88cd61106fa526f82a812b02aea23ddc SD_ROOT/wz_mini/etc/network.d/S15v4l2rtspserver
d3ac671f88b6d639926b9f5f9a40da6e SD_ROOT/wz_mini/etc/network.d/S01wlanhw
-758921d3c465ea9ecd578639dd33f774 SD_ROOT/wz_mini/etc/network.d/S10httpd
+27a4b83bb3aa6d53dcd1b81cd280f397 SD_ROOT/wz_mini/etc/network.d/S10httpd
9969970dc82daa31b41730ee460514b0 SD_ROOT/wz_mini/etc/network.d/S03ipv6
edebe65d895650235f05291618814ebe SD_ROOT/wz_mini/etc/network.d/S08hostname
3f025fd89a69b99b0b2e235faf504930 SD_ROOT/wz_mini/etc/network.d/S17go2rtc
@@ -192,7 +195,7 @@ b339aee882a5d1c943ad08e4282ec3fd SD_ROOT/wz_mini/usr/bin/iCamera-dbg
b10bdd54c56d8d8de2a27336f17285fe SD_ROOT/wz_mini/usr/bin/patch_icamera.sh
dafffcc1bd1e721d508400686338ceb8 SD_ROOT/wz_mini/usr/bin/imp_helper.sh
580b1b6e91e72b4a4fef7b21d8954403 SD_ROOT/wz_mini/usr/bin/getSensorType
-a063e244bab90ab16fa0781e15250d48 SD_ROOT/wz_mini/usr/bin/app.ver
+1116add42cb046e299a67abc0d04c2e5 SD_ROOT/wz_mini/usr/bin/app.ver
4c780f0455481d106d47d89f0ae04ed5 SD_ROOT/wz_mini/lib/uClibc.tar
9afeb088e4cbabbe0b04033b560204d0 SD_ROOT/wz_mini/lib/libimp.so
4100755cb6cc6e3b76da20c7e3690e16 SD_ROOT/wz_mini/lib/libalog.so