diff options
-rw-r--r-- | CMakeLists.txt | 95 | ||||
-rw-r--r-- | README | 11 | ||||
-rw-r--r-- | config.xml.in | 11 | ||||
-rw-r--r-- | export.map | 1 | ||||
-rw-r--r-- | hvac-hybrid-qml-binding.c | 351 | ||||
-rw-r--r-- | hvacplugin.cpp | 239 | ||||
-rw-r--r-- | hvacplugin.h | 86 | ||||
-rw-r--r-- | icon_hvac_hybrid_qml.png | bin | 0 -> 4236 bytes | |||
-rw-r--r-- | qml/ClimateButton.qml | 30 | ||||
-rw-r--r-- | qml/FanControl.qml | 54 | ||||
-rw-r--r-- | qml/HVAC.qml | 89 | ||||
-rw-r--r-- | qml/HazardButton.qml | 37 | ||||
-rw-r--r-- | qml/MiddleColumn.qml | 33 | ||||
-rw-r--r-- | qml/SeatHeatButton.qml | 39 | ||||
-rw-r--r-- | qml/TempSlider.qml | 61 | ||||
-rw-r--r-- | qml/TemperatureWheel.qml | 56 | ||||
-rw-r--r-- | qml/hvac-hybrid-qml-app.qml | 87 | ||||
-rw-r--r-- | qml/models/HVACModel.qml | 47 | ||||
-rw-r--r-- | qml/models/TemperatureModel.qml | 28 | ||||
-rw-r--r-- | qml/models/qmldir | 8 |
20 files changed, 1012 insertions, 351 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 9e772ce..21ed9b6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,23 +1,88 @@ -PROJECT(hvacplugin CXX) -CMAKE_MINIMUM_REQUIRED(VERSION 2.8.8) -SET(CMAKE_CXX_FLAGS "-std=c++11 ${CMAKE_CXX_FLAGS}") +########################################################################### +# 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(hvac-hybrid-qml) + +cmake_minimum_required(VERSION 3.3) + +include(GNUInstallDirs) + +set(PROJECT_VERSION "0.1") +set(PROJECT_ICON "icon_hvac_hybrid_qml.png") +set(PROJECT_LIBDIR "lib") + +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") + +########################################################################### include(FindPkgConfig) -pkg_check_modules(gio gio-2.0) -pkg_check_modules(gio-unix gio-unix-2.0) -pkg_check_modules(json-c json-c) -pkg_check_modules(amb automotive-message-broker) -find_package(Boost REQUIRED) -include_directories(${CMAKE_SOURCE_DIR}/lib ${include_dirs} ${gio_INCLUDE_DIRS} ${gio-unix_INCLUDE_DIRS} ${CMAKE_SOURCE_DIR}/plugins/common ${Boost_INCLUDE_DIRS} ${json-c_INCLUDE_DIRS} ${amb_INCLUDE_DIRS}) +pkg_check_modules(EXTRAS REQUIRED json-c afb-daemon) +add_compile_options(${EXTRAS_CFLAGS}) +include_directories(${EXTRAS_INCLUDE_DIRS}) +link_libraries(${EXTRAS_LIBRARIES}) + +########################################################################### +# the binding for afb + +message(STATUS "Creation of ${PROJECT_NAME} for AFB-DAEMON") -set(hvacplugin_headers hvacplugin.h) -set(hvacplugin_sources hvacplugin.cpp) +############################################################### +add_library(${PROJECT_NAME} MODULE ${PROJECT_NAME}-binding.c + ./qml) +# ./qml/${PROJECT_NAME}-app.qml) -add_library(hvacplugin MODULE ${hvacplugin_sources} ${hvacplugin_headers}) -set_target_properties(hvacplugin PROPERTIES PREFIX "") -target_link_libraries(hvacplugin amb amb-plugins-common -L${CMAKE_CURRENT_BINARY_DIR}/plugins/common ${link_libraries} -lrt ${json-c_LIBRARIES}) +set_target_properties(${PROJECT_NAME} PROPERTIES + PREFIX "" + LINK_FLAGS "-Wl,--version-script=${CMAKE_CURRENT_SOURCE_DIR}/export.map" +) -install(TARGETS hvacplugin LIBRARY DESTINATION ${PLUGIN_INSTALL_PATH}) +configure_file(config.xml.in config.xml) +add_custom_command( + OUTPUT ${PROJECT_NAME}.wgt + DEPENDS ${PROJECT_NAME} + 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 -r ${CMAKE_CURRENT_SOURCE_DIR}/qml/ package/ + COMMAND cp ${PROJECT_NAME}.so package/${PROJECT_LIBDIR} + COMMAND wgtpkg-pack -f -o ${PROJECT_NAME}.wgt package + # COMMAND rm -rf package +) +add_custom_target(widget ALL DEPENDS ${PROJECT_NAME}.wgt) @@ -1,11 +0,0 @@ -This folder contains the files to build the AMB source plugin from K2L to control the demo HVAC hardware provided by Microchip. It is a part of the AGL demo for CES 2016. See COPYING file for the license. - -Copyright (C) 2013-2015 K2L GmbH & Co. KG - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -This file is licensed under GPLv2. - diff --git a/config.xml.in b/config.xml.in new file mode 100644 index 0000000..6ca09f1 --- /dev/null +++ b/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>App Framework - @PROJECT_NAME@</name> + <icon src="icon.png"/> + <content src="qml/@PROJECT_NAME@-app.qml" type="application/vnd.agl.qml.hybrid"/> + <description>This application is 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/export.map b/export.map new file mode 100644 index 0000000..52c1b4a --- /dev/null +++ b/export.map @@ -0,0 +1 @@ +{ global: afbBindingV1*; local: *; }; diff --git a/hvac-hybrid-qml-binding.c b/hvac-hybrid-qml-binding.c new file mode 100644 index 0000000..634f12e --- /dev/null +++ b/hvac-hybrid-qml-binding.c @@ -0,0 +1,351 @@ +/* + * Copyright (C) 2015, 2016 "IoT.bzh" + * Author "Manuel Bachmann" + * + * 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. + */ +#define _GNU_SOURCE +#include <json-c/json.h> + +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/ioctl.h> +#include <net/if.h> +#include <linux/can.h> +#include <string.h> +#include <unistd.h> + +#include <afb/afb-binding.h> + +#define CAN_DEV "can0" + +const struct afb_binding_interface *interface; + +// Initialize CAN payload array that will be sent trough the socket +__u8 can_payload[8] = { + 21, + 21, + 21, + 240, // Don't know why 240 but it was 0xF0 in the original amb hvacplugin + 0, + 1, // Don't know why 1 but it was 0xF0 in the original amb hvacplugin + 0, // Don't know why 0 but it was 0xF0 in the original amb hvacplugin + 0 // Don't know why 0 but it was 0xF0 in the original amb hvacplugin +}; + +struct can_handler{ + int socket; + struct sockaddr_can txAddress; +}; + +struct can_handler *ch; + +/***************************************************************************************/ +/***************************************************************************************/ +/** **/ +/** **/ +/** SECTION: HANDLE CAN DEVICE **/ +/** **/ +/** **/ +/***************************************************************************************/ +/***************************************************************************************/ + +static struct can_handler open_can_dev() +{ + struct can_handler ch; + ch.socket = -1; + + ch.socket = socket(PF_CAN, SOCK_RAW, CAN_RAW); + printf("Socket: %i\n", ch.socket); + if (ch.socket < 0) + { + printf("ERROR: socket could not be created\n"); + } + else + { + struct ifreq ifr; + + // Attemts to open a socket to CAN bus + strcpy(ifr.ifr_name, CAN_DEV); + if(ioctl(ch.socket, SIOCGIFINDEX, &ifr) < 0) + { + close(ch.socket); + ch.socket = -1; + printf("ERROR: ioctl failed\n"); + } + else + { + ch.txAddress.can_family = AF_CAN; + ch.txAddress.can_ifindex = ifr.ifr_ifindex; + + // And bind it to ch.txAddress struct + if (bind(ch.socket, (struct sockaddr *)&ch.txAddress, sizeof(ch.txAddress)) < 0) + { + close(ch.socket); + ch.socket = -1; + printf("ERROR: bind failed\n"); + } + return ch; + } + } + printf("Error: unexpected behavior\n"); + return ch; +} + +static void close_can_dev(struct can_handler ch) +{ + int socketId = ch.socket; + if(socketId >= 0) + { + close(socketId); + printf("Socket closed\n"); + } +} + +static void write_can(struct can_handler ch) +{ + printf("Write to CAN bus\n"); + // Hardcoded can_id and dlc (data lenght code) + struct can_frame txCanFrame; + txCanFrame.can_id = 0x30; + txCanFrame.can_dlc = 8; + + if (ch.socket >= 0) + { + printf("Send CAN message\n"); + + txCanFrame.data[0] = can_payload[0]; + txCanFrame.data[1] = can_payload[1]; + txCanFrame.data[2] = (can_payload[0] + can_payload[1]) & 0x02; + txCanFrame.data[3] = can_payload[3]; + txCanFrame.data[4] = can_payload[4]; + txCanFrame.data[5] = can_payload[5]; + txCanFrame.data[6] = can_payload[6]; + txCanFrame.data[7] = can_payload[7]; + + sendto(ch.socket, &txCanFrame, sizeof(struct can_frame), 0, + (struct sockaddr*)&ch.txAddress, sizeof(ch.txAddress)); + } + else + { + printf("Error: socket not initialized\n"); + } + printf("Error sending on CAN bus. Unexpected behavior"); +} + +static int read_temp_left_zone() +{ + return can_payload[0]; +} + +static int read_temp_right_zone() +{ + return can_payload[1]; +} + +static int read_fanspeed() +{ + return can_payload[4]; +} + +/***************************************************************************************/ +/***************************************************************************************/ +/** **/ +/** **/ +/** SECTION: BINDING VERBS IMPLEMENTATION **/ +/** **/ +/** **/ +/***************************************************************************************/ +/***************************************************************************************/ + +/* + * @brief Simple afb-daemon ping + * + * @param struct afb_req : an afb request structure + * + */ +static void ping (struct afb_req request) +{ + static int pingcount = 0; + + json_object *query = afb_req_json(request); + afb_req_success_f(request, NULL, "Ping Binder Daemon count=%d query=%s", ++pingcount, json_object_to_json_string(query)); +} + +/* + * @brief Get fan speed HVAC system + * + * @param struct afb_req : an afb request structure + * + */ +static void get_fanspeed(struct afb_req request) +{ + json_object *ret_json; + int fanspeed = read_fanspeed(); + + ret_json = json_object_new_object(); + json_object_object_add(ret_json, "FanSpeed", json_object_new_int(fanspeed)); + + json_object *query = afb_req_json(request); + afb_req_success_f(request, ret_json, "Fan Speed:%d query=%s", fanspeed, json_object_to_json_string(query)); +} + +/* + * @brief Set fan speed to HVAC system + * + * @param struct afb_req : an afb request structure + * + */ +static void set_fanspeed(struct afb_req request) +{ + struct can_handler ch; + int fanspeed = (int)afb_req_value(request, "FanSpeed"); + + // Set can_payload array with the new fan speed value + can_payload[4] = fanspeed; + + ch = open_can_dev(); + write_can(ch); + close_can_dev(ch); + + json_object *query = afb_req_json(request); + afb_req_success_f(request, NULL, "Fanspeed=%d query=%s", can_payload[4], json_object_to_json_string(query)); +} + +/* + * @brief Read Consign right zone temperature for HVAC system + * + * @param struct afb_req : an afb request structure + * + */ +static void get_temp_right_zone(struct afb_req request) +{ + json_object *ret_json; + int temp = read_temp_right_zone(); + + ret_json = json_object_new_object(); + json_object_object_add(ret_json, "RightTemperature", json_object_new_int(temp)); + + json_object *query = afb_req_json(request); + afb_req_success_f(request, ret_json, "Temperature right zone:%d query=%s", temp, json_object_to_json_string(query)); +} + +/* + * @brief Read Consign left zone temperature for HVAC system + * + * @param struct afb_req : an afb request structure + * + */ +static void get_temp_left_zone(struct afb_req request) +{ + json_object *ret_json; + int temp = read_temp_left_zone(); + + ret_json = json_object_new_object(); + json_object_object_add(ret_json, "LeftTemperature", json_object_new_int(temp)); + + json_object *query = afb_req_json(request); + afb_req_success_f(request, ret_json, "Temperature left zone:%d query=%s", temp, json_object_to_json_string(query)); +} + + +/* + * @brief Set right zone temperature to HVAC system + * + * @param struct afb_req : an afb request structure + * + */ +static void set_temp_right_zone(struct afb_req request) +{ + struct can_handler ch; + int tempright = (int)afb_req_value(request, "RightTemperature"); + + // Set can_payload array with the new fan speed value + can_payload[1] = tempright; + + ch = open_can_dev(); + write_can(ch); + close_can_dev(ch); + + json_object *query = afb_req_json(request); + afb_req_success_f(request, NULL, "Ping Binder Daemon fanspeed=%d query=%s", can_payload[4], json_object_to_json_string(query)); +} + +/* + * @brief Set left zone temperature to HVAC system + * + * @param struct afb_req : an afb request structure + * + */ +static void set_temp_left_zone(struct afb_req request) +{ + struct can_handler ch; + int templeft = (int)afb_req_value(request, "LeftTemperature"); + + // Set can_payload array with the new fan speed value + can_payload[0] = templeft; + + ch = open_can_dev(); + write_can(ch); + close_can_dev(ch); + + json_object *query = afb_req_json(request); + afb_req_success_f(request, NULL, "Ping Binder Daemon fanspeed=%d query=%s", can_payload[4], json_object_to_json_string(query)); +} + +/* + * @brief Read all values + * + * @param struct afb_req : an afb request structure + * + */ +static void get_all(struct afb_req request) +{ + json_object *ret_json; + + ret_json = json_object_new_object(); + json_object_object_add(ret_json, "LeftTemperature", json_object_new_int(read_temp_left_zone())); + json_object_object_add(ret_json, "RightTemperature", json_object_new_int(read_temp_right_zone())); + json_object_object_add(ret_json, "FanSpeed", json_object_new_int(read_fanspeed())); + + afb_req_success_f(request, ret_json, NULL); +} + +// TODO: Have to change session management flag to AFB_SESSION_CHECK to use token auth +static const struct afb_verb_desc_v1 verbs[]= { + {"ping" , AFB_SESSION_NONE, ping , "Ping the binder"}, + {"get_temp_left_zone" , AFB_SESSION_NONE, get_temp_left_zone , "Get the left zone temperature"}, + {"get_temp_right_zone" , AFB_SESSION_NONE, get_temp_right_zone , "Get the right zone temperature"}, + {"set_temp_left_zone" , AFB_SESSION_NONE, set_temp_left_zone , "Set the left zone temperature"}, + {"set_temp_right_zone" , AFB_SESSION_NONE, set_temp_right_zone , "Set the right zone temperature"}, + {"set_fanspeed" , AFB_SESSION_NONE, set_fanspeed , "Set fanspeed"}, + {"get_fanspeed" , AFB_SESSION_NONE, get_fanspeed , "Read fan speed"}, + {"get_all" , AFB_SESSION_NONE, get_all , "Read all values"}, + {NULL} +}; + +static const struct afb_binding plugin_desc = { + .type = AFB_BINDING_VERSION_1, + .v1 = { + .info = "hvac hybrid service", + .prefix = "hvac", + .verbs = verbs + } +}; + +const struct afb_binding *afbBindingV1Register (const struct afb_binding_interface *itf) +{ + interface = itf; + + return &plugin_desc; +} diff --git a/hvacplugin.cpp b/hvacplugin.cpp deleted file mode 100644 index d67d9b4..0000000 --- a/hvacplugin.cpp +++ /dev/null @@ -1,239 +0,0 @@ -/* - * hvacplugin.cpp - AMB plugin to conttrol HVAC demo hardware for CES 2016 - * - * Copyright (C) 2013-2015 K2L GmbH & Co. KG - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * This file is licensed under GPLv2. - */ - -#include <vehicleproperty.h> -#include <listplusplus.h> - -#include <sys/types.h> -#include <sys/socket.h> -#include <sys/ioctl.h> -#include <net/if.h> -#include <linux/can.h> - -#include <logger.h> - -#include "hvacplugin.h" - -static const char* DEFAULT_CAN_IF_NAME = "vcan0"; - -//---------------------------------------------------------------------------- -// K2LHvacPlugin -//---------------------------------------------------------------------------- - -extern "C" void create(AbstractRoutingEngine* routingengine, std::map<std::string, std::string> config) -{ - new K2LHvacPlugin(routingengine, config); -} - -K2LHvacPlugin::K2LHvacPlugin(AbstractRoutingEngine* re, const map<string, string>& config) - : AbstractSource(re, config) -{ - printf("K2LHvacPlugin: created - 2015-12-08 10:04\n"); - - addPropertySupport(VehicleProperty::TargetTemperature, Zone::FrontLeft | Zone::FrontRight); - - Zone::ZoneList tempartureZones; - tempartureZones.push_back(Zone::FrontLeft); - tempartureZones.push_back(Zone::FrontRight); - - temperatureValues[Zone::FrontLeft] = 21; - temperatureValues[Zone::FrontRight] = 21; - - PropertyInfo tempartureZonesInfo(0, tempartureZones); - propertyInfoMap[VehicleProperty::TargetTemperature] = tempartureZonesInfo; - - addPropertySupport(VehicleProperty::FanSpeed, Zone::None); - - socketId = socket(PF_CAN, SOCK_RAW, CAN_RAW); - printf("Socket: %i\n", socketId); - if (socketId < 0) - { - printf("ERROR: socket could not be created\n"); - } - else - { - struct ifreq ifr; - - strcpy(ifr.ifr_name, "vcan0" ); - if(ioctl(this->socketId, SIOCGIFINDEX, &ifr) < 0) - { - close(socketId); - socketId = -1; - printf("ERROR: ioctl failed\n"); - } - else - { - txAddress.can_family = AF_CAN; - txAddress.can_ifindex = ifr.ifr_ifindex; - - if (bind(this->socketId, (struct sockaddr *)&txAddress, sizeof(txAddress)) < 0) - { - close(socketId); - socketId = -1; - printf("ERROR: bind failed\n"); - } - - txCanFrame.can_id = 0x30; - txCanFrame.can_dlc = 8; - } - } -} - -K2LHvacPlugin::~K2LHvacPlugin() -{ - if(socketId >= 0) - { - close(socketId); - printf("Socket closed\n"); - } -} - -void K2LHvacPlugin::getPropertyAsync(AsyncPropertyReply *reply) -{ - printf("K2LHvacPlugin::getPropertyAsync\n"); - - if(reply->property == VehicleProperty::FanSpeed) - { - printf("VehicleProperty::FanSpeed=OK\n"); - VehicleProperty::FanSpeedType temp(fanSpeedValue); - reply->value = &temp; - reply->success = true; - reply->completed(reply); - } - else if(reply->property == VehicleProperty::TargetTemperature) - { - printf("VehicleProperty::TargetTemperature="); - if(temperatureValues.find(reply->zoneFilter) == temperatureValues.end()) - { - printf("AsyncPropertyReply::ZoneNotSupported\n"); - reply->success = false; - reply->error = AsyncPropertyReply::ZoneNotSupported; - reply->completed(reply); - } - else - { - printf("OK\n"); - VehicleProperty::TargetTemperatureType temp(temperatureValues[reply->zoneFilter]); - reply->success = true; - reply->value = &temp; - reply->completed(reply); - } - } -} - -AsyncPropertyReply *K2LHvacPlugin::setProperty(AsyncSetPropertyRequest request ) -{ - printf("K2LHvacPlugin::setProperty\n"); - AsyncPropertyReply *reply = new AsyncPropertyReply(request); - reply->success = false; - reply->error = AsyncPropertyReply::NoError; - - if(reply->property == VehicleProperty::FanSpeed) - { - printf("VehicleProperty::FanSpeed=OK\n"); - fanSpeedValue = (uint8_t)reply->value->value<uint16_t>(); - } - else if(reply->property == VehicleProperty::TargetTemperature) - { - printf("VehicleProperty::TargetTemperature="); - if(temperatureValues.find(reply->zoneFilter) == temperatureValues.end()) - { - printf("AsyncPropertyReply::ZoneNotSupported\n"); - reply->error = AsyncPropertyReply::ZoneNotSupported; - } - else - { - printf("OK\n"); - temperatureValues[reply->zoneFilter] = (uint8_t)reply->value->value<int>(); - } - } - else - { - reply->error = AsyncPropertyReply::InvalidOperation; - } - - if(reply->error == AsyncPropertyReply::NoError) - { - reply->success = true; - - routingEngine->updateProperty(reply->value,uuid()); - - if (socketId >= 0) - { - printf("Send CAN message\n"); - - txCanFrame.data[0] = GetTemperature(temperatureValues[Zone::FrontLeft]); - txCanFrame.data[1] = GetTemperature(temperatureValues[Zone::FrontRight]); - txCanFrame.data[2] = GetTemperature((temperatureValues[Zone::FrontLeft] - + temperatureValues[Zone::FrontRight]) / 2); - txCanFrame.data[3] = 0xF0; - txCanFrame.data[4] = fanSpeedValue; - txCanFrame.data[5] = 0x01; - txCanFrame.data[6] = 0x00; - txCanFrame.data[7] = 0x00; - - sendto(socketId, &txCanFrame, sizeof(struct can_frame), 0, - (struct sockaddr*)&txAddress, sizeof(txAddress)); - } - } - else - { - reply->success = false; - } - - reply->completed(reply); - return reply; -} - -uint8_t K2LHvacPlugin::GetTemperature(uint8_t value) -{ - uint8_t result = ((0xF0 - 0x10) / 15) * value - 16; - if (result < 0x10) - result = 0x10; - if (result > 0xF0) - result = 0xF0; - - return result; -} - -void K2LHvacPlugin::subscribeToPropertyChanges(VehicleProperty::Property property) -{ - mRequests.push_back(property); -} - -PropertyList K2LHvacPlugin::supported() -{ - return mSupported; -} - -void K2LHvacPlugin::unsubscribeToPropertyChanges(VehicleProperty::Property property) -{ - if(contains(mRequests,property)) - removeOne(&mRequests, property); -} - -int K2LHvacPlugin::supportedOperations() -{ - return Get | Set | GetRanged; -} - -void K2LHvacPlugin::addPropertySupport(VehicleProperty::Property property, Zone::Type zone) -{ - mSupported.push_back(property); - - Zone::ZoneList zones; - zones.push_back(zone); - - PropertyInfo info(0, zones); - propertyInfoMap[property] = info; -} diff --git a/hvacplugin.h b/hvacplugin.h deleted file mode 100644 index 1229477..0000000 --- a/hvacplugin.h +++ /dev/null @@ -1,86 +0,0 @@ -/* - * hvacplugin.h - AMB plugin to conttrol HVAC demo hardware for CES 2016 - * - * Copyright (C) 2013-2015 K2L GmbH & Co. KG - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * This file is licensed under GPLv2. - */ - -#ifndef _HvacPlugin_H_ -#define _HvacPlugin_H_ - -/*! - * \defgroup hvacplugin hvacplugin shared library - * \brief HVAC plug-in from K2L. - * - * To load this plugin at AMB starup, insert following rows into AMB configuration file: - * \code - * { - * "name" : "HvacPlugin", - * "path":"/usr/lib/automotive-message-broker/hvacplugin.so" - * } - * \endcode - * - * \note HvacPlugin has to be the last source plug-in listed in AMB configuration file. Otherwise it can accidentally unregister or try to simulate some AMB properties supported from other sources. - * - * @{ - */ - -#include <abstractsource.h> -#include <string> - -using namespace std; - -class K2LHvacPlugin: public AbstractSource -{ -public: - K2LHvacPlugin(): AbstractSource(nullptr, map<string, string>()) {} - K2LHvacPlugin(AbstractRoutingEngine* re, const map<string, string>& config); - virtual ~K2LHvacPlugin(); - -public: - const string uuid() { return "4B029567-A667-4F6B-8F23-44D6E49DBF1E"; } - - void getPropertyAsync(AsyncPropertyReply *reply); - void getRangePropertyAsync(AsyncRangePropertyReply *reply) {} - AsyncPropertyReply * setProperty(AsyncSetPropertyRequest request); - void subscribeToPropertyChanges(VehicleProperty::Property property); - void unsubscribeToPropertyChanges(VehicleProperty::Property property); - PropertyList supported(); - int supportedOperations(); - void supportedChanged(const PropertyList &) {} - - PropertyInfo getPropertyInfo(const VehicleProperty::Property & property) - { - if(propertyInfoMap.find(property) != propertyInfoMap.end()) - return propertyInfoMap[property]; - - return PropertyInfo::invalid(); - } -private: - void addPropertySupport(VehicleProperty::Property property, Zone::Type zone); - uint8_t GetTemperature(uint8_t value); - void printFrame(const can_frame& frame) const; - - std::map<VehicleProperty::Property, PropertyInfo> propertyInfoMap; - std::map<Zone::Type, uint8_t> temperatureValues; - uint8_t fanSpeedValue; - - PropertyList mRequests; - PropertyList mSupported; - - VehicleProperty::TargetTemperatureType targetTemperature; - VehicleProperty::FanSpeedType fanSpeed; - - int socketId = -1; - struct can_frame txCanFrame; - struct sockaddr_can txAddress; -}; -#endif // _CANSIMPLUGINIMPL_H_ - -/** @} */ diff --git a/icon_hvac_hybrid_qml.png b/icon_hvac_hybrid_qml.png Binary files differnew file mode 100644 index 0000000..27df1a0 --- /dev/null +++ b/icon_hvac_hybrid_qml.png diff --git a/qml/ClimateButton.qml b/qml/ClimateButton.qml new file mode 100644 index 0000000..24802c7 --- /dev/null +++ b/qml/ClimateButton.qml @@ -0,0 +1,30 @@ +/* Copyright (C) 2015, Jaguar Land Rover. All Rights Reserved. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +import QtQuick 2.0 +import "models" + +Rectangle { + id: root + + width: imageItem.width + height: imageItem.height + color: "#aa000000" + + property string target: "" + property string image: "" + property bool value: HVACModel[target] + + Image { + id: imageItem + source: "images/" + image + "_" + (value ? "on" : "off") + ".png" + } + + MouseArea { + anchors.fill: parent + onClicked: HVACModel[target] = !HVACModel[target] + } +} diff --git a/qml/FanControl.qml b/qml/FanControl.qml new file mode 100644 index 0000000..00e6f9e --- /dev/null +++ b/qml/FanControl.qml @@ -0,0 +1,54 @@ +/* Copyright (C) 2015, Jaguar Land Rover. All Rights Reserved. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +import QtQuick 2.0 +import "models" + +Item { + width: childrenRect.width + height: childrenRect.height + + property real value: 0 + + Image { + y: 15 + source: "images/fan_icon_off.png" + } + + Image { + id: fanBar + x: 100 + source: "images/fan_bar_off.png" + } + + Image { + x: 100 + width: value * fanBar.width + fillMode: Image.PreserveAspectCrop + horizontalAlignment: Image.AlignLeft + source: "images/fan_bar_on.png" + + Image { + width: 20 + height: width + anchors.verticalCenter: parent.bottom + anchors.verticalCenterOffset: -1 + anchors.horizontalCenter: parent.right + source: "images/drag_knob.svg" + } + } + + MouseArea { + x: 100 + width: fanBar.width + height: parent.height + + onPositionChanged: { + value = Math.min(Math.max(mouse.x / fanBar.width, 0), 1) + HVACModel.fanSpeed = value; + } + } +} diff --git a/qml/HVAC.qml b/qml/HVAC.qml new file mode 100644 index 0000000..8ca981e --- /dev/null +++ b/qml/HVAC.qml @@ -0,0 +1,89 @@ +/* Copyright (C) 2015, Jaguar Land Rover. All Rights Reserved. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +import QtQuick 2.0 +import system 1.0 +import utils 1.0 +import "models" + +App { + appId: "hvac" + + HazardButton { + id: hazardButton + y: 100 + anchors.horizontalCenter: parent.horizontalCenter + } + + TempSlider { + id: lTempSlider + x: 30 + anchors.top: hazardButton.bottom + anchors.topMargin: 115 + side: "left" + } + + Row { + anchors.horizontalCenter: parent.horizontalCenter + anchors.top: hazardButton.bottom + anchors.topMargin: 90 + spacing: 200 + + MiddleColumn { side: "left" } + MiddleColumn { side: "right" } + } + + TempSlider { + id: rTempSlider + anchors.top: hazardButton.bottom + anchors.topMargin: 115 + anchors.right: parent.right + anchors.rightMargin: 30 + side: "right" + } + + Image { + y: 1057 + source: "images/separator.png" + } + + FanControl { + x: 259 + y: 1092 + } + + Item { + anchors.horizontalCenter: parent.horizontalCenter + width: childrenRect.width + height: childrenRect.height + anchors.bottom: parent.bottom + anchors.bottomMargin: 40 + + Row { + spacing: 20 + + Column { + spacing: 10 + + ClimateButton { image: "fan_dir_down"; target: "fanDown" } + ClimateButton { image: "fan_dir_right"; target: "fanRight" } + ClimateButton { image: "fan_dir_up"; target: "fanUp" } + } + + ClimateButton { y: 156; image: "fan_control_ac"; target: "fanAC" } + ClimateButton { y: 156; image: "fan_control_auto"; target: "fanAuto" } + ClimateButton { y: 156; image: "fan_control_circ"; target: "fanRecirc" } + + Column { + spacing: 10 + + ClimateButton { image: "defrost_max"; target: "defrostMax" } + ClimateButton { image: "defrost_rear"; target: "defrostRear" } + ClimateButton { image: "defrost_front"; target: "defrostFront" } + } + } + } +} diff --git a/qml/HazardButton.qml b/qml/HazardButton.qml new file mode 100644 index 0000000..ffae370 --- /dev/null +++ b/qml/HazardButton.qml @@ -0,0 +1,37 @@ +/* Copyright (C) 2015, Jaguar Land Rover. All Rights Reserved. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +import QtQuick 2.0 + +Rectangle { + id: hazardButton + width: 624 + height: 122 + color: "#aa000000" + border.color: "#ff53b5ce" + + property bool value: false + property bool flash: false + + Image { + id: image + source: "./images/hazard_" + (value ? (flash ? "blink" : "on") : "off") + ".png" + } + + MouseArea { + anchors.fill: parent + onClicked: value = !value + } + + Timer { + id: timer + interval: 500 + repeat: true + running: value + + onTriggered: flash = !flash + } +} diff --git a/qml/MiddleColumn.qml b/qml/MiddleColumn.qml new file mode 100644 index 0000000..89f0ebe --- /dev/null +++ b/qml/MiddleColumn.qml @@ -0,0 +1,33 @@ +/* Copyright (C) 2015, Jaguar Land Rover. All Rights Reserved. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +import QtQuick 2.0 +import components 1.0 +import utils 1.0 + +Item { + id: root + + width: 239 + height: 800 + + property string side: "left" + + Column { + spacing: 50 + + BoxHeading { + color: Style.orangeViv + boxWidth: 45 + boxHeight: 19 + fontSize: 27 + text: (side === "left" ? "L" : "R" ) + " CLIMATE" + } + + SeatHeatButton { side: root.side } + TemperatureWheel { side: root.side } + } +} diff --git a/qml/SeatHeatButton.qml b/qml/SeatHeatButton.qml new file mode 100644 index 0000000..43645fb --- /dev/null +++ b/qml/SeatHeatButton.qml @@ -0,0 +1,39 @@ +/* Copyright (C) 2015, Jaguar Land Rover. All Rights Reserved. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +import QtQuick 2.0 +import "models" + +Rectangle { + width: 239 + height: 194 + color: "#aa000000" + border.color: "#ff53b5ce" + + property string side: "left" + property string propertyName: side + "SeatHeat" + property int seatHeat: HVACModel[propertyName] + + Image { + source: "./images/" + side + "_heat_seat_off.png" + } + + Image { + y: 150 - seatHeat*40 + height: implicitHeight - y + fillMode: Image.Tile + verticalAlignment: Image.AlignBottom + source: "./images/" + side + "_heat_seat_on.png" + } + + MouseArea { + anchors.fill: parent + onClicked: { + var value = HVACModel[propertyName] + HVACModel[propertyName] = value > 0 ? value - 1 : 3 + } + } +} diff --git a/qml/TempSlider.qml b/qml/TempSlider.qml new file mode 100644 index 0000000..f5500e4 --- /dev/null +++ b/qml/TempSlider.qml @@ -0,0 +1,61 @@ +/* Copyright (C) 2015, Jaguar Land Rover. All Rights Reserved. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +import QtQuick 2.0 +import utils 1.0 +import "models" + +Item { + id: root + width: 64 + height: 716 + + property real value: HVACModel[propertyName] + property string propertyName: side + "Temperature" + property string side: "left" + + function setProperty(v) { + HVACModel[propertyName] = Math.min(Math.max(v, 0), 1) + } + + Rectangle { + anchors.fill: parent + color: "#4a53b5ce" + } + + Rectangle { + width: parent.width + height: value * parent.height + color: Style.orangeViv + anchors.bottom: parent.bottom + } + + Rectangle { + x: side === "left" ? parent.width + 30 : -30 + width: 2 + height: value * parent.height + anchors.bottom: parent.bottom + color: Style.orangeLt + + Image { + width: 30 + height: width + anchors.verticalCenter: parent.top + anchors.horizontalCenter: parent.horizontalCenter + source: "images/drag_knob.svg" + } + } + + MouseArea { + x: side === "left" ? 0 : -45 + width: parent.width + 45 + height: parent.height + + onPressed: setProperty(1 - mouse.y / height) + onPositionChanged: setProperty(1 - mouse.y / height) + } +} + diff --git a/qml/TemperatureWheel.qml b/qml/TemperatureWheel.qml new file mode 100644 index 0000000..7a59dde --- /dev/null +++ b/qml/TemperatureWheel.qml @@ -0,0 +1,56 @@ +/* Copyright (C) 2015, Jaguar Land Rover. All Rights Reserved. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +import QtQuick 2.0 +import "models" + +Rectangle { + width: 237 + height: 350 + color: "#aa000000" + + property string side: "left" + property string propertyName: side + "Temperature" + property real value: HVACModel[propertyName] + + ListView { + anchors.fill: parent + clip: true + snapMode: ListView.SnapToItem + model: TemperatureModel + header: Item { height: 120 } + footer: Item { height: 120 } + currentIndex: Math.min(value * count, count - 1) + flickDeceleration: 5000 + onContentYChanged: { + if (dragging || flicking) { + var item = Math.round((contentY + 120) / 110) + item = Math.max(Math.min(item, count - 1), 0) + if (item != currentIndex) { + var temperature = item / (count - 1) + HVACModel[propertyName] = temperature + } + } + } + highlightMoveDuration: 100 + interactive: true + + delegate: Text { + x: side === "right" ? 40 : 10 + height: 110 + verticalAlignment: Text.AlignVCenter + color: "white" + font.pixelSize: 70 + text: model.text + } + } + + Image { + mirror: side === "left" + source: "./images/right_number_cover.svg" + anchors.fill: parent + } +} diff --git a/qml/hvac-hybrid-qml-app.qml b/qml/hvac-hybrid-qml-app.qml new file mode 100644 index 0000000..eded2d0 --- /dev/null +++ b/qml/hvac-hybrid-qml-app.qml @@ -0,0 +1,87 @@ +import QtQuick 2.0 +import QtQuick.Window 2.0 +import QtQuick.Controls 1.4 +import QtWebSockets 1.0 + +Window { + // VARIABLES + + property string port_str: Qt.application.arguments[1] + property string token_str: Qt.application.arguments[2] + property string address_str: "ws://localhost:"+port_str+"/api?token="+token_str + property string request_str: "" + property string status_str: "waiting..." + property var msgid_enu: { "call":2, "retok":3, "reterr":4, "event":5 } + + // WINDOW PROPERTIES + + visible: true + width: 340 + height: 160 + + // WEBSOCKET WIDGET (MAIN LOGIC) + + WebSocket { + id: websocket + url: address_str + onTextMessageReceived: { + // VERB RESPONSE VALIDATION + var message_json = JSON.parse (message) + var request_json = message_json[2].request + if (message_json[0] != msgid_enu.retok) { + console.log ("Return value is not ok !") + status_str = "Bad return value, binding probably not installed" + return + } + // VERB RESPONSE PARSING AND LOGIC + status_str = request_json.info + } + onStatusChanged: { + if (websocket.status == WebSocket.Error) + status_str = "WebSocket error: " + websocket.errorString + } + active: true + } + + // OTHER WIDGETS + + Rectangle { + anchors.left: parent.left + anchors.top: parent.top + anchors.horizontalCenter: parent.horizontalCenter + anchors.margins: 20 + + // TITLE SECTION + Label { + text: "QML Websocket Sample Application" + font.pixelSize: 18 + font.bold: true + anchors.centerIn: parent + y: 0 + } + Text { + id: url_notifier + text: "<b>URL:</b> " + websocket.url + y: 20 + } + + // PING BUTTON + Button { + text: "Ping!" + onClicked: { + request_str = '[' + msgid_enu.call + ',"99999","xxxxxx/ping", null ]'; + websocket.sendTextMessage (request_str) + } + anchors.horizontalCenter: parent.horizontalCenter + y: 60 + } + + // STATUS SECTION + Text { + id: status_notifier + text: "<b>Status</b>: " + status_str + y: 100 + } + } + +} diff --git a/qml/models/HVACModel.qml b/qml/models/HVACModel.qml new file mode 100644 index 0000000..e737af4 --- /dev/null +++ b/qml/models/HVACModel.qml @@ -0,0 +1,47 @@ +/* Copyright (C) 2015, Jaguar Land Rover. All Rights Reserved. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +pragma Singleton + +import QtQuick 2.0 +import vehicle 1.0 + +Item { + property bool fanUp: false + property bool fanRight: false + property bool fanDown: false + + property bool fanAC: false + property bool fanAuto: false + property bool fanRecirc: false + + property real fanSpeed: 0 + + property bool defrostMax: false + property bool defrostFront: false + property bool defrostRear: false + + property real leftTemperature: 0 + property real rightTemperature: 0 + + property int leftSeatHeat: 0 + property int rightSeatHeat: 0 + + onFanSpeedChanged: { + var currentFan = ClimateModel.getRangeValue(fanSpeed,ClimateModel.fanStepSize); + ClimateModel.fanSpeed = currentFan; + } + + onLeftTemperatureChanged: { + var temperature = ClimateModel.getRangeValue(leftTemperature,ClimateModel.temperatureStepSize); + ClimateModel.leftTemp = temperature; + } + + onRightTemperatureChanged: { + var temperature = ClimateModel.getRangeValue(rightTemperature,ClimateModel.temperatureStepSize); + ClimateModel.rightTemp = temperature; + } +} diff --git a/qml/models/TemperatureModel.qml b/qml/models/TemperatureModel.qml new file mode 100644 index 0000000..85ca415 --- /dev/null +++ b/qml/models/TemperatureModel.qml @@ -0,0 +1,28 @@ +/* Copyright (C) 2015, Jaguar Land Rover. All Rights Reserved. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +pragma Singleton + +import QtQuick 2.0 + +ListModel { + ListElement { text: "LO" } + ListElement { text: "16\u00b0" } + ListElement { text: "17\u00b0" } + ListElement { text: "18\u00b0" } + ListElement { text: "19\u00b0" } + ListElement { text: "20\u00b0" } + ListElement { text: "21\u00b0" } + ListElement { text: "22\u00b0" } + ListElement { text: "23\u00b0" } + ListElement { text: "24\u00b0" } + ListElement { text: "25\u00b0" } + ListElement { text: "26\u00b0" } + ListElement { text: "27\u00b0" } + ListElement { text: "28\u00b0" } + ListElement { text: "29\u00b0" } + ListElement { text: "HI" } +} diff --git a/qml/models/qmldir b/qml/models/qmldir new file mode 100644 index 0000000..d757168 --- /dev/null +++ b/qml/models/qmldir @@ -0,0 +1,8 @@ +#/* Copyright (C) 2015, Jaguar Land Rover. All Rights Reserved. +# * +# * This Source Code Form is subject to the terms of the Mozilla Public +# * License, v. 2.0. If a copy of the MPL was not distributed with this +# * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +singleton HVACModel 1.0 HVACModel.qml +singleton TemperatureModel 1.0 TemperatureModel.qml |