aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/CMakeLists.txt92
-rw-r--r--src/can-decoder.cpp93
-rw-r--r--src/can-decoder.h146
-rw-r--r--src/can-signals.cpp40
-rw-r--r--src/can-signals.h84
-rw-r--r--src/can-utils.cpp226
-rw-r--r--src/can-utils.h396
-rw-r--r--src/can_decode_message.cpp40
-rw-r--r--src/can_event_push.cpp38
-rw-r--r--src/can_reader.cpp66
-rw-r--r--src/config.xml.in11
-rw-r--r--src/export.map1
-rw-r--r--src/icon.pngbin0 -> 4035 bytes
m---------src/libs/bitfield-c6
m---------src/libs/nanopb9
m---------src/libs/openxc-message-format9
m---------src/libs/uds-c10
-rw-r--r--src/low-can-binding.cpp253
-rw-r--r--src/obd2.cpp59
-rw-r--r--src/obd2.h150
-rw-r--r--src/timer.h33
21 files changed, 1762 insertions, 0 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
new file mode 100644
index 0000000..768d6b4
--- /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 0000000..000db2a
--- /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 0000000..249d5b0
--- /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 0000000..13ff30b
--- /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 0000000..f34c743
--- /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 0000000..6721410
--- /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 0000000..8c850e6
--- /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 0000000..ba82202
--- /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 0000000..b08619b
--- /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 0000000..d9b3e67
--- /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 0000000..33a8ff8
--- /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 &lt;romain.forlot@iot.bzh&gt;</author>
+ <license>APL 2.0</license>
+</widget>
+
+
diff --git a/src/export.map b/src/export.map
new file mode 100644
index 0000000..52c1b4a
--- /dev/null
+++ b/src/export.map
@@ -0,0 +1 @@
+{ global: afbBindingV1*; local: *; };
diff --git a/src/icon.png b/src/icon.png
new file mode 100644
index 0000000..9bd6a6e
--- /dev/null
+++ b/src/icon.png
Binary files differ
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 0000000..b609890
--- /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 0000000..7db5d97
--- /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 0000000..862c04a
--- /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 0000000..76eb51d
--- /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;