Show negative response codes in log output.
[apps/low-level-can-service.git] / src / uds / uds.c
index 443b05a..5a45c0c 100644 (file)
@@ -1,5 +1,6 @@
 #include <uds/uds.h>
 #include <bitfield/bitfield.h>
+#include <canutil/read.h>
 #include <string.h>
 #include <limits.h>
 #include <stddef.h>
@@ -35,7 +36,8 @@ static void setup_receive_handle(DiagnosticRequestHandle* handle) {
         for(response_id = 0;
                 response_id < OBD2_FUNCTIONAL_RESPONSE_COUNT; ++response_id) {
             handle->isotp_receive_handles[response_id] = isotp_receive(
-                    &handle->isotp_shims, OBD2_FUNCTIONAL_RESPONSE_START + response_id,
+                    &handle->isotp_shims,
+                    OBD2_FUNCTIONAL_RESPONSE_START + response_id,
                     NULL);
         }
         handle->isotp_receive_handle_count = OBD2_FUNCTIONAL_RESPONSE_COUNT;
@@ -47,6 +49,17 @@ static void setup_receive_handle(DiagnosticRequestHandle* handle) {
     }
 }
 
+static uint16_t autoset_pid_length(uint8_t mode, uint16_t pid,
+        uint8_t pid_length) {
+    if(pid_length == 0) {
+        if(pid > 0xffff || mode > 10) {
+            pid_length = 2;
+        } else {
+            pid_length = 1;
+        }
+    }
+    return pid_length;
+}
 
 DiagnosticRequestHandle diagnostic_request(DiagnosticShims* shims,
         DiagnosticRequest* request, DiagnosticResponseReceived callback) {
@@ -59,7 +72,10 @@ DiagnosticRequestHandle diagnostic_request(DiagnosticShims* shims,
 
     uint8_t payload[MAX_DIAGNOSTIC_PAYLOAD_SIZE] = {0};
     payload[MODE_BYTE_INDEX] = request->mode;
-    if(request->pid_length > 0) {
+    if(request->has_pid) {
+        request->pid_length = autoset_pid_length(request->mode,
+                request->pid, request->pid_length);
+        handle.request.pid_length = request->pid_length;
         set_bitfield(request->pid, PID_BYTE_INDEX * CHAR_BIT,
                 request->pid_length * CHAR_BIT, payload, sizeof(payload));
     }
@@ -71,24 +87,16 @@ DiagnosticRequestHandle diagnostic_request(DiagnosticShims* shims,
     handle.isotp_shims = isotp_init_shims(shims->log,
             shims->send_can_message,
             shims->set_timer);
+    handle.isotp_shims.frame_padding = !request->no_frame_padding;
 
     handle.isotp_send_handle = isotp_send(&handle.isotp_shims,
             request->arbitration_id, payload,
             1 + request->payload_length + request->pid_length,
             NULL);
     if(shims->log != NULL) {
-        shims->log("Sending diagnostic request: arb_id: 0x%02x, mode: 0x%x, pid: 0x%x, payload: 0x%02x%02x%02x%02x%02x%02x%02x, size: %d\r\n",
-                request->arbitration_id,
-                request->mode,
-                request->pid,
-                request->payload[0],
-                request->payload[1],
-                request->payload[2],
-                request->payload[3],
-                request->payload[4],
-                request->payload[5],
-                request->payload[6],
-                request->payload_length);
+        char request_string[128] = {0};
+        diagnostic_request_to_string(request, request_string, sizeof(request_string));
+        shims->log("Sending diagnostic request: %s", request_string);
     }
 
     setup_receive_handle(&handle);
@@ -105,7 +113,7 @@ DiagnosticRequestHandle diagnostic_request(DiagnosticShims* shims,
     // that are just called continuously from the main loop. it's a waste of a
     // few cpu cycles but it may be more  natural than callbacks.
     //
-    // what woudl a timer callback look like...it would need to pass the handle
+    // what would a timer callback look like...it would need to pass the handle
     // and that's all. seems like a context void* would be able to capture all
     // of the information but arg, memory allocation. look at how it's done in
     // the other library again
@@ -119,8 +127,8 @@ DiagnosticRequestHandle diagnostic_request_pid(DiagnosticShims* shims,
     DiagnosticRequest request = {
         arbitration_id: arbitration_id,
         mode: pid_request_type == DIAGNOSTIC_STANDARD_PID ? 0x1 : 0x22,
-        pid: pid,
-        pid_length: pid_request_type == DIAGNOSTIC_STANDARD_PID ? 1 : 2
+        has_pid: true,
+        pid: pid
     };
 
     return diagnostic_request(shims, &request, callback);
@@ -136,7 +144,8 @@ static bool handle_negative_response(IsoTpMessage* message,
         }
 
         if(message->size > NEGATIVE_RESPONSE_NRC_INDEX) {
-            response->negative_response_code = message->payload[NEGATIVE_RESPONSE_NRC_INDEX];
+            response->negative_response_code =
+                    message->payload[NEGATIVE_RESPONSE_NRC_INDEX];
         }
 
         response->success = false;
@@ -155,7 +164,7 @@ static bool handle_positive_response(DiagnosticRequestHandle* handle,
         // if it matched
         response->mode = handle->request.mode;
         response->has_pid = false;
-        if(handle->request.pid_length > 0 && message->size > 1) {
+        if(handle->request.has_pid && message->size > 1) {
             response->has_pid = true;
             if(handle->request.pid_length == 2) {
                 response->pid = get_bitfield(message->payload, message->size,
@@ -173,7 +182,7 @@ static bool handle_positive_response(DiagnosticRequestHandle* handle,
                     response->payload_length);
         }
 
-        if((handle->request.pid_length == 0 && !response->has_pid)
+        if((!handle->request.has_pid && !response->has_pid)
                 || response->pid == handle->request.pid) {
             response->success = true;
             response->completed = true;
@@ -201,43 +210,155 @@ DiagnosticResponse diagnostic_receive_can_frame(DiagnosticShims* shims,
         uint8_t i;
         for(i = 0; i < handle->isotp_receive_handle_count; ++i) {
             IsoTpMessage message = isotp_continue_receive(&handle->isotp_shims,
-                    &handle->isotp_receive_handles[i], arbitration_id, data, size);
+                    &handle->isotp_receive_handles[i], arbitration_id, data,
+                    size);
 
-            // TODO as of now we're completing the handle as soon as one
-            // broadcast response is received....need to hang on for 100ms
             if(message.completed) {
                 if(message.size > 0) {
                     response.mode = message.payload[0];
                     if(handle_negative_response(&message, &response, shims)) {
-                        shims->log("Received a negative response to mode %d on arb ID 0x%x",
-                                response.mode, response.arbitration_id);
+                        if(shims->log != NULL) {
+                            char response_string[128] = {0};
+                            diagnostic_response_to_string(&response, response_string, sizeof(response_string));
+                            shims->log("Received a negative response: %s", response_string);
+                        }
+
                         handle->success = true;
                         handle->completed = true;
-                    } else if(handle_positive_response(handle, &message, &response,
-                                shims)) {
-                        shims->log("Received a positive mode %d response on arb ID 0x%x",
-                                response.mode, response.arbitration_id);
+                    } else if(handle_positive_response(handle, &message,
+                                &response, shims)) {
+                        if(shims->log != NULL) {
+                            char response_string[128] = {0};
+                            diagnostic_response_to_string(&response, response_string, sizeof(response_string));
+                            shims->log("Received a positive response: %s", response_string);
+                        }
+
                         handle->success = true;
                         handle->completed = true;
                     } else {
-                        shims->log("Response was for a mode 0x%x request (pid 0x%x), not our mode 0x%x request (pid 0x%x)",
-                                MAX(0, response.mode - MODE_RESPONSE_OFFSET),
-                                response.pid, handle->request.mode,
-                                handle->request.pid);
-                        // TODO just leave handles open until the user decides
-                        // to be done with it - keep a count of valid responses
-                        // received.
+                        if(shims->log != NULL) {
+                            char response_string[128] = {0};
+                            diagnostic_response_to_string(&response, response_string, sizeof(response_string));
+                            shims->log("Expected a mode 0x%x response to pid 0x%x but received: %s",
+                                    MAX(0, response.mode - MODE_RESPONSE_OFFSET),
+                                    response.pid,
+                                    response_string);
+                        }
                     }
                 } else {
-                    shims->log("Received an empty response on arb ID 0x%x",
-                            response.arbitration_id);
+                    if(shims->log != NULL) {
+                        shims->log("Received an empty response on arb ID 0x%x",
+                                response.arbitration_id);
+                    }
                 }
 
                 if(handle->completed && handle->callback != NULL) {
                     handle->callback(&response);
                 }
+
+                break;
             }
         }
     }
     return response;
 }
+
+int diagnostic_payload_to_integer(const DiagnosticResponse* response) {
+    return get_bitfield(response->payload, response->payload_length, 0,
+            response->payload_length * CHAR_BIT);
+}
+
+float diagnostic_decode_obd2_pid(const DiagnosticResponse* response,
+        int parsed_payload) {
+    // handles on the single number values, not the bit encoded ones
+    switch(response->pid) {
+        case 0xa:
+            return response->payload[0] * 3;
+        case 0xc:
+            return (response->payload[0] * 256 + response->payload[1]) / 4.0;
+        case 0xd:
+        case 0x33:
+        case 0xb:
+            return response->payload[0];
+        case 0x10:
+            return (response->payload[0] * 256 + response->payload[1]) / 100.0;
+        case 0x11:
+        case 0x2f:
+        case 0x45:
+        case 0x4c:
+        case 0x52:
+        case 0x5a:
+        case 0x4:
+            return response->payload[0] * 100.0 / 255.0;
+        case 0x46:
+        case 0x5c:
+        case 0xf:
+        case 0x5:
+            return response->payload[0] - 40;
+        case 0x62:
+            return response->payload[0] - 125;
+        default:
+            return 0;
+    }
+}
+
+void diagnostic_response_to_string(const DiagnosticResponse* response,
+        char* destination, size_t destination_length) {
+    int bytes_used = snprintf(destination, destination_length,
+            "arb_id: 0x%02x, mode: 0x%x, ",
+            response->arbitration_id,
+            response->mode);
+
+    if(response->has_pid) {
+        bytes_used += snprintf(destination + bytes_used,
+                destination_length - bytes_used,
+                "pid: 0x%x, ",
+                response->pid);
+    }
+
+    if(!response->success) {
+        bytes_used += snprintf(destination + bytes_used,
+                destination_length - bytes_used,
+                "negative response code: 0x%x, ",
+                response->negative_response_code);
+    }
+
+    if(response->payload_length > 0) {
+        snprintf(destination + bytes_used, destination_length - bytes_used,
+                "payload: 0x%02x%02x%02x%02x%02x%02x%02x",
+                response->payload[0],
+                response->payload[1],
+                response->payload[2],
+                response->payload[3],
+                response->payload[4],
+                response->payload[5],
+                response->payload[6]);
+    } else {
+        snprintf(destination + bytes_used, destination_length - bytes_used,
+                "no payload");
+    }
+}
+
+void diagnostic_request_to_string(const DiagnosticRequest* request,
+        char* destination, size_t destination_length) {
+    int bytes_used = snprintf(destination, destination_length,
+            "arb_id: 0x%02x, mode: 0x%x, pid: 0x%x, ",
+            request->arbitration_id,
+            request->mode,
+            request->pid);
+    int remaining_space = destination_length - bytes_used;
+    if(request->payload_length > 0) {
+        snprintf(destination + bytes_used, remaining_space,
+                "payload: 0x%02x%02x%02x%02x%02x%02x%02x",
+                request->payload[0],
+                request->payload[1],
+                request->payload[2],
+                request->payload[3],
+                request->payload[4],
+                request->payload[5],
+                request->payload[6]);
+    } else {
+        snprintf(destination + bytes_used, remaining_space, "no payload");
+    }
+}
+