Skip to content

Commit

Permalink
Make CAN interface configurable via webserver
Browse files Browse the repository at this point in the history
  • Loading branch information
dalathegreat committed Feb 27, 2025
1 parent 26baf25 commit d19994c
Show file tree
Hide file tree
Showing 3 changed files with 76 additions and 11 deletions.
2 changes: 2 additions & 0 deletions Software/src/datalayer/datalayer.h
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,8 @@ typedef struct {
size_t logged_can_messages_offset = 0;
/** bool, determines if CAN messages should be logged for webserver */
bool can_logging_active = false;
/** uint8_t, enumeration which CAN interface should be used for log playback */
uint8_t can_replay_interface = CAN_NATIVE;

} DATALAYER_SYSTEM_INFO_TYPE;

Expand Down
67 changes: 56 additions & 11 deletions Software/src/devboard/webserver/can_replay_html.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,38 +22,84 @@ String can_replay_processor(void) {
".can-message { background-color: #404E57; margin-bottom: 5px; padding: 10px; border-radius: 5px; font-family: "
"monospace; }";
content += "</style>";
content += "<button onclick='importLog()'>Import .txt</button> ";
content += "<button onclick='stopPlaybackAndGoToMainPage()'>Stop &amp; Back to main page</button>";

// Start a new block for the CAN messages
content += "<div style='background-color: #303E47; padding: 20px; border-radius: 15px'>";

// Ask user to select which CAN interface log should be sent to
content += "<h4>Select CAN Interface for Playback</h4>";
content += "<h3>Step 1: Select CAN Interface for Playback</h3>";

// Dropdown with choices
content += "<label for='canInterface'>CAN Interface:</label>";
content += "<select id='canInterface' name='canInterface'>";
content += "<option value='" + String(CAN_NATIVE) + "'>CAN Native</option>";
content += "<option value='" + String(CANFD_NATIVE) + "'>CANFD Native</option>";
content += "<option value='" + String(CAN_ADDON_MCP2515) + "'>CAN Addon MCP2515</option>";
content += "<option value='" + String(CANFD_ADDON_MCP2518) + "'>CANFD Addon MCP2518</option>";
content += "<option value='" + String(CAN_NATIVE) + "' " +
(datalayer.system.info.can_replay_interface == CAN_NATIVE ? "selected" : "") +
">CAN Native</option>";
content += "<option value='" + String(CANFD_NATIVE) + "' " +
(datalayer.system.info.can_replay_interface == CANFD_NATIVE ? "selected" : "") +
">CANFD Native</option>";
content += "<option value='" + String(CAN_ADDON_MCP2515) + "' " +
(datalayer.system.info.can_replay_interface == CAN_ADDON_MCP2515 ? "selected" : "") +
">CAN Addon MCP2515</option>";
content += "<option value='" + String(CANFD_ADDON_MCP2518) + "' " +
(datalayer.system.info.can_replay_interface == CANFD_ADDON_MCP2518 ? "selected" : "") +
">CANFD Addon MCP2518</option>";

content += "</select>";

// Add a button to submit the selected CAN interface
// This function writes the selection to datalayer.system.info.can_replay_interface
content += "<button onclick='sendCANSelection()'>Apply</button>";

// Show text that log file is not loaded yet
content += "<h4>Log file not loaded yet. Import log before starting replay!</h4>";
content += "<h3>Step 2: Upload CAN Log File</h3>";

content += "<div id='drop-area' onclick=\"document.getElementById('file-input').click()\">";
content += "<p>Drag & drop a .txt file here, or click Browse to select one.</p>";
content += "<input type='file' id='file-input' accept='.txt'>";
content += "</div>";

content += "<div id='progress'><div id='progress-bar'></div></div>";

content += "<button id='upload-btn'>Upload</button>";

content += "<h3>Step 3: Playback control</h3>";

// Add a button to start playing the log
content += "<button onclick='startReplay()'>Start</button> ";

// Add a button to stop playing the log
content += "<button onclick='startReplay()'>Stop</button> ";

// Add a checkbox to loop the log
//content += "<input type="checkbox" id="myCheck" onclick="myFunction()">";
content += "<h3>Uploaded Log Preview:</h3>";
content += "<pre id='file-content'></pre>";

content += "<script>";
content += "const fileInput = document.getElementById('file-input');";
content += "const uploadBtn = document.getElementById('upload-btn');";
content += "const fileContent = document.getElementById('file-content');";
content += "const dropArea = document.getElementById('drop-area');";
content += "const progressBar = document.getElementById('progress-bar');";
content += "const progressContainer = document.getElementById('progress');";
content += "let selectedFile = null;";

content += "dropArea.addEventListener('dragover', (e) => { e.preventDefault(); dropArea.style.background = '#f0f0f0'; });";
content += "dropArea.addEventListener('dragleave', () => { dropArea.style.background = 'white'; });";
content += "dropArea.addEventListener('drop', (e) => { e.preventDefault(); dropArea.style.background = 'white'; if (e.dataTransfer.files.length > 0) { fileInput.files = e.dataTransfer.files; selectedFile = fileInput.files[0]; }});";

content += "fileInput.addEventListener('change', () => { selectedFile = fileInput.files[0]; });";

content += "uploadBtn.addEventListener('click', () => {";
content += "if (!selectedFile) { alert('Please select a file first!'); return; }";
content += "const formData = new FormData();";
content += "formData.append('file', selectedFile);";
content += "const xhr = new XMLHttpRequest();";
content += "xhr.open('POST', '/import_can_log', true);";
content += "xhr.upload.onprogress = (event) => { if (event.lengthComputable) { const percent = (event.loaded / event.total) * 100; progressContainer.style.display = 'block'; progressBar.style.width = percent + '%'; }};";
content += "xhr.onload = () => { if (xhr.status === 200) { alert('File uploaded successfully!'); progressBar.style.width = '100%'; const reader = new FileReader(); reader.onload = function (e) { fileContent.textContent = e.target.result; }; reader.readAsText(selectedFile); } else { alert('Upload failed! Server error.'); }};";
content += "xhr.send(formData);";
content += "});";
content += "</script>";

content += "</div>";

Expand All @@ -65,7 +111,6 @@ String can_replay_processor(void) {
content += " xhr.open('GET', '/setCANInterface?interface=' + selectedInterface, true);";
content += " xhr.send();";
content += "}";
content += "function importLog() { window.location.href = '/import_can_log'; }";
content += "function stopPlaybackAndGoToMainPage() {";
content += " fetch('/stop_can_logging').then(() => window.location.href = '/');";
content += "}";
Expand Down
18 changes: 18 additions & 0 deletions Software/src/devboard/webserver/webserver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,24 @@ void init_webserver() {
request->send(response);
});

// Route to handle setting the CAN interface for CAN replay
server.on("/setCANInterface", HTTP_GET, [](AsyncWebServerRequest* request) {
if (request->hasParam("interface")) {
String canInterface = request->getParam("interface")->value();

// Convert the received value to an integer
int interfaceValue = canInterface.toInt();

// Update the datalayer with the selected interface
datalayer.system.info.can_replay_interface = interfaceValue;

// Respond with success message
request->send(200, "text/plain", "CAN Interface Updated to " + canInterface);
} else {
request->send(400, "text/plain", "Error: Missing parameter 'interface'");
}
});

#if defined(DEBUG_VIA_WEB) || defined(LOG_TO_SD)
// Route for going to debug logging web page
server.on("/log", HTTP_GET, [](AsyncWebServerRequest* request) {
Expand Down

0 comments on commit d19994c

Please sign in to comment.