/* * 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 "vcan0" #define SIMULATE_HVAC static const struct afb_binding_interface *interface; // Initialize CAN hvac array that will be sent trough the socket static struct { const char *name; uint8_t value; } hvac_values[] = { { "LeftTemperature", 21 }, { "RightTemperature", 21 }, { "Temperature", 21 }, { "FanSpeed", 0 } }; 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 to_can_temp(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 uint8_t read_temp_left_zone() { return hvac_values[0].value; } static uint8_t read_temp_right_zone() { return hvac_values[1].value; } static uint8_t read_temp() { return (uint8_t)(((int)read_temp_left_zone() + (int)read_temp_right_zone()) >> 1); } static uint8_t read_fanspeed() { return hvac_values[3].value; } 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] = to_can_temp(read_temp_left_zone()); txCanFrame.data[1] = to_can_temp(read_temp_right_zone()); txCanFrame.data[2] = to_can_temp(read_temp()); txCanFrame.data[3] = 0xf0; txCanFrame.data[4] = read_fanspeed(); txCanFrame.data[5] = 1; txCanFrame.data[6] = 0; txCanFrame.data[7] = 0; #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; } /*****************************************************************************************/ /*****************************************************************************************/ /** **/ /** **/ /** 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, changed; struct json_object *query, *val; uint8_t values[sizeof hvac_values / sizeof *hvac_values]; uint8_t saves[sizeof hvac_values / sizeof *hvac_values]; /* records initial values */ DEBUG(interface, "Records initial values"); i = (int)(sizeof hvac_values / sizeof *hvac_values); while (i) { i--; values[i] = saves[i] = hvac_values[i].value; } /* Loop getting arguments */ query = afb_req_json(request); changed = 0; i = (int)(sizeof hvac_values / sizeof *hvac_values); 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, hvac_values[i].name); if (json_object_object_get_ex(query, hvac_values[i].name, &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", hvac_values[i].name); 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", hvac_values[i].name); return; } if (values[i] != x) { values[i] = (uint8_t)x; changed = 1; } } DEBUG(interface, "Not found !"); } /* attemps to set new values */ DEBUG(interface, "Diff: %d", changed); if (changed) { i = (int)(sizeof hvac_values / sizeof *hvac_values); while (i) { i--; hvac_values[i].value = values[i]; } rc = write_can(); if (rc >= 0) afb_req_success(request, NULL, NULL); else { /* restore initial values */ i = (int)(sizeof hvac_values / sizeof *hvac_values); while (i) { i--; hvac_values[i].value = saves[i]; } 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(); }