diff --git a/html/saraR4.html b/html/saraR4.html
index 67e91d2..3e66d21 100755
--- a/html/saraR4.html
+++ b/html/saraR4.html
@@ -424,17 +424,24 @@
@@ -1492,9 +1499,27 @@ function update_modem_configMode(param, checked){
// SELF TEST FUNCTIONS
// ============================================
+// Global object to store test results for report
+let selfTestReport = {
+ timestamp: '',
+ deviceId: '',
+ modemVersion: '',
+ results: {},
+ rawResponses: {}
+};
+
function runSelfTest() {
console.log("Starting Self Test...");
+ // Reset report
+ selfTestReport = {
+ timestamp: new Date().toISOString(),
+ deviceId: document.querySelector('.sideBar_sensorName')?.textContent || 'Unknown',
+ modemVersion: document.getElementById('modem_version')?.textContent || 'Unknown',
+ results: {},
+ rawResponses: {}
+ };
+
// Reset UI
resetSelfTestUI();
@@ -1502,9 +1527,10 @@ function runSelfTest() {
const modal = new bootstrap.Modal(document.getElementById('selfTestModal'));
modal.show();
- // Disable close buttons during test
+ // Disable buttons during test
document.getElementById('selfTestCloseBtn').disabled = true;
document.getElementById('selfTestDoneBtn').disabled = true;
+ document.getElementById('selfTestCopyBtn').disabled = true;
document.getElementById('btn_selfTest').disabled = true;
// Start test sequence
@@ -1547,10 +1573,17 @@ function resetSelfTestUI() {
document.getElementById('selftest_summary').innerHTML = '';
}
-function addSelfTestLog(message) {
+function addSelfTestLog(message, isRaw = false) {
const logsEl = document.getElementById('selftest_logs');
const timestamp = new Date().toLocaleTimeString();
- logsEl.innerHTML += `[${timestamp}] ${message}
`;
+
+ if (isRaw) {
+ // Raw AT response - format nicely
+ logsEl.textContent += `[${timestamp}] >>> RAW RESPONSE:\n${message}\n<<<\n`;
+ } else {
+ logsEl.textContent += `[${timestamp}] ${message}\n`;
+ }
+
// Auto-scroll to bottom
logsEl.parentElement.scrollTop = logsEl.parentElement.scrollHeight;
}
@@ -1559,6 +1592,12 @@ function updateTestStatus(testId, status, detail, badge) {
document.getElementById(`test_${testId}_status`).className = `badge ${badge}`;
document.getElementById(`test_${testId}_status`).textContent = status;
document.getElementById(`test_${testId}_detail`).textContent = detail;
+
+ // Store result in report
+ selfTestReport.results[testId] = {
+ status: status,
+ detail: detail
+ };
}
function setConfigMode(enabled) {
@@ -1591,18 +1630,24 @@ function setConfigMode(enabled) {
function sendATCommand(command, timeout) {
return new Promise((resolve, reject) => {
- addSelfTestLog(`Sending AT command: ${command}`);
+ addSelfTestLog(`Sending AT command: ${command} (timeout: ${timeout}s)`);
$.ajax({
url: `launcher.php?type=sara&port=ttyAMA2&command=${encodeURIComponent(command)}&timeout=${timeout}`,
dataType: 'text',
method: 'GET',
success: function(response) {
- addSelfTestLog(`Response: ${response.replace(/\n/g, ' | ')}`);
+ // Store raw response in report
+ selfTestReport.rawResponses[command] = response;
+
+ // Log raw response
+ addSelfTestLog(response.trim(), true);
+
resolve(response);
},
error: function(xhr, status, error) {
addSelfTestLog(`AT command error: ${error}`);
+ selfTestReport.rawResponses[command] = `ERROR: ${error}`;
reject(new Error(error));
}
});
@@ -1634,16 +1679,22 @@ async function selfTestSequence() {
dataType: 'json',
method: 'GET',
success: function(data) {
- addSelfTestLog(`WiFi status: ${JSON.stringify(data)}`);
+ addSelfTestLog(`WiFi status received`);
+ // Store raw response
+ selfTestReport.rawResponses['WiFi Status'] = JSON.stringify(data, null, 2);
resolve(data);
},
error: function(xhr, status, error) {
addSelfTestLog(`WiFi status error: ${error}`);
+ selfTestReport.rawResponses['WiFi Status'] = `ERROR: ${error}`;
reject(new Error(error));
}
});
});
+ // Log detailed WiFi info
+ addSelfTestLog(`Mode: ${wifiResponse.mode}, SSID: ${wifiResponse.ssid}, IP: ${wifiResponse.ip}, Hostname: ${wifiResponse.hostname}`);
+
if (wifiResponse.connected) {
let modeIcon = '';
let modeLabel = '';
@@ -1907,15 +1958,128 @@ async function selfTestSequence() {
${testsPassed} passed
${testsFailed} failed`;
- // Enable close buttons
+ // Store summary in report
+ selfTestReport.summary = {
+ passed: testsPassed,
+ failed: testsFailed,
+ status: statusText
+ };
+
+ // Enable buttons
document.getElementById('selfTestCloseBtn').disabled = false;
document.getElementById('selfTestDoneBtn').disabled = false;
+ document.getElementById('selfTestCopyBtn').disabled = false;
document.getElementById('btn_selfTest').disabled = false;
addSelfTestLog('Self test completed.');
+ addSelfTestLog('Click "Copy Report" to share results with support.');
}
}
+function copySelfTestReport() {
+ // Build formatted report
+ let report = `═══════════════════════════════════════════════════════════════
+ NEBULEAIR PRO 4G - SELF TEST REPORT
+═══════════════════════════════════════════════════════════════
+
+📅 Date: ${selfTestReport.timestamp}
+🔧 Device ID: ${selfTestReport.deviceId}
+📱 Modem Version: ${selfTestReport.modemVersion}
+
+───────────────────────────────────────────────────────────────
+ TEST RESULTS
+───────────────────────────────────────────────────────────────
+
+`;
+
+ // Add test results
+ const testNames = {
+ wifi: '📡 WiFi/Network',
+ modem: '📟 Modem Connection',
+ sim: '💳 SIM Card',
+ signal: '📶 Signal Strength',
+ network: '🌐 Network Connection'
+ };
+
+ for (const [testId, name] of Object.entries(testNames)) {
+ if (selfTestReport.results[testId]) {
+ const result = selfTestReport.results[testId];
+ const statusIcon = result.status === 'Passed' ? '✅' :
+ result.status === 'Failed' ? '❌' :
+ result.status.includes('Hotspot') || result.status.includes('WiFi') || result.status.includes('Ethernet') ? 'ℹ️' : '⚠️';
+ report += `${name}
+ Status: ${statusIcon} ${result.status}
+ Detail: ${result.detail}
+
+`;
+ }
+ }
+
+ // Add summary
+ if (selfTestReport.summary) {
+ report += `───────────────────────────────────────────────────────────────
+ SUMMARY
+───────────────────────────────────────────────────────────────
+
+✅ Passed: ${selfTestReport.summary.passed}
+❌ Failed: ${selfTestReport.summary.failed}
+📊 Status: ${selfTestReport.summary.status}
+
+`;
+ }
+
+ // Add raw AT responses
+ report += `───────────────────────────────────────────────────────────────
+ RAW AT RESPONSES
+───────────────────────────────────────────────────────────────
+
+`;
+
+ for (const [command, response] of Object.entries(selfTestReport.rawResponses)) {
+ report += `Command: ${command}
+Response:
+${response}
+─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
+
+`;
+ }
+
+ // Add full logs
+ report += `───────────────────────────────────────────────────────────────
+ DETAILED LOGS
+───────────────────────────────────────────────────────────────
+
+${document.getElementById('selftest_logs').textContent}
+
+═══════════════════════════════════════════════════════════════
+ END OF REPORT - Generated by NebuleAir Pro 4G
+═══════════════════════════════════════════════════════════════
+`;
+
+ // Copy to clipboard
+ navigator.clipboard.writeText(report).then(function() {
+ // Show success feedback
+ const copyBtn = document.getElementById('selfTestCopyBtn');
+ const originalHtml = copyBtn.innerHTML;
+ copyBtn.innerHTML = `
+
+ Copied!`;
+ copyBtn.classList.remove('btn-outline-primary');
+ copyBtn.classList.add('btn-success');
+
+ setTimeout(function() {
+ copyBtn.innerHTML = originalHtml;
+ copyBtn.classList.remove('btn-success');
+ copyBtn.classList.add('btn-outline-primary');
+ }, 2000);
+ }).catch(function(err) {
+ console.error('Failed to copy:', err);
+ alert('Failed to copy to clipboard. Please select and copy the logs manually.');
+ });
+}
+