/* * Copyright (C) 2015, 2016 "IoT.bzh" * Author "Romain Forlot" * Author "Jose Bolo" * * 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 #include #include #include #include #include #include #include #include #include #define CAN_DEV "can0" #define SIMULATE_HVAC static const struct afb_binding_interface *interface; // Initialize CAN hvac array that will be sent trough the socket static char *can_hvac_components[8] = { "LeftTemperature", "RightTemperature", "Temperature", NULL, "FanSpeed", NULL, NULL, NULL }; // Initialize CAN hvac array that will be sent trough the socket static uint8_t can_hvac_values[8] = { 21, // LeftTemperature 21, // RightTemperature 21, // AverageTemperature 240, // Don't know why 240 but it was 0xF0 in the original amb hvacplugin 0, // FanSpeed 1, // Don't know why 1 but it was 0x01 in the original amb hvacplugin 0, // Don't know why 0 but it was 0x00 in the original amb hvacplugin 0 // Don't know why 0 but it was 0x00 in the original amb hvacplugin }; struct can_handler { int socket; struct sockaddr_can txAddress; }; static struct can_handler can_handler = { .socket = -1 }; /*****************************************************************************************/ /*****************************************************************************************/ /** **/ /** **/ /** SECTION: HANDLE CAN DEVICE **/ /** **/ /** **/ /*****************************************************************************************/ /*****************************************************************************************/ static int open_can_dev() { #if defined(SIMULATE_HVAC) DEBUG(interface, "Defining can handler socket to 0 and return"); can_handler.socket = 0; return 0; #else struct ifreq ifr; can_handler.socket = socket(PF_CAN, SOCK_RAW, CAN_RAW); if (can_handler.socket < 0) { ERROR(interface, "socket could not be created"); } else { // Attempts to open a socket to CAN bus strcpy(ifr.ifr_name, CAN_DEV); if(ioctl(can_handler.socket, SIOCGIFINDEX, &ifr) < 0) { ERROR(interface, "ioctl failed"); } else { can_handler.txAddress.can_family = AF_CAN; can_handler.txAddress.can_ifindex = ifr.ifr_ifindex; // And bind it to txAddress if (bind(can_handler.socket, (struct sockaddr *)&can_handler.txAddress, sizeof(can_handler.txAddress)) < 0) { ERROR(interface, "bind failed"); } else { return 0; } } close(can_handler.socket); can_handler.socket = -1; } return -1; #endif } // Get original get temperature function from cpp hvacplugin code static uint8_t get_temperature(uint8_t value) { int result = ((0xF0 - 0x10) / 15) * value - 16; if (result < 0x10) result = 0x10; if (result > 0xF0) result = 0xF0; return (uint8_t)result; } static int write_can() { struct can_frame txCanFrame; int rc = 0; rc = can_handler.socket; if (rc >= 0) { // Hardcoded can_id and dlc (data lenght code) txCanFrame.can_id = 0x30; txCanFrame.can_dlc = 8; txCanFrame.data[0] = get_temperature(can_hvac_values[0]); txCanFrame.data[1] = get_temperature(can_hvac_values[1]); txCanFrame.data[2] = get_temperature((can_hvac_values[0] + can_hvac_values[1]) / 2); txCanFrame.data[3] = can_hvac_values[3]; txCanFrame.data[4] = can_hvac_values[4]; txCanFrame.data[5] = can_hvac_values[5]; txCanFrame.data[6] = can_hvac_values[6]; txCanFrame.data[7] = can_hvac_values[7]; #if defined(SIMULATE_HVAC) DEBUG(interface, "WRITING CAN: %d %d [%02x %02x %02x %02x %02x %02x %02x %02x]\n", txCanFrame.can_id, txCanFrame.can_dlc, txCanFrame.data[0], txCanFrame.data[1], txCanFrame.data[2], txCanFrame.data[3], txCanFrame.data[4], txCanFrame.data[5], txCanFrame.data[6], txCanFrame.data[7]); #else rc = sendto(can_handler.socket, &txCanFrame, sizeof(struct can_frame), 0, (struct sockaddr*)&can_handler.txAddress, sizeof(can_handler.txAddress)); if (rc < 0) { ERROR(interface, "Sending can frame failed"); } #endif } else { ERROR(interface, "socket not initialized"); } return rc; } static uint8_t read_temp_left_zone() { return can_hvac_values[0]; } static uint8_t read_temp_right_zone() { return can_hvac_values[1]; } static uint8_t read_fanspeed() { return can_hvac_values[4]; } /*****************************************************************************************/ /*****************************************************************************************/ /** **/ /** **/ /** SECTION: BINDING VERBS IMPLEMENTATION **/ /** **/ /** **/ /*****************************************************************************************/ /*****************************************************************************************/ /* * @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; uint8_t fanspeed = read_fanspeed(); ret_json = json_object_new_object(); json_object_object_add(ret_json, "FanSpeed", json_object_new_int(fanspeed)); afb_req_success(request, ret_json, NULL); } /* * @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; uint8_t temp = read_temp_right_zone(); ret_json = json_object_new_object(); json_object_object_add(ret_json, "RightTemperature", json_object_new_int(temp)); afb_req_success(request, ret_json, NULL); } /* * @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; uint8_t temp = read_temp_left_zone(); ret_json = json_object_new_object(); json_object_object_add(ret_json, "LeftTemperature", json_object_new_int(temp)); afb_req_success(request, ret_json, NULL); } /* * @brief Read all values * * @param struct afb_req : an afb request structure * */ static void get(struct afb_req request) { DEBUG(interface, "Getting all values"); 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(request, ret_json, NULL); } /* * @brief Set a component value using a json object retrieved from request * * @param struct afb_req : an afb request structure * */ static void set(struct afb_req request) { int i, rc, x; struct json_object *query, *val; uint8_t values[sizeof can_hvac_components / sizeof *can_hvac_components]; uint8_t saves[sizeof can_hvac_components / sizeof *can_hvac_components]; /* records initial values */ DEBUG(interface, "Records initial values"); memcpy(values, can_hvac_values, sizeof values); memcpy(saves, can_hvac_values, sizeof saves); /* Loop getting arguments */ query = afb_req_json(request); i = (int)(sizeof can_hvac_components / sizeof *can_hvac_components); DEBUG(interface, "Looping for args. i: %d", i); while (i) { i--; DEBUG(interface, "Searching... query: %s, i: %d, comp: %s", json_object_to_json_string(query), i, can_hvac_components[i]); if (can_hvac_components[i] != NULL && json_object_object_get_ex(query, can_hvac_components[i], &val)) { DEBUG(interface, "We got it. Tests if it is an int or not."); if (!json_object_is_type(val, json_type_int)) { afb_req_fail_f(request, "bad-request", "argument '%s' isn't integer", can_hvac_components[i]); return; } DEBUG(interface, "We get an 'int'. Hail for the int: %d", x); x = json_object_get_int(val); if (x < 0 || x > 255) { afb_req_fail_f(request, "bad-request", "argument '%s' is out of bounds", can_hvac_components[i]); return; } values[i] = (uint8_t)x; } DEBUG(interface, "Not found !"); } /* attemps to set new values */ DEBUG(interface, "Diff: %d", memcmp(can_hvac_values, values, sizeof values)); if (memcmp(can_hvac_values, values, sizeof values) != 0) { memcpy(can_hvac_values, values, sizeof values); rc = write_can(); if (rc >= 0) afb_req_success(request, NULL, NULL); else { /* restore initial values */ memcpy(can_hvac_values, saves, sizeof saves); afb_req_fail(request, "error", "CAN error"); } } else { afb_req_success(request, NULL, "No changes"); } } // TODO: Have to change session management flag to AFB_SESSION_CHECK to use token auth static const struct afb_verb_desc_v1 verbs[]= { {"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"}, {"get_fanspeed" , AFB_SESSION_NONE, get_fanspeed , "Read fan speed"}, {"get" , AFB_SESSION_NONE, get , "Read all values"}, {"set" , AFB_SESSION_NONE, set , "Set a HVAC component value"}, {NULL} }; static const struct afb_binding binding_desc = { .type = AFB_BINDING_VERSION_1, .v1 = { .info = "hvac service", .prefix = "hvac", .verbs = verbs } }; const struct afb_binding *afbBindingV1Register (const struct afb_binding_interface *itf) { interface = itf; return &binding_desc; } int afbBindingV1ServiceInit(struct afb_service service) { return open_can_dev(); }