diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/CMakeLists.txt | 92 | ||||
-rw-r--r-- | src/can-decoder.cpp | 93 | ||||
-rw-r--r-- | src/can-decoder.h | 146 | ||||
-rw-r--r-- | src/can-signals.cpp | 40 | ||||
-rw-r--r-- | src/can-signals.h | 84 | ||||
-rw-r--r-- | src/can-utils.cpp | 226 | ||||
-rw-r--r-- | src/can-utils.h | 396 | ||||
-rw-r--r-- | src/can_decode_message.cpp | 40 | ||||
-rw-r--r-- | src/can_event_push.cpp | 38 | ||||
-rw-r--r-- | src/can_reader.cpp | 66 | ||||
-rw-r--r-- | src/config.xml.in | 11 | ||||
-rw-r--r-- | src/export.map | 1 | ||||
-rw-r--r-- | src/icon.png | bin | 0 -> 4035 bytes | |||
m--------- | src/libs/bitfield-c | 6 | ||||
m--------- | src/libs/nanopb | 9 | ||||
m--------- | src/libs/openxc-message-format | 9 | ||||
m--------- | src/libs/uds-c | 10 | ||||
-rw-r--r-- | src/low-can-binding.cpp | 253 | ||||
-rw-r--r-- | src/obd2.cpp | 59 | ||||
-rw-r--r-- | src/obd2.h | 150 | ||||
-rw-r--r-- | src/timer.h | 33 |
21 files changed, 1762 insertions, 0 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt new file mode 100644 index 00000000..768d6b47 --- /dev/null +++ b/src/CMakeLists.txt @@ -0,0 +1,92 @@ +########################################################################### +# Copyright 2016 IoT.bzh +# +# author: José Bollo <jose.bollo@iot.bzh> +# author: Stéphane Desneux <stephane.desneux@iot.bzh> +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +########################################################################### + +project(ll-can) + +cmake_minimum_required(VERSION 3.3) + +include(GNUInstallDirs) + +set(PROJECT_VERSION "0.1") +set(PROJECT_ICON "icon.png") +set(PROJECT_LIBDIR "libs") + +set(CMAKE_BUILD_TYPE Debug) + +########################################################################### + +link_libraries(-Wl,--as-needed -Wl,--gc-sections) + +add_compile_options(-Wall -Wextra -Wconversion) +add_compile_options(-Wno-unused-parameter) # frankly not using a parameter does it care? +add_compile_options(-Werror=maybe-uninitialized) +add_compile_options(-Werror=implicit-function-declaration) +add_compile_options(-ffunction-sections -fdata-sections) +add_compile_options(-Wl,--as-needed -Wl,--gc-sections) +add_compile_options(-fPIC) + +set(CMAKE_C_FLAGS_PROFILING "-g -O0 -pg -Wp,-U_FORTIFY_SOURCE") +set(CMAKE_C_FLAGS_DEBUG "-g -O0 -ggdb -Wp,-U_FORTIFY_SOURCE") +set(CMAKE_C_FLAGS_RELEASE "-g -O2") +set(CMAKE_C_FLAGS_CCOV "-g -O2 --coverage") + +set(CMAKE_CXX_FLAGS_PROFILING "-g -O0 -pg -Wp,-U_FORTIFY_SOURCE") +set(CMAKE_CXX_FLAGS_DEBUG "-g -O0 -ggdb -Wp,-U_FORTIFY_SOURCE") +set(CMAKE_CXX_FLAGS_RELEASE "-g -O2") +set(CMAKE_CXX_FLAGS_CCOV "-g -O2 --coverage") + +########################################################################### + +include(FindPkgConfig) + +pkg_check_modules(EXTRAS REQUIRED json-c afb-daemon) +add_compile_options(${EXTRAS_CFLAGS}) +include_directories(${EXTRAS_INCLUDE_DIRS} ${PROJECT_LIBDIR}/openxc-message-format/gen/cpp ${PROJECT_LIBDIR}/nanopb/) +link_libraries(${EXTRAS_LIBRARIES}) + +########################################################################### +# the binding for afb + +message(STATUS "Creation of ${PROJECT_NAME} binding for AFB-DAEMON") +########################################################################### +add_library(${PROJECT_NAME}-binding MODULE ${PROJECT_NAME}-binding.c) + +set_target_properties(${PROJECT_NAME}-binding PROPERTIES + PREFIX "" + LINK_FLAGS "-Wl,--version-script=${CMAKE_CURRENT_SOURCE_DIR}/export.map" +) + +########################################################################### +# WGT packaging + +message(STATUS "Creation of ${PROJECT_NAME}.wgt package for AppFW") +############################################################### +configure_file(config.xml.in config.xml) + +add_custom_command( + OUTPUT ${PROJECT_NAME}.wgt + DEPENDS ${PROJECT_NAME}-binding + COMMAND rm -rf package + COMMAND mkdir -p package/${PROJECT_LIBDIR} package/htdocs + COMMAND cp config.xml package/ + COMMAND cp ${CMAKE_CURRENT_SOURCE_DIR}/${PROJECT_ICON} package/icon.png + COMMAND cp ${PROJECT_NAME}-binding.so package + COMMAND wgtpkg-pack -f -o ${PROJECT_NAME}.wgt package +) +add_custom_target(widget ALL DEPENDS ${PROJECT_NAME}.wgt) diff --git a/src/can-decoder.cpp b/src/can-decoder.cpp new file mode 100644 index 00000000..000db2ac --- /dev/null +++ b/src/can-decoder.cpp @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2015, 2016 "IoT.bzh" + * Author "Romain Forlot" <romain.forlot@iot.bzh> + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +Decoder::Decoder +{ + decoded_value = { .has_type = false, + .has_numeric_value = false, + .has_boolean_value = false, + .has_string_value = false }; +} + +float Decoder::parseSignalBitfield(CanSignal* signal, const CanMessage* message) +{ + return bitfield_parse_float(message->data, CAN_MESSAGE_SIZE, + signal->bitPosition, signal->bitSize, signal->factor, + signal->offset); +} + +openxc_DynamicField Decoder::noopDecoder(CanSignal* signal, + CanSignal* signals, int signalCount, float value, bool* send) +{ + decoded_value = { .has_type = true, + .type = openxc_DynamicField_Type_NUM, + .has_numeric_value = true, + .numeric_value = value }; + return decoded_value; +} + +openxc_DynamicField Decoder::booleanDecoder(CanSignal* signal, + CanSignal* signals, int signalCount, float value, bool* send) +{ + decoded_value = { .has_type = true, + .type = openxc_DynamicField_Type_BOOL, + .has_boolean_value = true, + .numeric_value = value == 0.0 ? false : true }; + return decoded_value; +} + +openxc_DynamicField Decoder::ignoreDecoder(CanSignal* signal, + CanSignal* signals, int signalCount, float value, bool* send) +{ + *send = false; + openxc_DynamicField decoded_value = {0}; + return decoded_value; +} + +openxc_DynamicField Decoder::stateDecoder(CanSignal* signal, + CanSignal* signals, int signalCount, float value, bool* send) +{ + openxc_DynamicField decoded_value = {0}; + decoded_value.has_type = true; + decoded_value.type = openxc_DynamicField_Type_STRING; + decoded_value.has_string_value = true; + + const CanSignalState* signalState = lookupSignalState(value, signal); + if(signalState != NULL) { + strcpy(decoded_value.string_value, signalState->name); + } else { + *send = false; + } + return decoded_value; +} + +openxc_DynamicField Decoder::decodeSignal(CanSignal* signal, + float value, CanSignal* signals, int signalCount, bool* send) +{ + SignalDecoder decoder = signal->decoder == NULL ? + noopDecoder : signal->decoder; + openxc_DynamicField decoded_value = decoder(signal, signals, + signalCount, value, send); + return decoded_value; +} + +openxc_DynamicField Decoder::decodeSignal(CanSignal* signal, + const CanMessage* message, CanSignal* signals, int signalCount, + bool* send) { + float value = parseSignalBitfield(signal, message); + return decodeSignal(signal, value, signals, signalCount, send); +}
\ No newline at end of file diff --git a/src/can-decoder.h b/src/can-decoder.h new file mode 100644 index 00000000..249d5b00 --- /dev/null +++ b/src/can-decoder.h @@ -0,0 +1,146 @@ +/* + * Copyright (C) 2015, 2016 "IoT.bzh" + * Author "Romain Forlot" <romain.forlot@iot.bzh> + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + + +class Decoder_c +{ + private: + openxc_DynamicField decoded_value; + + public: + /* Public: Parse the signal's bitfield from the given data and return the raw + * value. + * + * signal - The signal to parse from the data. + * data - The data to parse the signal from. + * length - The length of the data array. + * + * Returns the raw value of the signal parsed as a bitfield from the given byte + * array. + */ + float parseSignalBitfield(CanSignal* signal, const CanMessage* message); + + /* Public: Find and return the corresponding string state for a CAN signal's + * raw integer value. + * + * This is an implementation of the SignalDecoder type signature, and can be + * used directly in the CanSignal.decoder field. + * + * signal - The details of the signal that contains the state mapping. + * signals - The list of all signals. + * signalCount - the length of the signals array. + * value - The numerical value that should map to a state. + * send - An output argument that will be set to false if the value should + * not be sent for any reason. + * + * Returns a DynamicField with a string value if a matching state is found in + * the signal. If an equivalent isn't found, send is sent to false and the + * return value is undefined. + */ + openxc_DynamicField stateDecoder(CanSignal* signal, CanSignal* signals, + int signalCount, float value, bool* send); + + /* Public: Coerces a numerical value to a boolean. + * + * This is an implementation of the SignalDecoder type signature, and can be + * used directly in the CanSignal.decoder field. + * + * signal - The details of the signal that contains the state mapping. + * signals - The list of all signals + * signalCount - The length of the signals array + * value - The numerical value that will be converted to a boolean. + * send - An output argument that will be set to false if the value should + * not be sent for any reason. + * + * Returns a DynamicField with a boolean value of false if the raw signal value + * is 0.0, otherwise true. The 'send' argument will not be modified as this + * decoder always succeeds. + */ + openxc_DynamicField booleanDecoder(CanSignal* signal, CanSignal* signals, + int signalCount, float value, bool* send); + + /* Public: Update the metadata for a signal and the newly received value. + * + * This is an implementation of the SignalDecoder type signature, and can be + * used directly in the CanSignal.decoder field. + * + * This function always flips 'send' to false. + * + * signal - The details of the signal that contains the state mapping. + * signals - The list of all signals. + * signalCount - The length of the signals array. + * value - The numerical value that will be converted to a boolean. + * send - This output argument will always be set to false, so the caller will + * know not to publish this value to the pipeline. + * + * The return value is undefined. + */ + openxc_DynamicField ignoreDecoder(CanSignal* signal, CanSignal* signals, + int signalCount, float value, bool* send); + + /* Public: Wrap a raw CAN signal value in a DynamicField without modification. + * + * This is an implementation of the SignalDecoder type signature, and can be + * used directly in the CanSignal.decoder field. + * + * signal - The details of the signal that contains the state mapping. + * signals - The list of all signals + * signalCount - The length of the signals array + * value - The numerical value that will be wrapped in a DynamicField. + * send - An output argument that will be set to false if the value should + * not be sent for any reason. + * + * Returns a DynamicField with the original, unmodified raw CAN signal value as + * its numeric value. The 'send' argument will not be modified as this decoder + * always succeeds. + */ + openxc_DynamicField noopDecoder(CanSignal* signal, CanSignal* signals, + int signalCount, float value, bool* send); + + /* Public: Parse a signal from a CAN message and apply any required + * transforations to get a human readable value. + * + * If the CanSignal has a non-NULL 'decoder' field, the raw CAN signal value + * will be passed to the decoder before returning. + * + * signal - The details of the signal to decode and forward. + * message - The CAN message that contains the signal. + * signals - an array of all active signals. + * signalCount - The length of the signals array. + * send - An output parameter that will be flipped to false if the value could + * not be decoded. + * + * The decoder returns an openxc_DynamicField, which may contain a number, + * string or boolean. If 'send' is false, the return value is undefined. + */ + openxc_DynamicField decodeSignal(CanSignal* signal, + const CanMessage* message, CanSignal* signals, int signalCount, + bool* send); + + /* Public: Decode a transformed, human readable value from an raw CAN signal + * already parsed from a CAN message. + * + * This is the same as decodeSignal(CanSignal*, CanMessage*, CanSignal*, int, + * bool*) but you must parse the bitfield value of the signal from the CAN + * message yourself. This is useful if you need that raw value for something + * else. + */ + openxc_DynamicField decodeSignal(CanSignal* signal, float value, + CanSignal* signals, int signalCount, bool* send); +}
\ No newline at end of file diff --git a/src/can-signals.cpp b/src/can-signals.cpp new file mode 100644 index 00000000..13ff30b1 --- /dev/null +++ b/src/can-signals.cpp @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2015, 2016 "IoT.bzh" + * Author "Romain Forlot" <romain.forlot@iot.bzh> + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <string> +#include "can-signals.h" + +bool match(const std::string &first, const std::string &second) +{ + +} + +CanSignal* getSignals(std::string name) +{ + int n_signals, i; + CanSignal ret_signals[]; + + n_signals = size(SIGNALS); + + for(i=0; i<=n_signals; i++) + { + if(SIGNALS[i].generic_name == name) + return &SIGNALS[i]; + i++; + } + return 0; +} diff --git a/src/can-signals.h b/src/can-signals.h new file mode 100644 index 00000000..f34c743e --- /dev/null +++ b/src/can-signals.h @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2015, 2016 "IoT.bzh" + * Author "Romain Forlot" <romain.forlot@iot.bzh> + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "can-utils.h" +#include <string> + +/** Public: Return the currently active CAN configuration. */ +CanMessageSet* getActiveMessageSet(); + +/** Public: Retrive a list of all possible CAN configurations. + * * + * * Returns a pointer to an array of all configurations. + * */ +CanMessageSet* getMessageSets(); + +/** Public: Return the length of the array returned by getMessageSets() */ +int getMessageSetCount(); + +/* Public: Return the number of CAN buses configured in the active + * * configuration. This is limited to 2, as the hardware controller only has 2 + * * CAN channels. + * */ +int getCanBusCount(); + +/* Public: Return an array of all CAN messages to be processed in the active + * * configuration. + * */ +CanMessageDefinition* getMessages(); + +/* Public: Return signals from an signals array filtered on name. + */ +CanSignal* getSignals(std::string name); + +/* Public: Return an array of all OpenXC CAN commands enabled in the active + * * configuration that can write back to CAN with a custom handler. + * * + * * Commands not defined here are handled using a 1-1 mapping from the signals + * * list. + * */ +CanCommand* getCommands(); + +/* Public: Return the length of the array returned by getCommandCount(). */ +int getCommandCount(); + +/* Public: Return the length of the array returned by getSignals(). */ +int getSignalCount(); + +/* Public: Return the length of the array returned by getMessages(). */ +int getMessageCount(); + +/* Public: Return an array of the metadata for the 2 CAN buses you want to + * * monitor. The size of this array is fixed at 2. + * */ +CanBus* getCanBuses(); + +/* Public: Decode CAN signals from raw CAN messages, translate from engineering + * * units to something more human readable, and send the resulting value over USB + * * as an OpenXC-style JSON message. + * * + * * This is the main workhorse function of the VI. Every time a new + * * CAN message is received that matches one of the signals in the list returend + * * by getSignals(), this function is called with the message ID and 64-bit data + * * field. + * * + * * bus - The CAN bus this message was received on. + * * message - The received CAN message. + * */ +void decodeCanMessage(openxc::pipeline::Pipeline* pipeline, CanBus* bus, CanMessage* message); diff --git a/src/can-utils.cpp b/src/can-utils.cpp new file mode 100644 index 00000000..67214105 --- /dev/null +++ b/src/can-utils.cpp @@ -0,0 +1,226 @@ +/* + * Copyright (C) 2015, 2016 "IoT.bzh" + * Author "Romain Forlot" <romain.forlot@iot.bzh> + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +/******************************************************************************** +* +* CanBus method implementation +* +*********************************************************************************/ + +CanBus_c::CanBus_c(afb_binding_interface *itf, const std:string& dev_name) +{ + interface = itf; + deviceName = dev_name; +} + +int CanBus_c::open() +{ + const int canfd_on = 1; + struct ifreq ifr; + struct timeval timeout = {1, 0}; + + DEBUG(interface, "open_can_dev: CAN Handler socket : %d", socket); + if (socket >= 0) + close(socket); + + socket = socket(PF_CAN, SOCK_RAW, CAN_RAW); + if (socket < 0) + { + ERROR(interface, "open_can_dev: socket could not be created"); + } + else + { + /* Set timeout for read */ + setsockopt(socket, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout, sizeof(timeout)); + /* try to switch the socket into CAN_FD mode */ + if (setsockopt(socket, SOL_CAN_RAW, CAN_RAW_FD_FRAMES, &canfd_on, sizeof(canfd_on)) < 0) + { + NOTICE(interface, "open_can_dev: Can not switch into CAN Extended frame format."); + is_fdmode_on = false; + } else { + is_fdmode_on = true; + } + + /* Attempts to open a socket to CAN bus */ + strcpy(ifr.ifr_name, device); + if(ioctl(socket, SIOCGIFINDEX, &ifr) < 0) + ERROR(interface, "open_can_dev: ioctl failed"); + else + { + txAddress.can_family = AF_CAN; + txAddress.can_ifindex = ifr.ifr_ifindex; + + /* And bind it to txAddress */ + if (bind(socket, (struct sockaddr *)&txAddress, sizeof(txAddress)) < 0) + { + ERROR(interface, "open_can_dev: bind failed"); + } + else + { + fcntl(socket, F_SETFL, O_NONBLOCK); + return 0; + } + } + close(socket); + socket = -1; + } + return -1; +} + +int CanBus_c::close() +{ + close(socket); + socket = -1; +} + +void CanBus_c::start_threads() +{ + std::queue <CanMessage_c> can_message_q; + + th_reading = std::thread(can_reader, interface, socket, can_message_q); + th_decoding = std::thread(can_decoder, interface, can_message_q, can_message_q); + th_pushing = std::thread(can_event_push, interface, can_message_q); +} + +/* + * Send a can message from a CanMessage_c object. + */ +int CanBus_c::send_can_message(CanMessage_c can_msg) +{ + int nbytes; + canfd_frame *f; + + f = can_msg.convert_to_canfd_frame(); + + if(socket >= 0) + { + nbytes = sendto(socket, &f, sizeof(struct canfd_frame), 0, + (struct sockaddr*)&txAddress, sizeof(txAddress)); + + if (nbytes == -1) + { + ERROR(interface, "send_can_message: Sending CAN frame failed."); + return -1; + } + return nbytes; + } + else + { + ERROR(interface, "send_can_message: socket not initialized. Attempt to reopen can device socket."); + open_can_dev(); + } + return 0; +} + +/******************************************************************************** +* +* CanMessage method implementation +* +*********************************************************************************/ + +uint32_t CanMessage_c::get_id() +{ + return id; +} + +int CanMessage_c::get_format() +{ + return format; +} + +uint8_t CanMessage_c::get_data() +{ + return data; +} +uint8_t CanMessage_c::get_lenght() +{ + return lenght; +} + +void CanMessage_c::set_id(uint32_t new_id) +{ + switch(format): + case CanMessageFormat::SIMPLE: + id = new_id & CAN_SFF_MASK; + case CanMessageFormat::EXTENDED: + id = new_id & CAN_EFF_MASK; + default: + ERROR(interface, "ERROR: Can set id, not a compatible format or format not set prior to set id."); +} + +void CanMessage_c::set_format(CanMessageFormat new_format) +{ + if(new_format == CanMessageFormat::SIMPLE || new_format == CanMessageFormat::EXTENDED) + format = new_format; + else + ERROR(interface, "ERROR: Can set format, wrong format chosen"); +} + +void CanMessage_c::set_data(uint8_t new_data) +{ + data = new_data; +} + +void CanMessage_c::set_lenght(uint8_t new_length) +{ + lenght = new_lenght; +} + +/* + * This is the preferred way to initialize a CanMessage object + * from a read canfd_frame message. + * + * params: canfd_frame pointer + */ +void CanMessage_c::convert_from_canfd_frame(canfd_frame *frame) +{ + + lenght = (canfd_frame->len > maxdlen) ? maxdlen : canfd_frame->len; + + switch (canfd_frame->can_id): + case (canfd_frame->can_id & CAN_ERR_FLAG): + id = canfd_frame->can_id & (CAN_ERR_MASK|CAN_ERR_FLAG); + break; + case (canfd_frame->can_id & CAN_EFF_FLAG): + id = canfd_frame->can_id & CAN_EFF_MASK; + format = CanMessageFormat::EXTENDED; + break; + default: + format = CanMessageFormat::STANDARD; + id = canfd_frame->can_id & CAN_SFF_MASK; + break; + + if (sizeof(canfd_frame->data) <= sizeof(data)) + { + memcpy(data, canfd_frame->data, lenght); + return 0; + } else if (sizeof(canfd_frame->data) >= CAN_MAX_DLEN) + ERROR(interface, "CanMessage_c: canfd_frame data too long to be stored into CanMessage object"); +} + +canfd_frame* convert_to_canfd_frame() +{ + canfd_frame frame; + + frame.id = can_msg.get_id(); + frame.len = can_msg.get_lenght(); + frame.data = can_msg.get_data(); + + return &frame; +} diff --git a/src/can-utils.h b/src/can-utils.h new file mode 100644 index 00000000..8c850e6e --- /dev/null +++ b/src/can-utils.h @@ -0,0 +1,396 @@ +/* + * Copyright (C) 2015, 2016 "IoT.bzh" + * Author "Romain Forlot" <romain.forlot@iot.bzh> + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <string> +#include "timer.h" +#include "openxc.pb.h" + +// TODO actual max is 32 but dropped to 24 for memory considerations +#define MAX_ACCEPTANCE_FILTERS 24 +// TODO this takes up a ton of memory +#define MAX_DYNAMIC_MESSAGE_COUNT 12 + +#define CAN_MESSAGE_SIZE 8 + +#define CAN_ACTIVE_TIMEOUT_S 30 + +#define QUEUE_DECLARE(type, max_length) \ +static const int queue_##type##_max_length = max_length; \ +static const int queue_##type##_max_internal_length = max_length + 1; \ +typedef struct queue_##type##_s { \ + int head; \ + int tail; \ + type elements[max_length + 1]; \ +} queue_##type; \ +\ +bool queue_##type##_push(queue_##type* queue, type value); \ +\ +type queue_##type##_pop(queue_##type* queue); \ +\ +type queue_##type##_peek(queue_##type* queue); \ +void queue_##type##_init(queue_##type* queue); \ +int queue_##type##_length(queue_##type* queue); \ +int queue_##type##_available(queue_##type* queue); \ +bool queue_##type##_full(queue_##type* queue); \ +bool queue_##type##_empty(queue_##type* queue); \ +void queue_##type##_snapshot(queue_##type* queue, type* snapshot, int max); + +/* Public: The type signature for a CAN signal decoder. + * + * A SignalDecoder transforms a raw floating point CAN signal into a number, + * string or boolean. + * + * signal - The CAN signal that we are decoding. + * signals - The list of all signals. + * signalCount - The length of the signals array. + * value - The CAN signal parsed from the message as a raw floating point + * value. + * send - An output parameter. If the decoding failed or the CAN signal should + * not send for some other reason, this should be flipped to false. + * + * Returns a decoded value in an openxc_DynamicField struct. + */ +typedef openxc_DynamicField (*SignalDecoder)(struct CanSignal* signal, + CanSignal* signals, int signalCount, float value, bool* send); + +/* Public: The type signature for a CAN signal encoder. + * + * A SignalEncoder transforms a number, string or boolean into a raw floating + * point value that fits in the CAN signal. + * + * signal - The CAN signal to encode. + * value - The dynamic field to encode. + * send - An output parameter. If the encoding failed or the CAN signal should + * not be encoded for some other reason, this will be flipped to false. + */ +typedef uint64_t (*SignalEncoder)(struct CanSignal* signal, + openxc_DynamicField* value, bool* send); + +/* + * CanBus represent a can device definition gotten from configuraiton file + */ +class CanBus_c { + private: + afb_binding_interface *interface; + + /* Got from conf file */ + std::string deviceName; + + int socket; + bool is_fdmode_on; + struct sockaddr_can txAddress; + + std::thread th_reading; + std::thread th_decoding; + std::thread th_pushing; + std::queue <CanMessage_c> can_message_q; + std::queue <openxc_VehicleMessage> VehicleMessage_q; + + public: + int open(); + int close(); + + void start_threads(); + int send_can_message(CanMessage_c can_msg); +}; + +/* A compact representation of a single CAN message, meant to be used in in/out + * buffers. + * + * id - The ID of the message. + * format - the format of the message's ID. + * data - The message's data field. + * length - the length of the data array (max 8). +struct CanMessage { + uint32_t id; + CanMessageFormat format; + uint8_t data[CAN_MESSAGE_SIZE]; + uint8_t length; +}; +typedef struct CanMessage CanMessage; +*/ +class CanMessage_c { + private: + uint32_t id; + CanMessageFormat format; + uint8_t data[CAN_MESSAGE_SIZE]; + uint8_t length; + + public: + uint32_t get_id(); + int get_format(); + uint8_t get_data(); + uint8_t get_lenght(); + + void set_id(uint32_t id); + void set_format(CanMessageFormat format); + void set_data(uint8_t data); + void set_lenght(uint8_t length); + + void convert_from_canfd_frame(canfd_frame frame); + canfd_frame convert_to_canfd_frame(); +}; + +QUEUE_DECLARE(CanMessage_c, 8); + +/* Public: The ID format for a CAN message. + * + * STANDARD - standard 11-bit CAN arbitration ID. + * EXTENDED - an extended frame, with a 29-bit arbitration ID. + */ +enum CanMessageFormat { + STANDARD, + EXTENDED, +}; +typedef enum CanMessageFormat CanMessageFormat; + +/* Public: A state encoded (SED) signal's mapping from numerical values to + * OpenXC state names. + * + * value - The integer value of the state on the CAN bus. + * name - The corresponding string name for the state in OpenXC. +struct CanSignalState { + const int value; + const char* name; +}; +typedef struct CanSignalState CanSignalState; + */ + class CanSignalState_c { + private: + const int value; + const char *name; +}; + +/* Public: A CAN signal to decode from the bus and output over USB. + * + * message - The message this signal is a part of. + * genericName - The name of the signal to be output over USB. + * bitPosition - The starting bit of the signal in its CAN message (assuming + * non-inverted bit numbering, i.e. the most significant bit of + * each byte is 0) + * bitSize - The width of the bit field in the CAN message. + * factor - The final value will be multiplied by this factor. Use 1 if you + * don't need a factor. + * offset - The final value will be added to this offset. Use 0 if you + * don't need an offset. + * minValue - The minimum value for the processed signal. + * maxValue - The maximum value for the processed signal. + * frequencyClock - A FrequencyClock struct to control the maximum frequency to + * process and send this signal. To process every value, set the + * clock's frequency to 0. + * sendSame - If true, will re-send even if the value hasn't changed. + * forceSendChanged - If true, regardless of the frequency, it will send the + * value if it has changed. + * states - An array of CanSignalState describing the mapping + * between numerical and string values for valid states. + * stateCount - The length of the states array. + * writable - True if the signal is allowed to be written from the USB host + * back to CAN. Defaults to false. + * decoder - An optional function to decode a signal from the bus to a human + * readable value. If NULL, the default numerical decoder is used. + * encoder - An optional function to encode a signal value to be written to + * CAN into a byte array. If NULL, the default numerical encoder + * is used. + * received - True if this signal has ever been received. + * lastValue - The last received value of the signal. If 'received' is false, + * this value is undefined. + */ +struct CanSignal { + struct CanMessageDefinition* message; + const char* genericName; + uint8_t bitPosition; + uint8_t bitSize; + float factor; + float offset; + float minValue; + float maxValue; + FrequencyClock frequencyClock; + bool sendSame; + bool forceSendChanged; + const CanSignalState* states; + uint8_t stateCount; + bool writable; + SignalDecoder decoder; + SignalEncoder encoder; + bool received; + float lastValue; + + struct afb_event event; +}; +typedef struct CanSignal CanSignal; + +/* Public: The definition of a CAN message. This includes a lot of metadata, so + * to save memory this struct should not be used for storing incoming and + * outgoing CAN messages. + * + * bus - A pointer to the bus this message is on. + * id - The ID of the message. + * format - the format of the message's ID. + * clock - an optional frequency clock to control the output of this + * message, if sent raw, or simply to mark the max frequency for custom + * handlers to retrieve. + * forceSendChanged - If true, regardless of the frequency, it will send CAN + * message if it has changed when using raw passthrough. + * lastValue - The last received value of the message. Defaults to undefined. + * This is required for the forceSendChanged functionality, as the stack + * needs to compare an incoming CAN message with the previous frame. + */ +struct CanMessageDefinition { + struct CanBus* bus; + uint32_t id; + CanMessageFormat format; + FrequencyClock frequencyClock; + bool forceSendChanged; + uint8_t lastValue[CAN_MESSAGE_SIZE]; +}; +typedef struct CanMessageDefinition CanMessageDefinition; + +/* Private: An entry in the list of acceptance filters for each CanBus. + * + * This struct is meant to be used with a LIST type from <sys/queue.h>. + * + * filter - the value for the CAN acceptance filter. + * activeUserCount - The number of active consumers of this filter's messages. + * When 0, this filter can be removed. + * format - the format of the ID for the filter. +struct AcceptanceFilterListEntry { + uint32_t filter; + uint8_t activeUserCount; + CanMessageFormat format; + LIST_ENTRY(AcceptanceFilterListEntry) entries; +}; + */ + +/* Private: A type of list containing CAN acceptance filters. +LIST_HEAD(AcceptanceFilterList, AcceptanceFilterListEntry); + +struct CanMessageDefinitionListEntry { + CanMessageDefinition definition; + LIST_ENTRY(CanMessageDefinitionListEntry) entries; +}; +LIST_HEAD(CanMessageDefinitionList, CanMessageDefinitionListEntry); + */ + +/** Public: A parent wrapper for a particular set of CAN messages and associated + * CAN buses(e.g. a vehicle or program). + * + * index - A numerical ID for the message set, ideally the index in an array + * for fast lookup + * name - The name of the message set. + * busCount - The number of CAN buses defined for this message set. + * messageCount - The number of CAN messages (across all buses) defined for + * this message set. + * signalCount - The number of CAN signals (across all messages) defined for + * this message set. + * commandCount - The number of CanCommmands defined for this message set. +typedef struct { + uint8_t index; + const char* name; + uint8_t busCount; + unsigned short messageCount; + unsigned short signalCount; + unsigned short commandCount; +} CanMessageSet; + */ +class CanMessageSet_c { + private: + uint8_t index; + const char * name; + uint8_t busCount; + unsigned short messageCount; + unsigned short signalCount; + unsigned short commandCount; +}; + +/* Public: The type signature for a function to handle a custom OpenXC command. + * + * name - the name of the received command. + * value - the value of the received command, in a DynamicField. The actual type + * may be a number, string or bool. + * event - an optional event from the received command, in a DynamicField. The + * actual type may be a number, string or bool. + * signals - The list of all signals. + * signalCount - The length of the signals array. + */ +typedef void (*CommandHandler)(const char* name, openxc_DynamicField* value, + openxc_DynamicField* event, CanSignal* signals, int signalCount); + +/* Public: The structure to represent a supported custom OpenXC command. + * + * For completely customized CAN commands without a 1-1 mapping between an + * OpenXC message from the host and a CAN signal, you can define the name of the + * command and a custom function to handle it in the VI. An example is + * the "turn_signal_status" command in OpenXC, which has a value of "left" or + * "right". The vehicle may have separate CAN signals for the left and right + * turn signals, so you will need to implement a custom command handler to send + * the correct signals. + * + * Command handlers are also useful if you want to trigger multiple CAN messages + * or signals from a signal OpenXC message. + * + * genericName - The name of the command. + * handler - An function to process the received command's data and perform some + * action. +typedef struct { + const char* genericName; + CommandHandler handler; +} CanCommand; + */ + +class CanCommand_c { + private: + const char* genericName; + CommandHandler handler; +}; + +/* Pre initialize actions made before CAN bus initialization + * + * bus - A CanBus struct defining the bus's metadata + * writable - configure the controller in a writable mode. If false, it will be + * configured as "listen only" and will not allow writes or even CAN ACKs. + * buses - An array of all CAN buses. + * busCount - The length of the buses array. + */ +void pre_initialize(CanBus* bus, bool writable, CanBus* buses, const int busCount); + +/* Post-initialize actions made after CAN bus initialization and before the + * event loop connection. + * + * bus - A CanBus struct defining the bus's metadata + * writable - configure the controller in a writable mode. If false, it will be + * configured as "listen only" and will not allow writes or even CAN ACKs. + * buses - An array of all CAN buses. + * busCount - The length of the buses array. + */ +void post_initialize(CanBus* bus, bool writable, CanBus* buses, const int busCount); + +/* Public: Check if the device is connected to an active CAN bus, i.e. it's + * received a message in the recent past. + * + * Returns true if a message was received on the CAN bus within + * CAN_ACTIVE_TIMEOUT_S seconds. + */ +bool isBusActive(CanBus* bus); + +/* Public: Log transfer statistics about all active CAN buses to the debug log. + * + * buses - an array of active CAN buses. + * busCount - the length of the buses array. + */ +void logBusStatistics(CanBus* buses, const int busCount); diff --git a/src/can_decode_message.cpp b/src/can_decode_message.cpp new file mode 100644 index 00000000..ba822026 --- /dev/null +++ b/src/can_decode_message.cpp @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2015, 2016 "IoT.bzh" + * Author "Romain Forlot" <romain.forlot@iot.bzh> + * Author "Loic Collignon" <loic.collignon@iot.bzh> + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <linux/can.h> +#include <linux/can/raw.h> +#include <queue> + +#include <afb/afb-binding.h> + +#include "can-utils.h" +#include "openxc.pb.h" + +void can_decode_message(CanBus_c *can_bus) +{ + CanMessage_c can_message; + + while(true) + { + if(! can_bus->can_message_q.empty()) + { + can_message = can_bus->can_message_q.front(); + can_bus->can_message_q.pop(); + } + } +}
\ No newline at end of file diff --git a/src/can_event_push.cpp b/src/can_event_push.cpp new file mode 100644 index 00000000..b08619b8 --- /dev/null +++ b/src/can_event_push.cpp @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2015, 2016 "IoT.bzh" + * Author "Romain Forlot" <romain.forlot@iot.bzh> + * Author "Loic Collignon" <loic.collignon@iot.bzh> + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <linux/can.h> +#include <linux/can/raw.h> +#include <queue> + +#include <afb/afb-binding.h> + +#include "can-utils.h" +#include "openxc.pb.h" + +void can_event_push(CanBus_c *can_bus) +{ + while(true) + { + if(! can_bus->vehicle_message_q.empty()) + { + vehicle_message = can_bus->vehicle_message_q.front(); + can_bus->vehicle_message_q.pop(); + } + } +} diff --git a/src/can_reader.cpp b/src/can_reader.cpp new file mode 100644 index 00000000..d9b3e671 --- /dev/null +++ b/src/can_reader.cpp @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2015, 2016 "IoT.bzh" + * Author "Romain Forlot" <romain.forlot@iot.bzh> + * Author "Loic Collignon" <loic.collignon@iot.bzh> + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <linux/can.h> +#include <linux/can/raw.h> + +#include <afb/afb-binding.h> + +#include "can-utils.h" + +void can_reader(CanBus_c *can_bus)) +{ + ssize_t nbytes; + int maxdlen; + CanMessage_c can_message; + canfd_frame canfd_frame; + + /* Test that socket is really opened */ + if ( can_bus->socket < 0) + { + ERROR(interface, "read_can: Socket unavailable"); + return -1; + } + + while(true) + { + nbytes = read(can_bus->socket, &canfd_frame, CANFD_MTU); + + switch(nbytes) + { + case CANFD_MTU: + DEBUG(interface, "read_can: Got an CAN FD frame with length %d", canfd_frame.len); + maxdlen = CANFD_MAX_DLEN; + break; + case CAN_MTU: + DEBUG(interface, "read_can: Got a legacy CAN frame with length %d", canfd_frame.len); + maxdlen = CAN_MAX_DLEN; + break; + default: + if (errno == ENETDOWN) + ERROR(interface, "read_can: %s interface down", device); + + ERROR(interface, "read_can: Error reading CAN bus"); + return -2; + } + + can_message.convert_from_canfd_frame(canfd_frame); + + can_message_q.push(can_message); + } +} diff --git a/src/config.xml.in b/src/config.xml.in new file mode 100644 index 00000000..33a8ff85 --- /dev/null +++ b/src/config.xml.in @@ -0,0 +1,11 @@ +<?xml version="1.0" encoding="UTF-8"?> +<widget xmlns="http://www.w3.org/ns/widgets" id="@PROJECT_NAME@" version="@PROJECT_VERSION@"> + <name>@PROJECT_NAME@</name> + <icon src="icon.png"/> + <content src="@PROJECT_NAME@" type="application/vnd.agl.service"/> + <description>This is a demo application used to control and dialog with HVAC system</description> + <author>Romain Forlot <romain.forlot@iot.bzh></author> + <license>APL 2.0</license> +</widget> + + diff --git a/src/export.map b/src/export.map new file mode 100644 index 00000000..52c1b4aa --- /dev/null +++ b/src/export.map @@ -0,0 +1 @@ +{ global: afbBindingV1*; local: *; }; diff --git a/src/icon.png b/src/icon.png Binary files differnew file mode 100644 index 00000000..9bd6a6e4 --- /dev/null +++ b/src/icon.png diff --git a/src/libs/bitfield-c b/src/libs/bitfield-c new file mode 160000 +Subproject a34745ec93ae0a1d4f1b640dba8fb6702331a8e diff --git a/src/libs/nanopb b/src/libs/nanopb new file mode 160000 +Subproject ffe4aff87cc3a15863c09aa808adf2381c8f2fb diff --git a/src/libs/openxc-message-format b/src/libs/openxc-message-format new file mode 160000 +Subproject d9f54f97578429773421abce98d5f6579717afc diff --git a/src/libs/uds-c b/src/libs/uds-c new file mode 160000 +Subproject e506334e270d77b20c0bc259ac6c7d8c9b702b7 diff --git a/src/low-can-binding.cpp b/src/low-can-binding.cpp new file mode 100644 index 00000000..b6098907 --- /dev/null +++ b/src/low-can-binding.cpp @@ -0,0 +1,253 @@ +/* + * Copyright (C) 2015, 2016 "IoT.bzh" + * Author "Romain Forlot" <romain.forlot@iot.bzh> + * Author "Loic Collignon" <loic.collignon@iot.bzh> + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <unistd.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/ioctl.h> +#include <net/if.h> +#include <linux/can.h> +#include <linux/can/raw.h> +#include <fcntl.h> +#include <systemd/sd-event.h> +#include <errno.h> +#include <vector> +#include <map> +#include <queue> +#include <string> +#include <functional> +#include <memory> +#include <thread> + +#include <json-c/json.h> +#include <openxc.pb.h> + +#include <afb/afb-binding.h> +#include <afb/afb-service-itf.h> + +#include "ll-can-binding.h" +#include "obd2.h" + +/* + * Interface between the daemon and the binding + */ +static const struct afb_binding_interface *interface; + +/******************************************************************************** +* +* Event management +* +*********************************************************************************/ + +/* + * TBF TBF TBF + * called on an event on the CAN bus + */ +static int on_event(sd_event_source *s, int fd, uint32_t revents, void *userdata) +{ + openxc_CanMessage can_message; + + can_message = openxc_CanMessage_init_default; + + /* read available data */ + if ((revents & EPOLLIN) != 0) + { + read_can(&can_message); + send_event(); + } + + /* check if error or hangup */ + if ((revents & (EPOLLERR|EPOLLRDHUP|EPOLLHUP)) != 0) + { + sd_event_source_unref(s); + close(fd); + connect_to_event_loop(); + } + + return 0; +} + +/* + * USELESS SINCE THREADS SEPARATION + * + * Get the event loop running. + * Will trigger on_event function on EPOLLIN event on socket + * + * Return 0 or positive value on success. Else negative value for failure. +static int connect_to_event_loop(CanBus &CanBus_handler) +{ + sd_event *event_loop; + sd_event_source *source; + int rc; + + if (CanBus_handler.socket < 0) + { + return CanBus_handler.socket; + } + + event_loop = afb_daemon_get_event_loop(interface->daemon); + rc = sd_event_add_io(event_loop, &source, CanBus_handler.socket, EPOLLIN, on_event, NULL); + if (rc < 0) + { + CanBus_handler.close(); + ERROR(interface, "Can't connect CAN bus %s to the event loop", CanBus_handler.device); + } else + { + NOTICE(interface, "Connected CAN bus %s to the event loop", CanBus_handler.device); + } + + return rc; +} + */ + +/******************************************************************************** +* +* Subscription and unsubscription +* +*********************************************************************************/ + +static int subscribe_unsubscribe_sig(struct afb_req request, int subscribe, struct signal *sig) +{ + if (!afb_event_is_valid(sig->event)) { + if (!subscribe) + return 1; + sig->event = afb_daemon_make_event(afbitf->daemon, sig->name); + if (!afb_event_is_valid(sig->event)) { + return 0; + } + } + + if (((subscribe ? afb_req_subscribe : afb_req_unsubscribe)(request, sig->event)) < 0) { + return 0; + } + + return 1; +} + +static int subscribe_unsubscribe_all(struct afb_req request, int subscribe) +{ + int i, n, e; + + n = sizeof OBD2_PIDS / sizeof * OBD2_PIDS; + e = 0; + for (i = 0 ; i < n ; i++) + e += !subscribe_unsubscribe_sig(request, subscribe, &OBD2_PIDS[i]); + return e == 0; +} + +static int subscribe_unsubscribe_name(struct afb_req request, int subscribe, const char *name) +{ + struct signal *sig; + + if (0 == strcmp(name, "*")) + return subscribe_unsubscribe_all(request, subscribe); + + sig = getSignal(name); + if (sig == NULL) { + return 0; + } + + return subscribe_unsubscribe_sig(request, subscribe, sig); +} + +static void subscribe_unsubscribe(struct afb_req request, int subscribe) +{ + int ok, i, n; + struct json_object *args, *a, *x; + + /* makes the subscription/unsubscription */ + args = afb_req_json(request); + if (args == NULL || !json_object_object_get_ex(args, "event", &a)) { + ok = subscribe_unsubscribe_all(request, subscribe); + } else if (json_object_get_type(a) != json_type_array) { + ok = subscribe_unsubscribe_name(request, subscribe, json_object_get_string(a)); + } else { + n = json_object_array_length(a); + ok = 0; + for (i = 0 ; i < n ; i++) { + x = json_object_array_get_idx(a, i); + if (subscribe_unsubscribe_name(request, subscribe, json_object_get_string(x))) + ok++; + } + ok = (ok == n); + } + + /* send the report */ + if (ok) + afb_req_success(request, NULL, NULL); + else + afb_req_fail(request, "error", NULL); +} + +static void subscribe(struct afb_req request) +{ + subscribe_unsubscribe(request, 1); +} + +static void unsubscribe(struct afb_req request) +{ + subscribe_unsubscribe(request, 0); +} +static const struct afb_verb_desc_v1 verbs[]= +{ + { .name= "subscribe", .session= AFB_SESSION_NONE, .callback= subscribe, .info= "subscribe to notification of CAN bus messages." }, + { .name= "unsubscribe", .session= AFB_SESSION_NONE, .callback= unsubscribe, .info= "unsubscribe a previous subscription." }, + {NULL} +}; + +static const struct afb_binding binding_desc = { + .type = AFB_BINDING_VERSION_1, + .v1 = { + .info = "CAN bus service", + .prefix = "can", + .verbs = verbs + } +}; + +const struct afb_binding *afbBindingV1Register (const struct afb_binding_interface *itf) +{ + interface = itf; + + return &binding_desc; +} + +int afbBindingV1ServiceInit(struct afb_service service) +{ + std::ifstream fd_conf; + std::string fd_conf_content; + json_object jo_canbus; + + /* Open JSON conf file */ + jo_canbus = json_object_new_object(); + fd_conf = afb_daemon_rootdir_open_locale(interface->daemon, "canbus.json", O_RDONLY, NULL); + if (fd_conf) + { + fd_conf.seekg(0, std::ios::end); + fd_conf_content.resize(fd_conf.tellg()); + fd_conf.seekg(0, std::ios::beg); + fd_conf.read(&fd_conf_content[0], fd_conf_content.size()); + fd_conf.close(); + } + + jo_canbus = json_tokener_parse(&fd_conf_content); + + /* Open CAN socket */ + CanBus_c CanBus_handler(interface, json_object_get_string(json_object_object_get(jo_canbus, "deviceName")); + CanBus_handler.open(); + CanBus_handler.start_threads(); +} diff --git a/src/obd2.cpp b/src/obd2.cpp new file mode 100644 index 00000000..7db5d97d --- /dev/null +++ b/src/obd2.cpp @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2015, 2016 "IoT.bzh" + * Author "Romain Forlot" <romain.forlot@iot.bzh> + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +void shims_logger(afb_binding_interface *itf) +{ + //DEBUG(itf, "dd"); +} + +void shims_timer() +{ +} + +/* + * Will scan for supported Obd2 pids + */ +Obd2Handler::Obd2Handler(afb_binding_interface *itf, CanBus_c cb) +{ + CanBus_c can_bus = cb; + DiagnosticShims shims = diagnostic_init_shims(shims_logger, can_bus.send_can_message, NULL); + + int n_pids, i; + + n_pids = size(Obd2Pid); + for(i=0; i<=n_pids; i++) + { + } +} + +Obd2Handler::add_request(int pid) +{ + DiagnosticRequest request = { + arbitration_id: OBD2_FUNCTIONAL_BROADCAST_ID, + mode: 0x1, has_pid: true, pid: pid}; +} + +Obd2Handler::is_obd2_request(DiagnosticRequest* request) +{ + return request->mode == 0x1 && request->has_pid && request->pid < 0xff; +} + +Obd2Handler::decode_obd2_response(DiagnosticResponse* responce) +{ + return diagnostic_decode_obd2_pid(response); +} diff --git a/src/obd2.h b/src/obd2.h new file mode 100644 index 00000000..862c04a2 --- /dev/null +++ b/src/obd2.h @@ -0,0 +1,150 @@ +/* + * Copyright (C) 2015, 2016 "IoT.bzh" + * Author "Romain Forlot" <romain.forlot@iot.bzh> + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * A representation of an OBD-II PID. + * + * pid - The 1 byte PID. + * name - A human readable name to use for this PID when published. + * frequency - The frequency to request this PID if supported by the vehicle + * when automatic, recurring OBD-II requests are enabled. + */ + +enum UNIT { + POURCENT, + DEGREES_CELSIUS, + KPA, + RPM, + GRAMS_SEC, + SECONDS, + KM, + KM_H, + PA, + NM +}; + +const char *UNIT_NAMES[10] = { + "POURCENT", + "DEGREES_CELSIUS", + "KPA", + "RPM", + "GRAMS_SEC", + "SECONDS", + "KM", + "KM_H", + "PA", + "NM" +}; + + +/* + * A representation of an OBD-II PID. + * + * pid - The 1 byte PID. + * name - A human readable name to use for this PID when published. + * min - minimum value for this pid + * max - maximum value for this pid + * unit - unit used + * frequency - The frequency to request this PID if supported by the vehicle + * when automatic, recurring OBD-II requests are enabled. + * supported - is it supported by the vehicle. Initialized after scan + * event - application framework event handler. + * + */ +typedef struct _Obd2Pid { + uint8_t pid; + const char* name; + const int min; + const int max; + enum UNIT unit; + int frequency; + bool supported; + struct afb_event event; +} Obd2Pid; + +/* + { pid: 0x4, name: "engine.load", frequency: 5 }, + { pid: 0x5, name: "engine.coolant.temperature", frequency: 1 }, + { pid: 0xa, name: "fuel.pressure", frequency: 1 }, + { pid: 0xb, name: "intake.manifold.pressure", frequency: 1 }, + { pid: 0xc, name: "engine.speed", frequency: 5 }, + { pid: 0xd, name: "vehicle.speed", frequency: 5 }, + { pid: 0xf, name: "intake.air.temperature", frequency: 1 }, + { pid: 0x10, name: "mass.airflow", frequency: 5 }, + { pid: 0x11, name: "throttle.position", frequency: 5 }, + { pid: 0x1f, name: "running.time", frequency: 1 }, + { pid: 0x27, name: "fuel.level", frequency: 1 }, + { pid: 0x33, name: "barometric.pressure", frequency: 1 }, + { pid: 0x4c, name: "commanded.throttle.position", frequency: 1 }, + { pid: 0x52, name: "ethanol.fuel.percentage", frequency: 1 }, + { pid: 0x5a, name: "accelerator.pedal.position", frequency: 5 }, + { pid: 0x5c, name: "engine.oil.temperature", frequency: 1 }, + { pid: 0x63, name: "engine.torque", frequency: 1 }, +}; + */ + +/* Public: Check if a request is an OBD-II PID request. + * + * Returns true if the request is a mode 1 request and it has a 1 byte PID. + */ +bool isObd2Request(DiagnosticRequest* request); + +/* Public: Decode the payload of an OBD-II PID. + * + * This function matches the type signature for a DiagnosticResponseDecoder, so + * it can be used as the decoder for a DiagnosticRequest. It returns the decoded + * value of the PID, using the standard formulas (see + * http://en.wikipedia.org/wiki/OBD-II_PIDs#Mode_01). + */ +float handleObd2Pid(const DiagnosticResponse* response, float parsedPayload); + +/* + * Object to handle obd2 session with pre-scan of supported pid + * then request them regularly + */ +class Obd2Handler_c { + private: + /* + * Pre-defined OBD-II PIDs to query for if supported by the vehicle. + */ + const Obd2Pid OBD2_PIDS[] = { + { pid: 0x04, name: "obd2.engine.load", min:0, max: 100, unit: POURCENT, frequency: 5, supported: false }, + { pid: 0x05, name: "obd2.engine.coolant.temperature", min: -40, max: 215, unit: DEGREES_CELSIUS, frequency: 1, supported: false }, + { pid: 0x0a, name: "obd2.fuel.pressure", min: 0, max: 765, unit: KPA, frequency: 1, supported: false }, + { pid: 0x0b, name: "obd2.intake.manifold.pressure", min: 0, max: 255, unit: KPA, frequency: 1, supported: false }, + { pid: 0x0c, name: "obd2.engine.speed", min: 0, max: 16383, unit: RPM, frequency: 5, supported: false }, + { pid: 0x0d, name: "obd2.vehicle.speed", min: 0, max: 255, unit: KM_H, frequency: 5, supported: false }, + { pid: 0x0f, name: "obd2.intake.air.temperature", min: -40, max:215, unit: DEGREES_CELSIUS, frequency: 1, supported: false }, + { pid: 0x10, name: "obd2.mass.airflow", min: 0, max: 655, unit: GRAMS_SEC, frequency: 5, supported: false }, + { pid: 0x11, name: "obd2.throttle.position", min: 0, max: 100, unit: POURCENT, frequency: 5, supported: false }, + { pid: 0x1f, name: "obd2.running.time", min: 0, max: 65535, unit: SECONDS, frequency: 1, supported: false }, + { pid: 0x2d, name: "obd2.EGR.error", min: -100, max: 99, unit: POURCENT, frequency: 0, supported: false }, + { pid: 0x2f, name: "obd2.fuel.level", min: 0, max: 100, unit: POURCENT, frequency: 1, supported: false }, + { pid: 0x33, name: "obd2.barometric.pressure", min: 0, max: 255, unit: KPA, frequency: 1, supported: false }, + { pid: 0x4c, name: "obd2.commanded.throttle.position", min: 0, max: 100, unit: POURCENT, frequency: 1, supported: false }, + { pid: 0x52, name: "obd2.ethanol.fuel.percentage", min: 0, max: 100, unit: POURCENT, frequency: 1, supported: false }, + { pid: 0x5a, name: "obd2.accelerator.pedal.position", min: 0, max: 100, unit: POURCENT, frequency: 5, supported: false }, + { pid: 0x5b, name: "obd2.hybrid.battery-pack.remaining.life", min: 0, max: 100, unit: POURCENT, frequency: 5, supported: false }, + { pid: 0x5c, name: "obd2.engine.oil.temperature",min: -40, max: 210, unit: DEGREES_CELSIUS, frequency: 1, supported: false }, + { pid: 0x63, name: "obd2.engine.torque", min: 0, max: 65535, unit: NM, frequency: 1, supported: false }, + }; + + public: + Obd2Handler_c(); + + bool isObd2Request(request); +} diff --git a/src/timer.h b/src/timer.h new file mode 100644 index 00000000..76eb51df --- /dev/null +++ b/src/timer.h @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2015, 2016 "IoT.bzh" + * Author "Romain Forlot" <romain.forlot@iot.bzh> + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +//typedef unsigned long (*TimeFunction)(); + +/* Public: A frequency counting clock. + * + * frequency - the clock frequency in Hz. + * last_time - the last time (in milliseconds since startup) that the clock + * ticked. + * time_function - a function returning current time in ms + */ +typedef struct { + float frequency; + unsigned long lastTick; + TimeFunction timeFunction; +} FrequencyClock; |