Commit 00eeb130 authored by 陈伟灿's avatar 陈伟灿

Merge branch 'cwc' into 'master'

【修改内容】1,增加空调网关的online offline 处理;2,调整发现线程,采用select

See merge request chenweican/k-sdk!177
parents bdee3f61 067ec46c
......@@ -69,55 +69,79 @@ void *kk_findccu_handle(void *data)
int sockfd;
struct sockaddr_in saddr;
int r;
int ret = 0;
char recvline[1025] = {0};
char recvline_tmp[1025] = {0};
struct sockaddr_in presaddr;
socklen_t len;
cJSON *json;
cJSON *opcode;
struct timeval time_out;
char *pStart = NULL,*pEnd = NULL;
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
bzero(&saddr, sizeof(saddr));
fd_set server_fd_set;
int yes = 1;
while(1){
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
bzero(&saddr, sizeof(saddr));
setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes ,sizeof(int));// 允许IP地址复用
saddr.sin_family = AF_INET;
saddr.sin_addr.s_addr = htonl(INADDR_ANY);
saddr.sin_port = htons(FINDCCU_LOCAL_PORT);
if(bind(sockfd, (struct sockaddr*)&saddr, sizeof(saddr)) == -1)
{
ERROR_PRINT("bind error...\n");
}
while (1)
{
FD_ZERO(&server_fd_set);
FD_SET(sockfd,&server_fd_set);
time_out.tv_sec = 1;//select会更改timeout的值,所以需要重新初始化超时时间
time_out.tv_usec = 0;
ret = select(sockfd + 1, &server_fd_set,NULL, NULL, &time_out );
if(ret < 0){
DEBUG_PRINT("UDP receiving error......\n");
close(sockfd);
sockfd = -1;
break;
}else if(ret == 0){
saddr.sin_family = AF_INET;
saddr.sin_addr.s_addr = htonl(INADDR_ANY);
saddr.sin_port = htons(FINDCCU_LOCAL_PORT);
if(bind(sockfd, (struct sockaddr*)&saddr, sizeof(saddr)) == -1)
{
ERROR_PRINT("bind error...\n");
}
while (1)
{
r = recvfrom(sockfd, recvline, sizeof(recvline), 0 , (struct sockaddr*)&presaddr, &len);
if (r <= 0){
WARNING_PRINT("read error....\n");
}else{
DEBUG_PRINT("findccu recmsg: %s\n", recvline);
WARNING_PRINT("findccu from ip: %s\n", inet_ntoa(presaddr.sin_addr));
pStart = strstr(recvline, "!");
pEnd = strstr(recvline, "$");
if(pStart != NULL && pEnd != NULL){
memset(recvline_tmp,0x0,sizeof(recvline_tmp));
memcpy(recvline_tmp,pStart+1,(pEnd - pStart - 1));
json=cJSON_Parse(recvline_tmp);
if (!json) {
WARNING_PRINT("Error before: [%s]\n","cJSON_Parse");
}else{
opcode = cJSON_GetObjectItem(json, OPCODE_STRING);
if(opcode != NULL){
if(strcmp(opcode->valuestring,FINDCCU_OPCODE) == 0){
kk_findccu_ack(sockfd,&presaddr);
}else{
if (FD_ISSET(sockfd, &server_fd_set))
{
r = recvfrom(sockfd, recvline, sizeof(recvline), 0 , (struct sockaddr*)&presaddr, &len);
if (r <= 0){
WARNING_PRINT("read error....\n");
close(sockfd);
sockfd = -1;
break;
}else{
DEBUG_PRINT("findccu recmsg: %s\n", recvline);
WARNING_PRINT("findccu from ip: %s\n", inet_ntoa(presaddr.sin_addr));
pStart = strstr(recvline, "!");
pEnd = strstr(recvline, "$");
if(pStart != NULL && pEnd != NULL){
memset(recvline_tmp,0x0,sizeof(recvline_tmp));
memcpy(recvline_tmp,pStart+1,(pEnd - pStart - 1));
json=cJSON_Parse(recvline_tmp);
if (!json) {
WARNING_PRINT("Error before: [%s]\n","cJSON_Parse");
}else{
opcode = cJSON_GetObjectItem(json, OPCODE_STRING);
if(opcode != NULL){
if(strcmp(opcode->valuestring,FINDCCU_OPCODE) == 0){
kk_findccu_ack(sockfd,&presaddr);
}
}
cJSON_Delete(json);
}
}else{
WARNING_PRINT("data error....\n");
}
}
cJSON_Delete(json);
}
}else{
WARNING_PRINT("data error....\n");
}
}
}
}
return NULL;
}
......
......@@ -22,7 +22,7 @@
#define MID_STR_MAXLEN (64)
#define PRODUCT_SECRET_MAXLEN (64 + 1)
#define FIRMWARE_VERSION_MAXLEN (32 + 1)
#define VERSION_MAXLEN (32 + 1)
#define VERSION_MAXLEN (16 + 1)
#define HAL_CID_LEN (64 + 1)
#define NETWORK_ADDR_LEN (16+1) /* IP�����ַ�ij��� */
#define MAC_ADDR_LEN (17+1)
......
......@@ -242,7 +242,7 @@ int dm_mgr_properities_db_create(kk_tsl_t* dev_shadow,char *deviceCode,int devTy
}
int dm_mgr_device_create(_IN_ int dev_type,_IN_ char productCode[PRODUCT_CODE_MAXLEN], _IN_ char deviceCode[DEVICE_CODE_MAXLEN],
_IN_ char mac[DEVICE_MAC_MAXLEN],_IN_ char fatherDeviceCode[DEVICE_CODE_MAXLEN],_IN_ int isOffline, _OU_ int *devid,_OU_ int *heartbeat)
_IN_ char mac[DEVICE_MAC_MAXLEN],_IN_ char fatherDeviceCode[DEVICE_CODE_MAXLEN],_IN_ int isOffline, _OU_ int *devid,_OU_ int *heartbeat,char *version)
{
int res = 0;
dm_mgr_ctx *ctx = _dm_mgr_get_ctx();
......@@ -290,6 +290,9 @@ int dm_mgr_device_create(_IN_ int dev_type,_IN_ char productCode[PRODUCT_CODE_MA
if (dev_type != KK_DM_DEVICE_CCU && fatherDeviceCode != NULL) {
memcpy(node->fatherDeviceCode, fatherDeviceCode, strlen(fatherDeviceCode));
}
if(version != NULL && strlen(version) < 16){
memcpy(node->version, version, strlen(version));
}
node->timestamp = HAL_Uptimes();
//node->dev_status = IOTX_DM_DEV_STATUS_AUTHORIZED;
//if(strcmp(productCode,"1035") == 0){
......@@ -469,7 +472,31 @@ int dm_mgr_update_timestamp_by_devicecode(_IN_ char deviceCode[DEVICE_CODE_MAXLE
ERROR_PRINT("Device Not Found, deviceCode: %s\n", deviceCode);
return FAIL_RETURN;
}
static int _kk_indoor_device_online_report(dm_mgr_dev_node_t *node,int type)//type:0 airgw;1,airswitch gw;2 freshair gw
{
int count = 0;
int isOnline = 0;
int i = 0;
int eplist[65] = {0};
if(type == 0){
count = kk_indoorAir_query_epnums(node->deviceCode,eplist);
}else if(type == 1){
count = kk_subAirSwitch_query_epnums(node->deviceCode,eplist);
}else{
count = kk_subFreshAir_query_epnums(node->deviceCode,eplist);
}
for(i = 0; i < count; i++){
if(type == 0){
isOnline = kk_indoor_air_get_online(node->deviceCode,eplist[i]);
}else if(type == 1){
isOnline = kk_subAirSwitch_get_online(node->deviceCode,eplist[i]);
}else{
isOnline = kk_subFreshAir_get_online(node->deviceCode,eplist[i]);
}
kk_indoor_device_onoffline_report(node->deviceCode,node->productCode,eplist[i],isOnline);
}
return SUCCESS_RETURN;
}
int dm_mgr_set_dev_onoffline(dm_mgr_dev_node_t *node,int isOffline)
{
if(node == NULL){
......@@ -492,8 +519,13 @@ int dm_mgr_set_dev_onoffline(dm_mgr_dev_node_t *node,int isOffline)
/*再报对应网关和子设备的属性,主机上线后子设备属性直接从数据库获取并上报*/
kk_subDev_send_property_get_from_db();
}
else{
}else if(strcmp(node->productType,KK_DM_AIR_GATEWAY_TYPE) == 0){
_kk_indoor_device_online_report(node,0);
}else if(strcmp(node->productType,KK_DM_AIR_SWITCH_GATEWAY_TYPE) == 0){
_kk_indoor_device_online_report(node,1);
}else if(strcmp(node->productType,KK_DM_FRESHAIR_GATEWAY_TYPE) == 0){
_kk_indoor_device_online_report(node,2);
}else{
kk_subDev_update_offline(node->isOffline,node->deviceCode);
/*如果网关在线,下发对应子设备的属性获取*/
if(node->dev_type == KK_DM_DEVICE_GATEWAY){
......@@ -608,6 +640,7 @@ int dm_mgr_init(void)
char mac[DEVICE_MAC_MAXLEN]= {0};
char ccuid[32] = {0};
char pid[32] = {0};
char ccuVer[VERSION_MAXLEN] = {0};
int devId = 0,heartbeat = 0;
memset(ctx, 0, sizeof(dm_mgr_ctx));
/* Create Mutex */
......@@ -624,8 +657,9 @@ int dm_mgr_init(void)
INIT_LIST_HEAD(&ctx->dev_list);
HAL_Get_mac(mac);
kk_sync_init();
HAL_GetVersion(ccuVer);
//kk_sync_get_info();
res = dm_mgr_device_create(KK_DM_DEVICE_CCU,pid,ccuid,mac,"",KK_DEV_UNKNOW,&devId,&heartbeat);
res = dm_mgr_device_create(KK_DM_DEVICE_CCU,pid,ccuid,mac,"",KK_DEV_UNKNOW,&devId,&heartbeat,ccuVer);
if (res != SUCCESS_RETURN) {
goto ERROR;
}
......@@ -1694,10 +1728,10 @@ int dm_mgr_ota_report_version(_IN_ int devid, char *version)
int dm_mgr_subdev_create(int devtype,_IN_ char productCode[PRODUCT_CODE_MAXLEN], _IN_ char deviceCode[DEVICE_CODE_MAXLEN],
_IN_ char mac[DEVICE_MAC_MAXLEN],_IN_ char fatherDeviceCode[DEVICE_CODE_MAXLEN],_IN_ int isOffline, _OU_ int *devid,_OU_ int *heartbeat){
_IN_ char mac[DEVICE_MAC_MAXLEN],_IN_ char fatherDeviceCode[DEVICE_CODE_MAXLEN],_IN_ int isOffline, _OU_ int *devid,_OU_ int *heartbeat,char *version){
int res = 0;
res = dm_mgr_device_create(devtype,productCode,deviceCode,mac,fatherDeviceCode,isOffline,devid,heartbeat);
res = dm_mgr_device_create(devtype,productCode,deviceCode,mac,fatherDeviceCode,isOffline,devid,heartbeat,version);
if(TSL_ALREADY_EXIST == res)
{
ERROR_PRINT("SUBDEV ALREADY EXIST!!!\n");
......
......@@ -24,6 +24,7 @@ typedef struct {
int devid;
int dev_type;
kk_tsl_t *dev_shadow;
char version[DEVICE_VERSION_MAXLEN];
char mac[DEVICE_MAC_MAXLEN];
char productCode[PRODUCT_CODE_MAXLEN];
char deviceCode[DEVICE_CODE_MAXLEN];
......@@ -33,6 +34,7 @@ typedef struct {
int hb_timeout; //heartbeat time
time_t timestamp;
char dormancyDev; //1,休眠设备;0,不是
struct list_head linked_list;
} dm_mgr_dev_node_t;
......@@ -84,7 +86,7 @@ int dm_mgr_search_dev_by_devid(_IN_ int devid, _OU_ dm_mgr_dev_node_t **node);
int dm_mgr_properities_db_create(kk_tsl_t* dev_shadow,char *deviceCode,int devType );
int dm_mgr_get_device_by_devicecode(_IN_ char deviceCode[DEVICE_CODE_MAXLEN], _OU_ dm_mgr_dev_node_t **node);
int dm_mgr_device_create(_IN_ int dev_type,_IN_ char productCode[PRODUCT_CODE_MAXLEN], _IN_ char deviceCode[DEVICE_CODE_MAXLEN],
_IN_ char mac[DEVICE_MAC_MAXLEN],_IN_ char fatherDeviceCode[DEVICE_CODE_MAXLEN],_IN_ int isOffline, _OU_ int *devid,_OU_ int *heartbeat);
_IN_ char mac[DEVICE_MAC_MAXLEN],_IN_ char fatherDeviceCode[DEVICE_CODE_MAXLEN],_IN_ int isOffline, _OU_ int *devid,_OU_ int *heartbeat,char *version);
int dm_mgr_search_device_by_pkdn( _IN_ char deviceCode[DEVICE_CODE_MAXLEN],_OU_ int *devid);
int dm_mgr_get_devicetype_by_devicecode(_IN_ char deviceCode[DEVICE_CODE_MAXLEN], _OU_ int* deviceType);
int dm_mgr_get_device_shadow_by_devicecode(_IN_ char deviceCode[DEVICE_CODE_MAXLEN], _OU_ kk_tsl_t **shadow);
......@@ -119,7 +121,7 @@ int dm_mgr_upstream_combine_login(_IN_ int devid);
int dm_mgr_upstream_combine_logout(_IN_ int devid);
int dm_mgr_ota_report_version(_IN_ int devid, char *version);
int dm_mgr_subdev_create(int devtype,_IN_ char productCode[PRODUCT_CODE_MAXLEN], _IN_ char deviceCode[DEVICE_CODE_MAXLEN],
_IN_ char mac[DEVICE_MAC_MAXLEN],_IN_ char fatherDeviceCode[DEVICE_CODE_MAXLEN],_IN_ int isOffline, _OU_ int *devid,_OU_ int *heartbeat);
_IN_ char mac[DEVICE_MAC_MAXLEN],_IN_ char fatherDeviceCode[DEVICE_CODE_MAXLEN],_IN_ int isOffline, _OU_ int *devid,_OU_ int *heartbeat,char *version);
int dm_mgr_subdev_delete(_IN_ char deviceCode[DEVICE_CODE_MAXLEN]);
int dm_msg_thing_property_post_all(char *deviceCode);
int dm_mgr_get_gw_deviceCode(_OU_ dm_mgr_dev_node_t **node);
......
......@@ -1208,6 +1208,8 @@ int dm_msg_thing_property_post_by_identify(char *productCode,char *deviceCode,cJ
cJSON_AddStringToObject(rootData,"epNum", epIdx);
if(versionItem != NULL && versionFlag == 0){
cJSON_AddStringToObject(rootData,"version", versionItem->valuestring);
}else if(versionItem == NULL && versionFlag == 0){//为了快速上报版本号,属性上报时中控带上版本号上报
cJSON_AddStringToObject(rootData,"version", node->version);
}
for(i = 0; i < countPro; i++){
int vtype = propertyInfoBuf[idx].info[i].type;
......@@ -1612,4 +1614,44 @@ int kk_msg_execute_scene_delete(const char* params,const char *fatherDeviceCode)
return SUCCESS_RETURN;
}
int kk_indoor_device_onoffline_report(char *deviceCode,char *productCode,int ep,int isOnline)
{
int res = 0;
int i = 0,num = 0;
char ccuid[32] = {0};
char msgId[MSG_MAX_LEN] = {0};
if(deviceCode == NULL || productCode == NULL){
return INVALID_PARAMETER;
}
cJSON *info = cJSON_CreateObject();
cJSON *payload = cJSON_CreateObject();
if(isOnline){
cJSON_AddStringToObject(info, MSG_TYPE_STR, "/thing/status/online");
cJSON_AddStringToObject(payload, "method", "thing.status.online");
}else{
cJSON_AddStringToObject(info, MSG_TYPE_STR, "/thing/status/offline");
cJSON_AddStringToObject(payload, "method", "thing.status.offline");
}
cJSON_AddStringToObject(info, MSG_DEVICE_CODE_STR, deviceCode);
cJSON_AddStringToObject(info, MSG_PRODUCT_CODE_STR, productCode);
char *infff=cJSON_Print(info);
iotx_report_id(msgId);
cJSON_AddStringToObject(payload, "version", "1.0");
cJSON_AddStringToObject(payload, "msgId", msgId);
cJSON_AddNumberToObject(payload,"time", HAL_GetTime_ms());
cJSON *Item = cJSON_CreateObject();
HAL_Get_ccuid(ccuid);
cJSON_AddStringToObject(Item, "ccuCode", ccuid);
cJSON_AddStringToObject(Item, MSG_DEVICE_CODE_STR, deviceCode);
cJSON_AddNumberToObject(Item, "epNum", ep);
cJSON_AddItemToObject(payload, "params", Item);
char *payload11=cJSON_Print(payload);
kk_sendData2app(infff,payload11,0);
free(payload11);
free(infff);
cJSON_Delete(payload);
cJSON_Delete(info);
return res;
}
......@@ -46,6 +46,7 @@ typedef struct {
#define KK_ADD_TOPIC_REPLY "/thing/topo/add_reply"
#define KK_LOGIN_TOPIC_REPLY "/thing/combine/login_reply"
#define KK_THING_STATUS_ONLINE "/thing/status/online"
#define KK_THING_STATUS_OFFLINE "/thing/status/offline"
#define KK_ONLINE_TOPIC_REPLY "/thing/status/online_reply"
#define KK_THING_SERVICE_PROPERTY_SET "/thing/service/property/set"
#define KK_THING_SERVICE_PROPERTY_GET "/thing/service/property/get"
......@@ -200,6 +201,7 @@ int kk_msg_cloud_status_notify(char *deviceCode,const char *productCode);
int dm_msg_thing_syncdeviceinfo_reply(cJSON *msgId);
int dm_msg_heartbeat_cloud(_IN_ char productCode[PRODUCT_CODE_MAXLEN],
_IN_ char deviceCode[DEVICE_CODE_MAXLEN], _OU_ dm_msg_request_t *request);
int kk_indoor_device_onoffline_report(char *deviceCode,char *productCode,int ep,int isOnline);
//const char DM_URI_SYS_PREFIX[] DM_READ_ONLY = "/sys/%s/%s/";
......
......@@ -2677,7 +2677,7 @@ int kk_mid_subdev_add(int devType, char productCode[PRODUCT_CODE_MAXLEN], char d
int devid = 0;
int heartbeat = 0;
dm_mgr_dev_node_t *node = NULL;
res = dm_mgr_subdev_create(devType,productCode,deviceCode,mac,fatherDeviceCode,KK_DEV_ONLINE,&devid,&heartbeat);
res = dm_mgr_subdev_create(devType,productCode,deviceCode,mac,fatherDeviceCode,KK_DEV_ONLINE,&devid,&heartbeat,version);
if (res != SUCCESS_RETURN && TSL_ALREADY_EXIST != res) {
ERROR_PRINT("subdev create Failed\n");
return FAIL_RETURN;
......@@ -2741,11 +2741,11 @@ int kk_mid_subdev_add(int devType, char productCode[PRODUCT_CODE_MAXLEN], char d
*其他说明:
*************************************************************/
int kk_mid_subdev_batch_add( char productCode[PRODUCT_CODE_MAXLEN], char deviceCode[DEVICE_CODE_MAXLEN],char mac[DEVICE_MAC_MAXLEN],char fatherDeviceCode[DEVICE_CODE_MAXLEN]){
int kk_mid_subdev_batch_add( char productCode[PRODUCT_CODE_MAXLEN], char deviceCode[DEVICE_CODE_MAXLEN],char mac[DEVICE_MAC_MAXLEN],char fatherDeviceCode[DEVICE_CODE_MAXLEN],char *version){
int res = 0;
int devid = 0;
int heartbeat = 0;
res = dm_mgr_subdev_create(KK_DM_DEVICE_SUBDEV,productCode,deviceCode,mac,fatherDeviceCode,KK_DEV_ONLINE,&devid,&heartbeat);
res = dm_mgr_subdev_create(KK_DM_DEVICE_SUBDEV,productCode,deviceCode,mac,fatherDeviceCode,KK_DEV_ONLINE,&devid,&heartbeat,version);
if (res != SUCCESS_RETURN && TSL_ALREADY_EXIST != res) {
ERROR_PRINT("subdev create Failed\n");
return FAIL_RETURN;
......
#ifndef _KK_LINKKIT_H_
#define _KK_LINKKIT_H_
#include "kk_tsl_common.h"
int kk_mid_subdev_batch_add( char productCode[PRODUCT_CODE_MAXLEN], char deviceCode[DEVICE_CODE_MAXLEN],char mac[DEVICE_MAC_MAXLEN],char fatherDeviceCode[DEVICE_CODE_MAXLEN]);
int kk_mid_subdev_batch_add( char productCode[PRODUCT_CODE_MAXLEN], char deviceCode[DEVICE_CODE_MAXLEN],char mac[DEVICE_MAC_MAXLEN],char fatherDeviceCode[DEVICE_CODE_MAXLEN],char *version);
int kk_topo_delete_handle(cJSON *payload,cJSON *buf);
int kk_get_cloudstatus(void);
int kk_get_cloud_recv_status(void);
......
......@@ -106,7 +106,8 @@ static int _kk_property_db_Init(void)
identifier varchar(33), \
value varchar(33), \
valueType INTEGER, \
epNum INTEGER)";
epNum INTEGER, \
online INTEGER)";
if (sqlite3_exec(ctx->pDb, pIndoorAirtable, NULL, NULL, &pcErr) != SQLITE_OK)
{
......@@ -122,7 +123,8 @@ static int _kk_property_db_Init(void)
identifier varchar(33), \
value varchar(33), \
valueType INTEGER, \
epNum INTEGER)";
epNum INTEGER, \
online INTEGER)";
if (sqlite3_exec(ctx->pDb, pSubAirSwitchtable, NULL, NULL, &pcErr) != SQLITE_OK)
{
ERROR_PRINT("Error creating table (%s)\n", pcErr);
......@@ -137,7 +139,8 @@ static int _kk_property_db_Init(void)
identifier varchar(33), \
value varchar(33), \
valueType INTEGER, \
epNum INTEGER)";
epNum INTEGER, \
online INTEGER)";
if (sqlite3_exec(ctx->pDb, pSubFreshAirtable, NULL, NULL, &pcErr) != SQLITE_OK)
{
......@@ -400,8 +403,8 @@ static int _kk_check_subAirSwitch_exist(const char* deviceCode,const char *ident
*************************************************************/
int kk_subAirSwitch_db_insert(const char *deviceCode,const char *identifier,kk_tsl_data_type_e valuetype,int epNum)
{
const char *insertCmd = "insert into subAirSwitchProperties (deviceCode,identifier,value,valueType,epNum) \
values ('%s','%s','%s','%d','%d');";
const char *insertCmd = "insert into subAirSwitchProperties (deviceCode,identifier,value,valueType,epNum,online) \
values ('%s','%s','%s','%d','%d','%d');";
char *sqlCmd = NULL;
int rc = 0;
char *zErrMsg = 0;
......@@ -411,7 +414,7 @@ int kk_subAirSwitch_db_insert(const char *deviceCode,const char *identifier,kk_t
return SUCCESS_RETURN;
}
_kk_property_db_lock();
sqlCmd = sqlite3_mprintf(insertCmd,deviceCode,identifier,"",valuetype,epNum);
sqlCmd = sqlite3_mprintf(insertCmd,deviceCode,identifier,"",valuetype,epNum,1);
rc = sqlite3_exec(ctx->pDb, sqlCmd, NULL, NULL, &zErrMsg);
if( rc != SQLITE_OK ){
......@@ -520,7 +523,67 @@ int kk_subAirSwitch_db_update_value(const char *deviceCode,const char *identifie
_kk_property_db_unlock();
return SUCCESS_RETURN;
}
/************************************************************
*功能描述: 更新属性值
*输入参数: deviceCode:设备deviceCode
identifier:属性名称
value:属性值
*输出参数: 无
*返 回 值: 0:成功;其他:失败
*其他说明:
*************************************************************/
int kk_subAirSwitch_db_update_online(const char *deviceCode,int online,int epNum)
{
char *sqlCmd = NULL;
int rc = 0;
char *zErrMsg = 0;
kk_property_db_ctx_t *ctx = _kk_property_db_get_ctx();
_kk_property_db_lock();
//if()
sqlCmd = sqlite3_mprintf("UPDATE subAirSwitchProperties SET online='%d' WHERE (deviceCode= '%s' and epNum = '%d')) ",online,deviceCode,epNum);
//DEBUG_PRINT("kk_property_db_update_value sqlCmd:%s\n",sqlCmd);
rc = sqlite3_exec(ctx->pDb, sqlCmd, NULL, NULL, &zErrMsg);
if( rc != SQLITE_OK ){
ERROR_PRINT("SQL error: %s\n", zErrMsg);
sqlite3_free(zErrMsg);
}else{
//DEBUG_PRINT("kk_property_db_update_value successfully\n");
}
sqlite3_free(sqlCmd);
_kk_property_db_unlock();
return SUCCESS_RETURN;
}
/************************************************************
*功能描述: 获取属性值
*输入参数: deviceCode:设备deviceCode
identifier:属性名称
*输出参数: value:属性值,返回的是字串
*返 回 值: 0:成功;其他:失败
*其他说明:
*************************************************************/
int kk_subAirSwitch_get_online(const char *deviceCode,int ep)
{
char *sqlCmd = NULL;
//int rc = 0;
//char *zErrMsg = 0;
sqlite3_stmt *stmt;
int online = 0;
kk_property_db_ctx_t *ctx = _kk_property_db_get_ctx();
_kk_property_db_lock();
sqlCmd = sqlite3_mprintf("select * from subAirSwitchProperties WHERE deviceCode= '%s' and epNum = '%d'",deviceCode,ep);
sqlite3_prepare_v2(ctx->pDb, sqlCmd, strlen(sqlCmd), &stmt, NULL);
while(sqlite3_step(stmt) == SQLITE_ROW){
online = sqlite3_column_int(stmt, DB_SUBFRESHAIR_ONLINE);
break;
}
sqlite3_free(sqlCmd);
sqlite3_finalize(stmt);
_kk_property_db_unlock();
return online;
}
static int _kk_check_subFreshAir_exist(const char* deviceCode,const char *identifier,int epNum)
{
int isExist = 0;
......@@ -559,8 +622,8 @@ static int _kk_check_subFreshAir_exist(const char* deviceCode,const char *identi
*************************************************************/
int kk_subFreshAir_db_insert(const char *deviceCode,const char *identifier,kk_tsl_data_type_e valuetype,int epNum)
{
const char *insertCmd = "insert into subFreshAirProperties (deviceCode,identifier,value,valueType,epNum) \
values ('%s','%s','%s','%d','%d');";
const char *insertCmd = "insert into subFreshAirProperties (deviceCode,identifier,value,valueType,epNum,online) \
values ('%s','%s','%s','%d','%d','%d');";
char *sqlCmd = NULL;
int rc = 0;
char *zErrMsg = 0;
......@@ -570,7 +633,7 @@ int kk_subFreshAir_db_insert(const char *deviceCode,const char *identifier,kk_ts
return SUCCESS_RETURN;
}
_kk_property_db_lock();
sqlCmd = sqlite3_mprintf(insertCmd,deviceCode,identifier,"",valuetype,epNum);
sqlCmd = sqlite3_mprintf(insertCmd,deviceCode,identifier,"",valuetype,epNum,1);
rc = sqlite3_exec(ctx->pDb, sqlCmd, NULL, NULL, &zErrMsg);
if( rc != SQLITE_OK ){
......@@ -679,7 +742,67 @@ int kk_subFreshAir_db_update_value(const char *deviceCode,const char *identifier
_kk_property_db_unlock();
return SUCCESS_RETURN;
}
/************************************************************
*功能描述: 更新属性值
*输入参数: deviceCode:设备deviceCode
identifier:属性名称
value:属性值
*输出参数: 无
*返 回 值: 0:成功;其他:失败
*其他说明:
*************************************************************/
int kk_subFreshAir_db_update_online(const char *deviceCode,int online,int epNum)
{
char *sqlCmd = NULL;
int rc = 0;
char *zErrMsg = 0;
kk_property_db_ctx_t *ctx = _kk_property_db_get_ctx();
_kk_property_db_lock();
//if()
sqlCmd = sqlite3_mprintf("UPDATE subFreshAirProperties SET online='%d' WHERE (deviceCode= '%s' and epNum = '%d')) ",online,deviceCode,epNum);
//DEBUG_PRINT("kk_property_db_update_value sqlCmd:%s\n",sqlCmd);
rc = sqlite3_exec(ctx->pDb, sqlCmd, NULL, NULL, &zErrMsg);
if( rc != SQLITE_OK ){
ERROR_PRINT("SQL error: %s\n", zErrMsg);
sqlite3_free(zErrMsg);
}else{
//DEBUG_PRINT("kk_property_db_update_value successfully\n");
}
sqlite3_free(sqlCmd);
_kk_property_db_unlock();
return SUCCESS_RETURN;
}
/************************************************************
*功能描述: 获取属性值
*输入参数: deviceCode:设备deviceCode
identifier:属性名称
*输出参数: value:属性值,返回的是字串
*返 回 值: 0:成功;其他:失败
*其他说明:
*************************************************************/
int kk_subFreshAir_get_online(const char *deviceCode,int ep)
{
char *sqlCmd = NULL;
//int rc = 0;
//char *zErrMsg = 0;
sqlite3_stmt *stmt;
int online = 0;
kk_property_db_ctx_t *ctx = _kk_property_db_get_ctx();
_kk_property_db_lock();
sqlCmd = sqlite3_mprintf("select * from subFreshAirProperties WHERE deviceCode= '%s' and epNum = '%d'",deviceCode,ep);
sqlite3_prepare_v2(ctx->pDb, sqlCmd, strlen(sqlCmd), &stmt, NULL);
while(sqlite3_step(stmt) == SQLITE_ROW){
online = sqlite3_column_int(stmt, DB_SUBFRESHAIR_ONLINE);
break;
}
sqlite3_free(sqlCmd);
sqlite3_finalize(stmt);
_kk_property_db_unlock();
return online;
}
/************************************************************
*功能描述: 插入空调内机属性到数据库
......@@ -691,8 +814,8 @@ int kk_subFreshAir_db_update_value(const char *deviceCode,const char *identifier
*************************************************************/
int kk_indoorAir_db_insert(const char *deviceCode,const char *identifier,kk_tsl_data_type_e valuetype,int epNum)
{
const char *insertCmd = "insert into indoorAirProperties (deviceCode,identifier,value,valueType,epNum) \
values ('%s','%s','%s','%d','%d');";
const char *insertCmd = "insert into indoorAirProperties (deviceCode,identifier,value,valueType,epNum,online) \
values ('%s','%s','%s','%d','%d','%d');";
char *sqlCmd = NULL;
int rc = 0;
char *zErrMsg = 0;
......@@ -702,7 +825,7 @@ int kk_indoorAir_db_insert(const char *deviceCode,const char *identifier,kk_tsl_
return SUCCESS_RETURN;
}
_kk_property_db_lock();
sqlCmd = sqlite3_mprintf(insertCmd,deviceCode,identifier,"",valuetype,epNum);
sqlCmd = sqlite3_mprintf(insertCmd,deviceCode,identifier,"",valuetype,epNum,1);
rc = sqlite3_exec(ctx->pDb, sqlCmd, NULL, NULL, &zErrMsg);
if( rc != SQLITE_OK ){
......@@ -814,6 +937,67 @@ int kk_indoorAir_db_update_value(const char *deviceCode,const char *identifier,c
return SUCCESS_RETURN;
}
/************************************************************
*功能描述: 更新属性值
*输入参数: deviceCode:设备deviceCode
identifier:属性名称
value:属性值
*输出参数: 无
*返 回 值: 0:成功;其他:失败
*其他说明:
*************************************************************/
int kk_indoorAir_db_update_online(const char *deviceCode,int online,int epNum)
{
char *sqlCmd = NULL;
int rc = 0;
char *zErrMsg = 0;
kk_property_db_ctx_t *ctx = _kk_property_db_get_ctx();
_kk_property_db_lock();
//if()
sqlCmd = sqlite3_mprintf("UPDATE indoorAirProperties SET online='%d' WHERE (deviceCode= '%s' and epNum = '%d')) ",online,deviceCode,epNum);
//DEBUG_PRINT("kk_property_db_update_value sqlCmd:%s\n",sqlCmd);
rc = sqlite3_exec(ctx->pDb, sqlCmd, NULL, NULL, &zErrMsg);
if( rc != SQLITE_OK ){
ERROR_PRINT("SQL error: %s\n", zErrMsg);
sqlite3_free(zErrMsg);
}else{
//DEBUG_PRINT("kk_property_db_update_value successfully\n");
}
sqlite3_free(sqlCmd);
_kk_property_db_unlock();
return SUCCESS_RETURN;
}
/************************************************************
*功能描述: 获取属性值
*输入参数: deviceCode:设备deviceCode
identifier:属性名称
*输出参数: value:属性值,返回的是字串
*返 回 值: 0:成功;其他:失败
*其他说明:
*************************************************************/
int kk_indoor_air_get_online(const char *deviceCode,int ep)
{
char *sqlCmd = NULL;
//int rc = 0;
//char *zErrMsg = 0;
sqlite3_stmt *stmt;
int online = 0;
kk_property_db_ctx_t *ctx = _kk_property_db_get_ctx();
_kk_property_db_lock();
sqlCmd = sqlite3_mprintf("select * from indoorAirProperties WHERE deviceCode= '%s' and epNum = '%d'",deviceCode,ep);
sqlite3_prepare_v2(ctx->pDb, sqlCmd, strlen(sqlCmd), &stmt, NULL);
while(sqlite3_step(stmt) == SQLITE_ROW){
online = sqlite3_column_int(stmt, DB_INDOORAIR_ONLINE);
break;
}
sqlite3_free(sqlCmd);
sqlite3_finalize(stmt);
_kk_property_db_unlock();
return online;
}
/************************************************************
*功能描述: 获取属性值
*输入参数: deviceCode:设备deviceCode
identifier:属性名称
......
......@@ -38,7 +38,8 @@ enum{
DB_INDOORAIR_IDENTIFITER,
DB_INDOORAIR_VALUE,
DB_INDOORAIR_VALUETYPE,
DB_INDOORAIR_EPNUM
DB_INDOORAIR_EPNUM,
DB_INDOORAIR_ONLINE
};
enum{
DB_SUBAIRSWITCH_IDX = 0,
......@@ -46,7 +47,8 @@ enum{
DB_SUBAIRSWITCH_IDENTIFITER,
DB_SUBAIRSWITCH_VALUE,
DB_SUBAIRSWITCH_VALUETYPE,
DB_SUBAIRSWITCH_EPNUM
DB_SUBAIRSWITCH_EPNUM,
DB_SUBAIRSWITCH_ONLINE
};
enum{
DB_SUBFRESHAIR_IDX = 0,
......@@ -54,7 +56,8 @@ enum{
DB_SUBFRESHAIR_IDENTIFITER,
DB_SUBFRESHAIR_VALUE,
DB_SUBFRESHAIR_VALUETYPE,
DB_SUBFRESHAIR_EPNUM
DB_SUBFRESHAIR_EPNUM,
DB_SUBFRESHAIR_ONLINE
};
int kk_property_db_init(void);
int kk_property_db_get_rawdata(const char *identifier,const int dev_type, kk_prop_raw_struct_t* raw, int count);
......@@ -81,5 +84,6 @@ int kk_subFreshAir_db_insert(const char *deviceCode,const char *identifier,kk_ts
int kk_subFreshAir_query_epnums(const char *deviceCode,int epList[]);
int kk_subFreshAir_delete_by_dcode(char deviceCode[DEVICE_CODE_MAXLEN]);
int kk_subFreshAir_db_update_value(const char *deviceCode,const char *identifier,const char* value,int epNum);
int kk_indoor_air_get_online(const char *deviceCode,int ep);
#endif
......@@ -130,6 +130,7 @@ static int _kk_load_subDevice(void)
{
const char *searchCmd = "select * from SubDeviceInfo;";
sqlite3_stmt *stmt;
char *version;
int deviceType = 0;
kk_subDb_ctx_t *ctx = _kk_subDb_get_ctx();
int devId = 0,heartbeat = 0;
......@@ -138,12 +139,13 @@ static int _kk_load_subDevice(void)
sqlite3_prepare_v2(ctx->pDb, searchCmd, strlen(searchCmd), &stmt, NULL);
while(sqlite3_step(stmt) == SQLITE_ROW){
deviceType = sqlite3_column_int(stmt, DB_SUB_DEVTYPE);
version = (char*)sqlite3_column_text(stmt, DB_SUB_VERSION),
res = dm_mgr_subdev_create(deviceType,
(char*)sqlite3_column_text(stmt, DB_SUB_PRODUCTCODE),
(char*)sqlite3_column_text(stmt, DB_SUB_DEVICECODE),
(char*)sqlite3_column_text(stmt, DB_SUB_MAC),
(char*)sqlite3_column_text(stmt, DB_SUB_FATHERDEVICECODE),
KK_DEV_UNKNOW,&devId,&heartbeat);
KK_DEV_UNKNOW,&devId,&heartbeat,version);
if(res != SUCCESS_RETURN){
ERROR_PRINT("[%s][%d]dm_mgr_subdev_create FAIL!!!\n",__FUNCTION__,__LINE__);
......
......@@ -86,9 +86,6 @@ static cJSON * kk_get_room_devices(const char *roomId)
const char *selectCmd = "select * from AreaDevInfo WHERE roomId = '%s';";
int res = 0;
cJSON *dev = NULL;
int airGw = 0;
int airSwitchGw = 0;
int airFreshGw = 0;
char *sqlCmd = NULL;
sqlite3_stmt *stmt;
char *deviceCode = NULL;
......@@ -112,30 +109,11 @@ static cJSON * kk_get_room_devices(const char *roomId)
if(kk_check_multi_ep_num(deviceCode)){
dev = cJSON_CreateObject();
cJSON_AddStringToObject(dev,KK_SYNC_SCENE_EPNUM_STR,epNum);
}else if(strcmp(node->productType,KK_DM_AIR_GATEWAY_TYPE) == 0){//空调网关只add网关本身
if(airGw == 0){
dev = cJSON_CreateObject();
cJSON_AddStringToObject(dev,KK_SYNC_SCENE_EPNUM_STR,"1");
airGw = 1;
}else{
continue;
}
}else if(strcmp(node->productType,KK_DM_FRESHAIR_GATEWAY_TYPE) == 0){
if(airFreshGw == 0){
dev = cJSON_CreateObject();
cJSON_AddStringToObject(dev,KK_SYNC_SCENE_EPNUM_STR,"1");
airFreshGw = 1;
}else{
continue;
}
}else if(strcmp(node->productType,KK_DM_AIR_SWITCH_GATEWAY_TYPE) == 0){
if(airSwitchGw == 0){
dev = cJSON_CreateObject();
cJSON_AddStringToObject(dev,KK_SYNC_SCENE_EPNUM_STR,"1");
airSwitchGw = 1;
}else{
continue;
}
}else if(strcmp(node->productType,KK_DM_AIR_GATEWAY_TYPE) == 0 ||
strcmp(node->productType,KK_DM_FRESHAIR_GATEWAY_TYPE) == 0 ||
strcmp(node->productType,KK_DM_AIR_SWITCH_GATEWAY_TYPE) == 0){
dev = cJSON_CreateObject();
cJSON_AddStringToObject(dev,KK_SYNC_SCENE_EPNUM_STR,epNum);
}else{
dev = cJSON_CreateObject();
}
......@@ -454,6 +432,7 @@ static cJSON *kk_get_indoor_properties_info(char *deviceCode)
int eplist[65] = {0};
int count = 0,i = 0;
char *ptr = NULL;
int online = 0;
kk_sync_ctx_t *ctx = _kk_sync_get_ctx();
if(deviceCode == NULL){
return NULL;
......@@ -480,7 +459,10 @@ static cJSON *kk_get_indoor_properties_info(char *deviceCode)
}else if(valueType == KK_TSL_DATA_TYPE_DOUBLE){
cJSON_AddNumberToObject(propertyItem, propertyStr, atof(valueStr));
}
}
online = kk_indoor_air_get_online(deviceCode,eplist[i]);
cJSON_AddNumberToObject(propertyItem, "onlineStatus", online);
cJSON_AddItemToArray(eps,propertyItem);
sqlite3_free(sqlCmd);
sqlite3_finalize(stmt);
......@@ -501,6 +483,7 @@ static cJSON *kk_get_subfreshair_properties_info(char *deviceCode)
int eplist[65] = {0};
int count = 0,i = 0;
char *ptr = NULL;
int online = 0;
kk_sync_ctx_t *ctx = _kk_sync_get_ctx();
if(deviceCode == NULL){
return NULL;
......@@ -528,6 +511,8 @@ static cJSON *kk_get_subfreshair_properties_info(char *deviceCode)
cJSON_AddNumberToObject(propertyItem, propertyStr, atof(valueStr));
}
}
online = kk_subFreshAir_get_online(deviceCode,eplist[i]);
cJSON_AddNumberToObject(propertyItem, "onlineStatus", online);
cJSON_AddItemToArray(eps,propertyItem);
}
cJSON_AddItemToObject(obj, "eps", eps);
......@@ -548,6 +533,7 @@ static cJSON *kk_get_subAirSwitch_properties_info(char *deviceCode)
int eplist[65] = {0};
int count = 0,i = 0;
char *ptr = NULL;
int online = 0;
kk_sync_ctx_t *ctx = _kk_sync_get_ctx();
if(deviceCode == NULL){
return NULL;
......@@ -575,6 +561,8 @@ static cJSON *kk_get_subAirSwitch_properties_info(char *deviceCode)
cJSON_AddNumberToObject(propertyItem, propertyStr, atof(valueStr));
}
}
online = kk_subAirSwitch_get_online(deviceCode,eplist[i]);
cJSON_AddNumberToObject(propertyItem, "onlineStatus", online);
cJSON_AddItemToArray(eps,propertyItem);
}
cJSON_AddItemToObject(obj, "eps", eps);
......
......@@ -1211,8 +1211,6 @@ static int _kk_subDevice_online_to_app(cJSON *info,cJSON *payload)
cJSON_AddStringToObject(param, "ccuCode", ccuid);
char *payload11=cJSON_Print(payload);
char *infff=cJSON_Print(info);
printf("------------------------payload11:%s\n",payload11);
printf("------------------------infff:%s\n",infff);
kk_sendData2app(infff,payload11,0);
free(payload11);
free(infff);
......@@ -1389,16 +1387,45 @@ void kk_platMsg_handle(void* data, char* chalMark){
cJSON *epNumJson = cJSON_GetObjectItem(jsonPay, "epNum");
if(epNumJson == NULL) goto error;
kk_indoorAir_online_handle(search_node,devCode->valuestring,epNumJson->valueint);
_kk_subDevice_online_to_app(info,payload);
}else if(strcmp(search_node->productType,KK_DM_AIR_SWITCH_GATEWAY_TYPE) == 0){
cJSON *epNumJson = cJSON_GetObjectItem(jsonPay, "epNum");
if(epNumJson == NULL) goto error;
kk_subAirSwitch_online_handle(search_node,devCode->valuestring,epNumJson->valueint);
_kk_subDevice_online_to_app(info,payload);
}else if(strcmp(search_node->productType,KK_DM_FRESHAIR_GATEWAY_TYPE) == 0){
cJSON *epNumJson = cJSON_GetObjectItem(jsonPay, "epNum");
if(epNumJson == NULL) goto error;
kk_subFreshair_online_handle(search_node,devCode->valuestring,epNumJson->valueint);
_kk_subDevice_online_to_app(info,payload);
}
}
else if (strstr(msgType->valuestring, KK_THING_STATUS_OFFLINE) != NULL){//空调内机上报处理
jsonPay = cJSON_GetObjectItem(payload, MSG_PARAMS_STR);
if(jsonPay == NULL) goto error;
devCode = cJSON_GetObjectItem(jsonPay, MSG_DEVICE_CODE_STR);
dm_mgr_dev_node_t *search_node = NULL;
res = dm_mgr_get_device_by_devicecode(devCode->valuestring,&search_node);
if (res < SUCCESS_RETURN) {
goto error;
}
if(strcmp(search_node->productType,KK_DM_AIR_GATEWAY_TYPE) == 0){
cJSON *epNumJson = cJSON_GetObjectItem(jsonPay, "epNum");
if(epNumJson == NULL) goto error;
kk_indoorAir_db_update_online(search_node,0,epNumJson->valueint);
_kk_subDevice_online_to_app(info,payload);
}else if(strcmp(search_node->productType,KK_DM_AIR_SWITCH_GATEWAY_TYPE) == 0){
cJSON *epNumJson = cJSON_GetObjectItem(jsonPay, "epNum");
if(epNumJson == NULL) goto error;
kk_subAirSwitch_db_update_online(search_node,0,epNumJson->valueint);
_kk_subDevice_online_to_app(info,payload);
}else if(strcmp(search_node->productType,KK_DM_FRESHAIR_GATEWAY_TYPE) == 0){
cJSON *epNumJson = cJSON_GetObjectItem(jsonPay, "epNum");
if(epNumJson == NULL) goto error;
kk_subFreshAir_db_update_online(search_node,0,epNumJson->valueint);
_kk_subDevice_online_to_app(info,payload);
}
}
else if (strstr(msgType->valuestring, KK_THING_TOPO_BATCH_ADD_MSG) != NULL){
kk_ipc_send(IPC_MID2APP,data,strlen(data));
jsonPay = cJSON_GetObjectItem(payload, MSG_PARAMS_STR);
......@@ -1411,7 +1438,13 @@ void kk_platMsg_handle(void* data, char* chalMark){
char *productCode = cJSON_GetObjectItem(item,MSG_PRODUCT_CODE_STR)->valuestring;
char *mac_s = cJSON_GetObjectItem(item,MSG_DEVICE_MAC)->valuestring;
char *fatherMac = cJSON_GetObjectItem(item,MSG_DEVICE_PARENTCODE)->valuestring;
kk_mid_subdev_batch_add(productCode,deviceCode,mac_s,fatherMac);
version = cJSON_GetObjectItem(item, "version");
if(version != NULL ){
memcpy(versionBuf,version->valuestring,strlen(version->valuestring));
}else{
memcpy(versionBuf,"1.1.0",strlen("1.1.0"));
}
kk_mid_subdev_batch_add(productCode,deviceCode,mac_s,fatherMac,versionBuf);
item = item->next;
}
}else if (strstr(msgType->valuestring, KK_THING_PROPERTY_POST) != NULL){
......@@ -1447,10 +1480,10 @@ void kk_platMsg_handle(void* data, char* chalMark){
char* outstr = cJSON_Print(payload);
kk_tsl_property_set_by_shadow(search_node->dev_shadow, outstr, strlen(outstr)+1);
proCode = cJSON_GetObjectItem(info, MSG_PRODUCT_CODE_STR);
dm_msg_thing_property_post_by_identify(proCode->valuestring,info_dcode->valuestring,jsonPay);
if(is_BodySensor_dev(proCode->valuestring)==0){
//如果带version,代表的是快照信息,快照信息不需要触发iftt
cJSON *version = cJSON_GetObjectItem(jsonPay, "version");
if(version == NULL && is_BodySensor_dev(proCode->valuestring)==0){
kk_scene_iftt_check(info_dcode->valuestring,jsonPay);
}
free(outstr);
......
......@@ -541,11 +541,11 @@ int kk_scene_action_add(const char *gwdeviceCode,const char *sceneId,kk_scene_ac
//}
}else{
//开启/关闭取反操作
if(strcmp(ptr->actionInfo->info.propertyName,"PowerSwitch") == 0 && strcmp(ptr->actionInfo->info.propertyValue,"2") == 0){
cJSON_AddStringToObject(info,MSG_SCENE_PROPERTYVALUE,"10");
}else{
cJSON_AddStringToObject(info,MSG_SCENE_PROPERTYVALUE,ptr->actionInfo->info.propertyValue);
}
//if(strcmp(ptr->actionInfo->info.propertyName,"PowerSwitch") == 0 && strcmp(ptr->actionInfo->info.propertyValue,"2") == 0){
//cJSON_AddStringToObject(info,MSG_SCENE_PROPERTYVALUE,"10");
//}else{
cJSON_AddStringToObject(info,MSG_SCENE_PROPERTYVALUE,ptr->actionInfo->info.propertyValue);
//}
}
cJSON_AddNumberToObject(info,MSG_SCENE_DELAY,ptr->actionInfo->info.delay);
......
......@@ -20,7 +20,7 @@
#define DEVICE_MAC_MAXLEN (32 + 1)
#define DEVICE_SN_MAXLEN (32 + 1)
#define DEVICE_VERSION_MAXLEN (32 + 1)
#define DEVICE_VERSION_MAXLEN (16 + 1)
#define DM_UTILS_UINT16_STRLEN (5)
#define DM_UTILS_UINT32_STRLEN (10)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment