Mercurial > louis > mq > lightsd
view implement_some_metadata_packet_types.h @ 240:fde704d2089a
wip
author | Louis Opter <kalessin@kalessin.fr> |
---|---|
date | Sun, 09 Aug 2015 17:06:27 -0700 |
parents | 889a4157359e |
children |
line wrap: on
line source
# HG changeset patch # Parent 0955e5e7ff9f454c303a1314cfec54d3c3708cd5 Handle various informational packet types - MCU state & firmware info; - WiFi state & firmware info; - device/runtime info. diff --git a/core/lightsd.h b/core/lightsd.h --- a/core/lightsd.h +++ b/core/lightsd.h @@ -24,10 +24,33 @@ #define LGTD_ABS(v) ((v) >= 0 ? (v) : (v) * -1) #define LGTD_MIN(a, b) ((a) < (b) ? (a) : (b)) #define LGTD_ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0])) -#define LGTD_MSECS_TO_TIMEVAL(v) { \ +#define LGTD_MSECS_TO_TIMEVAL(v) { \ .tv_sec = (v) / 1000, \ .tv_usec = ((v) % 1000) * 1000 \ } +#define LGTD_NSECS_TO_USECS(v) ((v) / (unsigned int)10E6) +#define LGTD_NSECS_TO_SECS(v) ((v) / (unsigned int)10E9) +#define LGTD_SECS_TO_NSECS(v) ((v) * (unsigned int)10E9) +#define LGTD_TM_TO_ISOTIME(tm, sbuf, bufsz, usec) do { \ + /* '2015-01-02T10:13:16.132222+00:00' */ \ + if ((usec)) { \ + snprintf( \ + (sbuf), (bufsz), "%d-%02d-%02dT%02d:%02d:%02d.%jd%c%02ld:%02ld", \ + 1900 + (tm)->tm_year, 1 + (tm)->tm_mon, (tm)->tm_mday, \ + (tm)->tm_hour, (tm)->tm_min, (tm)->tm_sec, (intmax_t)usec, \ + (tm)->tm_gmtoff >= 0 ? '+' : '-', /* %+02ld doesn't work */ \ + LGTD_ABS((tm)->tm_gmtoff / 60 / 60), (tm)->tm_gmtoff % (60 * 60) \ + ); \ + } else { \ + snprintf( \ + (sbuf), (bufsz), "%d-%02d-%02dT%02d:%02d:%02d%c%02ld:%02ld", \ + 1900 + (tm)->tm_year, 1 + (tm)->tm_mon, (tm)->tm_mday, \ + (tm)->tm_hour, (tm)->tm_min, (tm)->tm_sec, \ + (tm)->tm_gmtoff >= 0 ? '+' : '-', /* %+02ld doesn't work */ \ + LGTD_ABS((tm)->tm_gmtoff / 60 / 60), (tm)->tm_gmtoff % (60 * 60) \ + ); \ + } \ +} while (0) enum lgtd_verbosity { LGTD_DEBUG = 0, @@ -51,6 +74,11 @@ void lgtd_sockaddrtoa(const struct sockaddr_storage *, char *buf, int buflen); short lgtd_sockaddrport(const struct sockaddr_storage *); +void lgtd_print_duration(uint64_t, char *, int); +#define LGTD_PRINT_DURATION(secs, arr) do { \ + lgtd_print_duration((secs), (arr), sizeof((arr))); \ +} while (0) + void _lgtd_err(void (*)(int, const char *, ...), int, const char *, ...) __attribute__((format(printf, 3, 4))); #define lgtd_err(eval, fmt, ...) _lgtd_err(err, (eval), (fmt), ##__VA_ARGS__); diff --git a/core/log.c b/core/log.c --- a/core/log.c +++ b/core/log.c @@ -52,14 +52,7 @@ if (!localtime_r(&now.tv_sec, &tm_now)) { goto error; } - // '2015-01-02T10:13:16.132222+00:00' - snprintf( - strbuf, bufsz, "%d-%02d-%02dT%02d:%02d:%02d.%jd%c%02ld:%02ld", - 1900 + tm_now.tm_year, 1 + tm_now.tm_mon, tm_now.tm_mday, - tm_now.tm_hour, tm_now.tm_min, tm_now.tm_sec, - (intmax_t)now.tv_usec, tm_now.tm_gmtoff >= 0 ? '+' : '-', // %+02ld doesn't work - LGTD_ABS(tm_now.tm_gmtoff / 60 / 60), tm_now.tm_gmtoff % (60 * 60) - ); + LGTD_TM_TO_ISOTIME(&tm_now, strbuf, bufsz, now.tv_usec); return; error: strbuf[0] = '\0'; @@ -110,6 +103,26 @@ } void +lgtd_print_duration(uint64_t secs, char *buf, int bufsz) +{ + assert(buf); + assert(bufsz > 0); + + int days = secs / (60 * 60 * 24); + int minutes = secs / 60; + int hours = minutes / 60; + hours = hours % 24; + minutes = minutes % 60; + + int i = 0; + if (days) { + int n = snprintf(buf, bufsz, "%d days ", days); + i = LGTD_MIN(i + n, bufsz); + } + snprintf(&buf[i], bufsz - i, "%02d:%02d", hours, minutes); +} + +void _lgtd_err(void (*errfn)(int, const char *, ...), int eval, const char *fmt, diff --git a/core/proto.c b/core/proto.c --- a/core/proto.c +++ b/core/proto.c @@ -196,6 +196,39 @@ } static const char *state_fmt = ("{" + "\"_lifx\":{" + "\"gateway\":{" + "\"url\":\"tcp://%s:[%hu]\"," + "\"latency\":%d" + "}," + "\"mcu\":{" + "\"firmware_built_at\":\"%s\"," + "\"firmware_installed_at\":\"%s\"," + "\"firmware_version\":%u," + "\"signal_strength\":%u," + "\"tx_bytes\":%u," + "\"rx_bytes\":%u," + "\"unknown\":%u" + "}," + "\"wifi\":{" + "\"firmware_built_at\":\"%s\"," + "\"firmware_installed_at\":\"%s\"," + "\"signal_strength\":%u," + "\"tx_bytes\":%u," + "\"rx_bytes\":%u," + "\"unknown\":%u" + "}," + "\"product_info\":{" + "\"vendor_id\":%#x," + "\"product_id\":%#x," + "\"version\":%u" + "}," + "\"runtime_info\":{" + "\"time\":\"%s\"," + "\"uptime\":%ju," + "\"downtime\":%ju" + "}" + "}," "\"hsbk\":[%s,%s,%s,%hu]," "\"power\":%s," "\"label\":\"%s\"," @@ -206,6 +239,21 @@ (src), (start), (stop), (dst), sizeof((dst)) \ ) +#define PRINT_LIFX_FW_TIMESTAMPS(fw_info, built_at_buf, installed_at_buf) \ + LGTD_LIFX_WIRE_PRINT_NSEC_TIMESTAMP((fw_info)->built_at, (built_at_buf)); \ + LGTD_LIFX_WIRE_PRINT_NSEC_TIMESTAMP( \ + (fw_info)->installed_at, (installed_at_buf) \ + ) + +#define LIFX_IP_STATUS_ARGS(b, ip) \ + ip##_fw_built_at, \ + ip##_fw_installed_at, \ + (b)->ip##_fw_info.version, \ + (b)->ip##_state.signal_strength, \ + (b)->ip##_state.tx_bytes, \ + (b)->ip##_state.rx_bytes, \ + (b)->ip##_state.reserved + lgtd_client_start_send_response(client); lgtd_client_write_string(client, "["); struct lgtd_router_device *device; @@ -217,9 +265,33 @@ PRINT_COMPONENT(bulb->state.saturation, s, 0, 1); PRINT_COMPONENT(bulb->state.brightness, b, 0, 1); - char buf[3072]; + char mcu_fw_built_at[64], mcu_fw_installed_at[64], bulb_time[64]; + LGTD_LIFX_WIRE_PRINT_NSEC_TIMESTAMP(bulb->runtime_info.time, bulb_time); + PRINT_LIFX_FW_TIMESTAMPS( + &bulb->mcu_fw_info, mcu_fw_built_at, mcu_fw_installed_at + ); + + char wifi_fw_built_at[64], wifi_fw_installed_at[64]; + PRINT_LIFX_FW_TIMESTAMPS( + &bulb->wifi_fw_info, wifi_fw_built_at, wifi_fw_installed_at + ); + + char buf[4096]; int written = snprintf( buf, sizeof(buf), state_fmt, + // _lifx.gateway: + bulb->gw->ip_addr, bulb->gw->port, + LGTD_LIFX_GATEWAY_LATENCY(bulb->gw), + // _lifx.{mcu,wifi}: + LIFX_IP_STATUS_ARGS(bulb, mcu), LIFX_IP_STATUS_ARGS(bulb, wifi), + // _lifx.product_info: + bulb->product_info.vendor_id, bulb->product_info.product_id, + bulb->product_info.version, + // _lifx.runtime_info: + bulb_time, + LGTD_NSECS_TO_SECS(bulb->runtime_info.uptime), + LGTD_NSECS_TO_SECS(bulb->runtime_info.downtime), + // bulb state: h, s, b, bulb->state.kelvin, bulb->state.power == LGTD_LIFX_POWER_ON ? "true" : "false", bulb->state.label[0] ? bulb->state.label : lgtd_addrtoa(bulb->addr) diff --git a/lifx/bulb.c b/lifx/bulb.c --- a/lifx/bulb.c +++ b/lifx/bulb.c @@ -154,3 +154,69 @@ bulb->state.tags = tags; } + +void +lgtd_lifx_bulb_set_mcu_state(struct lgtd_lifx_bulb *bulb, + const struct lgtd_lifx_ip_state *state, + lgtd_time_mono_t received_at) +{ + assert(bulb); + assert(state); + + bulb->last_mcu_state_at = received_at; + memcpy(&bulb->mcu_state, state, sizeof(bulb->mcu_state)); +} + +void +lgtd_lifx_bulb_set_mcu_firmware_info(struct lgtd_lifx_bulb *bulb, + const struct lgtd_lifx_ip_firmware_info *info) +{ + assert(bulb); + assert(info); + + memcpy(&bulb->mcu_fw_info, info, sizeof(bulb->mcu_fw_info)); +} + +void +lgtd_lifx_bulb_set_wifi_state(struct lgtd_lifx_bulb *bulb, + const struct lgtd_lifx_ip_state *state, + lgtd_time_mono_t received_at) +{ + assert(bulb); + assert(state); + + bulb->last_wifi_state_at = received_at; + memcpy(&bulb->wifi_state, state, sizeof(bulb->wifi_state)); +} + +void +lgtd_lifx_bulb_set_wifi_firmware_info(struct lgtd_lifx_bulb *bulb, + const struct lgtd_lifx_ip_firmware_info *info) +{ + assert(bulb); + assert(info); + + memcpy(&bulb->wifi_fw_info, info, sizeof(bulb->wifi_fw_info)); +} + +void +lgtd_lifx_bulb_set_product_info(struct lgtd_lifx_bulb *bulb, + const struct lgtd_lifx_product_info *info) +{ + assert(bulb); + assert(info); + + memcpy(&bulb->product_info, info, sizeof(bulb->product_info)); +} + +void +lgtd_lifx_bulb_set_runtime_info(struct lgtd_lifx_bulb *bulb, + const struct lgtd_lifx_runtime_info *info, + lgtd_time_mono_t received_at) +{ + assert(bulb); + assert(info); + + bulb->last_runtime_info_at = received_at; + memcpy(&bulb->runtime_info, info, sizeof(bulb->runtime_info)); +} diff --git a/lifx/bulb.h b/lifx/bulb.h --- a/lifx/bulb.h +++ b/lifx/bulb.h @@ -30,17 +30,51 @@ char label[LGTD_LIFX_LABEL_SIZE]; uint64_t tags; }; + +struct lgtd_lifx_ip_state { + float signal_strength; // mW + uint32_t tx_bytes; + uint32_t rx_bytes; + uint16_t reserved; // Temperature? +}; + +struct lgtd_lifx_ip_firmware_info { + uint64_t built_at; // ns since epoch + uint64_t installed_at; // ns since epoch + uint32_t version; +}; + +struct lgtd_lifx_product_info { + uint32_t vendor_id; + uint32_t product_id; + uint32_t version; +}; + +struct lgtd_lifx_runtime_info { + uint64_t time; // ns since epoch + uint64_t uptime; // ns + uint64_t downtime; // ns, last power off period duration +}; #pragma pack(pop) struct lgtd_lifx_bulb { - RB_ENTRY(lgtd_lifx_bulb) link; - SLIST_ENTRY(lgtd_lifx_bulb) link_by_gw; - struct lgtd_lifx_gateway *gw; - uint8_t addr[LGTD_LIFX_ADDR_LENGTH]; - struct lgtd_lifx_light_state state; - lgtd_time_mono_t last_light_state_at; - lgtd_time_mono_t dirty_at; - uint16_t expected_power_on; + RB_ENTRY(lgtd_lifx_bulb) link; + SLIST_ENTRY(lgtd_lifx_bulb) link_by_gw; + lgtd_time_mono_t last_light_state_at; + lgtd_time_mono_t last_mcu_state_at; + lgtd_time_mono_t last_wifi_state_at; + lgtd_time_mono_t last_runtime_info_at; + lgtd_time_mono_t dirty_at; + uint16_t expected_power_on; + uint8_t addr[LGTD_LIFX_ADDR_LENGTH]; + struct lgtd_lifx_gateway *gw; + struct lgtd_lifx_light_state state; + struct lgtd_lifx_ip_state mcu_state; + struct lgtd_lifx_ip_state wifi_state; + struct lgtd_lifx_ip_firmware_info mcu_fw_info; + struct lgtd_lifx_ip_firmware_info wifi_fw_info; + struct lgtd_lifx_product_info product_info; + struct lgtd_lifx_runtime_info runtime_info; }; RB_HEAD(lgtd_lifx_bulb_map, lgtd_lifx_bulb); SLIST_HEAD(lgtd_lifx_bulb_list, lgtd_lifx_bulb); @@ -69,3 +103,19 @@ lgtd_time_mono_t); void lgtd_lifx_bulb_set_power_state(struct lgtd_lifx_bulb *, uint16_t); void lgtd_lifx_bulb_set_tags(struct lgtd_lifx_bulb *, uint64_t); + +void lgtd_lifx_bulb_set_mcu_state(struct lgtd_lifx_bulb *, + const struct lgtd_lifx_ip_state *, + lgtd_time_mono_t); +void lgtd_lifx_bulb_set_mcu_firmware_info(struct lgtd_lifx_bulb *, + const struct lgtd_lifx_ip_firmware_info *); +void lgtd_lifx_bulb_set_wifi_state(struct lgtd_lifx_bulb *, + const struct lgtd_lifx_ip_state *, + lgtd_time_mono_t); +void lgtd_lifx_bulb_set_wifi_firmware_info(struct lgtd_lifx_bulb *, + const struct lgtd_lifx_ip_firmware_info *); +void lgtd_lifx_bulb_set_product_info(struct lgtd_lifx_bulb *, + const struct lgtd_lifx_product_info *); +void lgtd_lifx_bulb_set_runtime_info(struct lgtd_lifx_bulb *, + const struct lgtd_lifx_runtime_info *, + lgtd_time_mono_t); diff --git a/lifx/gateway.c b/lifx/gateway.c --- a/lifx/gateway.c +++ b/lifx/gateway.c @@ -489,12 +489,8 @@ (uintmax_t)pkt->tags ); - struct lgtd_lifx_bulb *b = lgtd_lifx_gateway_get_or_open_bulb( - gw, hdr->target.device_addr - ); - if (!b) { - return; - } + struct lgtd_lifx_bulb *b; + LGTD_LIFX_GATEWAY_GET_BULB_OR_RETURN(b, gw, hdr->target.device_addr); assert(sizeof(*pkt) == sizeof(b->state)); lgtd_lifx_bulb_set_light_state( @@ -519,7 +515,7 @@ } } - int latency = gw->last_pkt_at - gw->last_req_at; + int latency = LGTD_LIFX_GATEWAY_LATENCY(gw); if (latency < LGTD_LIFX_GATEWAY_MIN_REFRESH_INTERVAL_MSECS) { if (!event_pending(gw->refresh_ev, EV_TIMEOUT, NULL)) { int timeout = LGTD_LIFX_GATEWAY_MIN_REFRESH_INTERVAL_MSECS - latency; @@ -559,14 +555,9 @@ gw->ip_addr, gw->port, lgtd_addrtoa(hdr->target.device_addr), pkt->power ); - struct lgtd_lifx_bulb *b = lgtd_lifx_gateway_get_or_open_bulb( - gw, hdr->target.device_addr + LGTD_LIFX_GATEWAY_SET_BULB_ATTR( + gw, hdr->target.device_addr, lgtd_lifx_bulb_set_power_state, pkt->power ); - if (!b) { - return; - } - - lgtd_lifx_bulb_set_power_state(b, pkt->power); } int @@ -673,9 +664,10 @@ } } -void lgtd_lifx_gateway_handle_tags(struct lgtd_lifx_gateway *gw, - const struct lgtd_lifx_packet_header *hdr, - const struct lgtd_lifx_packet_tags *pkt) +void +lgtd_lifx_gateway_handle_tags(struct lgtd_lifx_gateway *gw, + const struct lgtd_lifx_packet_header *hdr, + const struct lgtd_lifx_packet_tags *pkt) { assert(gw && hdr && pkt); @@ -685,12 +677,8 @@ (uintmax_t)pkt->tags ); - struct lgtd_lifx_bulb *b = lgtd_lifx_gateway_get_or_open_bulb( - gw, hdr->target.device_addr - ); - if (!b) { - return; - } + struct lgtd_lifx_bulb *b; + LGTD_LIFX_GATEWAY_GET_BULB_OR_RETURN(b, gw, hdr->target.device_addr); int tag_id; LGTD_LIFX_WIRE_FOREACH_TAG_ID(tag_id, pkt->tags) { @@ -707,3 +695,142 @@ lgtd_lifx_bulb_set_tags(b, pkt->tags); } + +void +lgtd_lifx_gateway_handle_ip_state(struct lgtd_lifx_gateway *gw, + const struct lgtd_lifx_packet_header *hdr, + const struct lgtd_lifx_packet_ip_state *pkt) +{ + assert(gw && hdr && pkt); + + const char *type; + void (*bulb_fn)(struct lgtd_lifx_bulb *, + const struct lgtd_lifx_ip_state *, + lgtd_time_mono_t); + + switch (hdr->packet_type) { + case LGTD_LIFX_MESH_INFO: + type = "MCU_STATE"; + bulb_fn = lgtd_lifx_bulb_set_mcu_state; + break; + case LGTD_LIFX_WIFI_INFO: + type = "WIFI_STATE"; + bulb_fn = lgtd_lifx_bulb_set_wifi_state; + break; + default: + lgtd_info("invalid ip state packet_type %#hx", hdr->packet_type); +#ifndef NDEBUG + abort(); +#endif + return; + } + + lgtd_debug( + "%s <-- [%s]:%hu - %s " + "signal_strength=%f, rx_bytes=%u, tx_bytes=%u, reserved=%hu", + type, gw->ip_addr, gw->port, lgtd_addrtoa(hdr->target.device_addr), + pkt->signal_strength, pkt->rx_bytes, pkt->tx_bytes, pkt->reserved + ); + + LGTD_LIFX_GATEWAY_SET_BULB_ATTR_WITH_RECV_AT( + gw, + hdr->target.device_addr, + bulb_fn, + (const struct lgtd_lifx_ip_state *)pkt + ); +} + +void +lgtd_lifx_gateway_handle_ip_firmware_info(struct lgtd_lifx_gateway *gw, + const struct lgtd_lifx_packet_header *hdr, + const struct lgtd_lifx_packet_ip_firmware_info *pkt) +{ + assert(gw && hdr && pkt); + + const char *type; + void (*bulb_fn)(struct lgtd_lifx_bulb *, + const struct lgtd_lifx_ip_firmware_info *); + + switch (hdr->packet_type) { + case LGTD_LIFX_MESH_FIRMWARE: + type = "MCU_FIRMWARE_INFO"; + bulb_fn = lgtd_lifx_bulb_set_mcu_firmware_info; + break; + case LGTD_LIFX_WIFI_FIRMWARE_STATE: + type = "WIFI_FIRMWARE_INFO"; + bulb_fn = lgtd_lifx_bulb_set_wifi_firmware_info; + break; + default: + lgtd_info("invalid ip firmware packet_type %#hx", hdr->packet_type); +#ifndef NDEBUG + abort(); +#endif + return; + } + + char built_at[64], installed_at[64]; + LGTD_LIFX_WIRE_PRINT_NSEC_TIMESTAMP(pkt->built_at, built_at); + LGTD_LIFX_WIRE_PRINT_NSEC_TIMESTAMP(pkt->installed_at, installed_at); + + lgtd_debug( + "%s <-- [%s]:%hu - %s " + "built_at=%s, installed_at=%s, version=%u", + type, gw->ip_addr, gw->port, lgtd_addrtoa(hdr->target.device_addr), + built_at, installed_at, pkt->version + ); + + LGTD_LIFX_GATEWAY_SET_BULB_ATTR( + gw, + hdr->target.device_addr, + bulb_fn, + (const struct lgtd_lifx_ip_firmware_info *)pkt + ); +} + +void +lgtd_lifx_gateway_handle_product_info(struct lgtd_lifx_gateway *gw, + const struct lgtd_lifx_packet_header *hdr, + const struct lgtd_lifx_packet_product_info *pkt) +{ + assert(gw && hdr && pkt); + + lgtd_debug( + "PRODUCT_INFO <-- [%s]:%hu - %s " + "vendor_id=%#x, product_id=%#x, version=%u", + gw->ip_addr, gw->port, lgtd_addrtoa(hdr->target.device_addr), + pkt->vendor_id, pkt->product_id, pkt->version + ); + + LGTD_LIFX_GATEWAY_SET_BULB_ATTR( + gw, + hdr->target.device_addr, + lgtd_lifx_bulb_set_product_info, + (const struct lgtd_lifx_product_info *)pkt + ); +} + +void +lgtd_lifx_gateway_handle_runtime_info(struct lgtd_lifx_gateway *gw, + const struct lgtd_lifx_packet_header *hdr, + const struct lgtd_lifx_packet_runtime_info *pkt) +{ + assert(gw && hdr && pkt); + + char device_time[64], uptime[64], downtime[64]; + LGTD_LIFX_WIRE_PRINT_NSEC_TIMESTAMP(pkt->time, device_time); + LGTD_PRINT_DURATION(LGTD_NSECS_TO_SECS(pkt->uptime), uptime); + LGTD_PRINT_DURATION(LGTD_NSECS_TO_SECS(pkt->downtime), downtime); + + lgtd_debug( + "PRODUCT_INFO <-- [%s]:%hu - %s time=%s, uptime=%s, downtime=%s", + gw->ip_addr, gw->port, lgtd_addrtoa(hdr->target.device_addr), + device_time, uptime, downtime + ); + + LGTD_LIFX_GATEWAY_SET_BULB_ATTR_WITH_RECV_AT( + gw, + hdr->target.device_addr, + lgtd_lifx_bulb_set_runtime_info, + (const struct lgtd_lifx_runtime_info *)pkt + ); +} diff --git a/lifx/gateway.h b/lifx/gateway.h --- a/lifx/gateway.h +++ b/lifx/gateway.h @@ -36,6 +36,12 @@ struct lgtd_lifx_gateway { LIST_ENTRY(lgtd_lifx_gateway) link; struct lgtd_lifx_bulb_list bulbs; +#define LGTD_LIFX_GATEWAY_GET_BULB_OR_RETURN(b, gw, bulb_addr) do { \ + (b) = lgtd_lifx_gateway_get_or_open_bulb((gw), (bulb_addr)); \ + if (!(b)) { \ + return; \ + } \ +} while (0) // Multiple gateways can share the same site (that happens when bulbs are // far away enough that ZigBee can't be used). Moreover the SET_PAN_GATEWAY // packet doesn't include the device address in the header (i.e: site and @@ -60,6 +66,7 @@ lgtd_time_mono_t last_req_at; lgtd_time_mono_t next_req_at; lgtd_time_mono_t last_pkt_at; +#define LGTD_LIFX_GATEWAY_LATENCY(gw) ((gw)->last_pkt_at - (gw)->last_req_at) struct lgtd_lifx_message pkt_ring[LGTD_LIFX_GATEWAY_PACKET_RING_SIZE]; #define LGTD_LIFX_GATEWAY_INC_MESSAGE_RING_INDEX(idx) do { \ (idx) += 1; \ @@ -77,6 +84,18 @@ extern struct lgtd_lifx_gateway_list lgtd_lifx_gateways; +#define LGTD_LIFX_GATEWAY_SET_BULB_ATTR(gw, bulb_addr, bulb_fn, payload) do { \ + struct lgtd_lifx_bulb *b; \ + LGTD_LIFX_GATEWAY_GET_BULB_OR_RETURN(b, gw, bulb_addr); \ + (bulb_fn)(b, (payload)); \ +} while (0) + +#define LGTD_LIFX_GATEWAY_SET_BULB_ATTR_WITH_RECV_AT(gw, bulb_addr, bulb_fn, payload) do { \ + struct lgtd_lifx_bulb *b; \ + LGTD_LIFX_GATEWAY_GET_BULB_OR_RETURN(b, gw, bulb_addr); \ + (bulb_fn)(b, (payload), (gw)->last_pkt_at); \ +} while (0) + struct lgtd_lifx_gateway *lgtd_lifx_gateway_get(const struct sockaddr_storage *); struct lgtd_lifx_gateway *lgtd_lifx_gateway_open(const struct sockaddr_storage *, ev_socklen_t, @@ -119,3 +138,15 @@ void lgtd_lifx_gateway_handle_tags(struct lgtd_lifx_gateway *, const struct lgtd_lifx_packet_header *, const struct lgtd_lifx_packet_tags *); +void lgtd_lifx_gateway_handle_ip_state(struct lgtd_lifx_gateway *, + const struct lgtd_lifx_packet_header *, + const struct lgtd_lifx_packet_ip_state *); +void lgtd_lifx_gateway_handle_ip_firmware_info(struct lgtd_lifx_gateway *, + const struct lgtd_lifx_packet_header *, + const struct lgtd_lifx_packet_ip_firmware_info *); +void lgtd_lifx_gateway_handle_product_info(struct lgtd_lifx_gateway *, + const struct lgtd_lifx_packet_header *, + const struct lgtd_lifx_packet_product_info *); +void lgtd_lifx_gateway_handle_runtime_info(struct lgtd_lifx_gateway *, + const struct lgtd_lifx_packet_header *, + const struct lgtd_lifx_packet_runtime_info *); diff --git a/lifx/wire_proto.c b/lifx/wire_proto.c --- a/lifx/wire_proto.c +++ b/lifx/wire_proto.c @@ -24,8 +24,10 @@ #include <stdarg.h> #include <stdbool.h> #include <stdint.h> +#include <stdio.h> #include <stdlib.h> #include <string.h> +#include <time.h> #include <event2/util.h> @@ -74,12 +76,32 @@ (void)pkt; } +static void +lgtd_lifx_wire_enosys_packet_handler(struct lgtd_lifx_gateway *gw, + const struct lgtd_lifx_packet_header *hdr, + const void *pkt) +{ + (void)pkt; + + const struct lgtd_lifx_packet_info *pkt_info; + pkt_info = lgtd_lifx_wire_get_packet_info(hdr->packet_type); + bool addressable = hdr->protocol & LGTD_LIFX_PROTOCOL_ADDRESSABLE; + bool tagged = hdr->protocol & LGTD_LIFX_PROTOCOL_TAGGED; + unsigned int protocol = hdr->protocol & LGTD_LIFX_PROTOCOL_VERSION_MASK; + lgtd_info( + "%s <-- [%s]:%hu - (Unimplemented, header info: " + "addressable=%d, tagged=%d, protocol=%d)", + pkt_info->name, gw->ip_addr, gw->port, + addressable, tagged, protocol + ); +} + void lgtd_lifx_wire_load_packet_info_map(void) { #define DECODER(x) ((void (*)(void *))(x)) #define ENCODER(x) ((void (*)(void *))(x)) -#define HANDLER(x) \ +#define HANDLER(x) \ ((void (*)(struct lgtd_lifx_gateway *, \ const struct lgtd_lifx_packet_header *, \ const void *))(x)) @@ -90,6 +112,10 @@ #define REQUEST_ONLY \ .decode = lgtd_lifx_wire_null_packet_encoder_decoder, \ .handle = lgtd_lifx_wire_null_packet_handler +#define UNIMPLEMENTED \ + .decode = lgtd_lifx_wire_null_packet_encoder_decoder, \ + .encode = lgtd_lifx_wire_null_packet_encoder_decoder, \ + .handle = lgtd_lifx_wire_enosys_packet_handler static struct lgtd_lifx_packet_info packet_table[] = { // Gateway packets: @@ -187,6 +213,211 @@ .size = sizeof(struct lgtd_lifx_packet_tags), .decode = DECODER(lgtd_lifx_wire_decode_tags), .handle = HANDLER(lgtd_lifx_gateway_handle_tags) + }, + { + REQUEST_ONLY, + NO_PAYLOAD, + .name = "GET_MESH_INFO", + .type = LGTD_LIFX_GET_MESH_INFO + }, + { + RESPONSE_ONLY, + .name = "MESH_INFO", + .type = LGTD_LIFX_MESH_INFO, + .size = sizeof(struct lgtd_lifx_packet_ip_state), + .decode = DECODER(lgtd_lifx_wire_decode_ip_state), + .handle = HANDLER(lgtd_lifx_gateway_handle_ip_state) + }, + { + REQUEST_ONLY, + NO_PAYLOAD, + .name = "GET_MESH_FIRMWARE", + .type = LGTD_LIFX_GET_MESH_FIRMWARE + }, + { + RESPONSE_ONLY, + .name = "MESH_FIRMWARE", + .type = LGTD_LIFX_MESH_FIRMWARE, + .size = sizeof(struct lgtd_lifx_packet_ip_firmware_info), + .decode = DECODER(lgtd_lifx_wire_decode_ip_firmware_info), + .handle = HANDLER(lgtd_lifx_gateway_handle_ip_firmware_info) + }, + { + REQUEST_ONLY, + NO_PAYLOAD, + .name = "GET_WIFI_INFO", + .type = LGTD_LIFX_GET_WIFI_INFO, + }, + { + RESPONSE_ONLY, + .name = "WIFI_INFO", + .type = LGTD_LIFX_WIFI_INFO, + .size = sizeof(struct lgtd_lifx_packet_ip_state), + .decode = DECODER(lgtd_lifx_wire_decode_ip_state), + .handle = HANDLER(lgtd_lifx_gateway_handle_ip_state) + }, + { + REQUEST_ONLY, + NO_PAYLOAD, + .name = "GET_WIFI_FIRMWARE_STATE", + .type = LGTD_LIFX_GET_WIFI_FIRMWARE_STATE + }, + { + RESPONSE_ONLY, + .name = "WIFI_FIRMWARE_STATE", + .type = LGTD_LIFX_WIFI_FIRMWARE_STATE, + .size = sizeof(struct lgtd_lifx_packet_ip_firmware_info), + .decode = DECODER(lgtd_lifx_wire_decode_ip_firmware_info), + .handle = HANDLER(lgtd_lifx_gateway_handle_ip_firmware_info) + }, + { + REQUEST_ONLY, + NO_PAYLOAD, + .name = "GET_VERSION", + .type = LGTD_LIFX_GET_VERSION + }, + { + RESPONSE_ONLY, + .name = "VERSION_STATE", + .type = LGTD_LIFX_VERSION_STATE, + .size = sizeof(struct lgtd_lifx_packet_product_info), + .decode = DECODER(lgtd_lifx_wire_decode_product_info), + .handle = HANDLER(lgtd_lifx_gateway_handle_product_info) + }, + { + REQUEST_ONLY, + NO_PAYLOAD, + .name = "GET_INFO", + .type = LGTD_LIFX_GET_INFO + }, + { + RESPONSE_ONLY, + .name = "INFO_STATE", + .type = LGTD_LIFX_INFO_STATE, + .size = sizeof(struct lgtd_lifx_packet_runtime_info), + .decode = DECODER(lgtd_lifx_wire_decode_runtime_info), + .handle = HANDLER(lgtd_lifx_gateway_handle_runtime_info) + }, + // Unimplemented but "known" packets + { + UNIMPLEMENTED, + .name = "GET_TIME", + .type = LGTD_LIFX_GET_TIME + }, + { + UNIMPLEMENTED, + .name = "SET_TIME", + .type = LGTD_LIFX_SET_TIME + }, + { + UNIMPLEMENTED, + .name = "TIME_STATE", + .type = LGTD_LIFX_TIME_STATE + }, + { + UNIMPLEMENTED, + .name = "GET_RESET_SWITCH_STATE", + .type = LGTD_LIFX_GET_RESET_SWITCH_STATE + }, + { + UNIMPLEMENTED, + .name = "RESET_SWITCH_STATE", + .type = LGTD_LIFX_RESET_SWITCH_STATE + }, + { + UNIMPLEMENTED, + .name = "GET_BULB_LABEL", + .type = LGTD_LIFX_GET_BULB_LABEL + }, + { + UNIMPLEMENTED, + .name = "SET_BULB_LABEL", + .type = LGTD_LIFX_SET_BULB_LABEL + }, + { + UNIMPLEMENTED, + .name = "BULB_LABEL", + .type = LGTD_LIFX_BULB_LABEL + }, + { + UNIMPLEMENTED, + .name = "GET_MCU_RAIL_VOLTAGE", + .type = LGTD_LIFX_GET_MCU_RAIL_VOLTAGE + }, + { + UNIMPLEMENTED, + .name = "MCU_RAIL_VOLTAGE", + .type = LGTD_LIFX_MCU_RAIL_VOLTAGE + }, + { + UNIMPLEMENTED, + .name = "REBOOT", + .type = LGTD_LIFX_REBOOT + }, + { + UNIMPLEMENTED, + .name = "SET_FACTORY_TEST_MODE", + .type = LGTD_LIFX_SET_FACTORY_TEST_MODE + }, + { + UNIMPLEMENTED, + .name = "DISABLE_FACTORY_TEST_MODE", + .type = LGTD_LIFX_DISABLE_FACTORY_TEST_MODE + }, + { + UNIMPLEMENTED, + .name = "ACK", + .type = LGTD_LIFX_ACK + }, + { + UNIMPLEMENTED, + .name = "ECHO_REQUEST", + .type = LGTD_LIFX_ECHO_REQUEST + }, + { + UNIMPLEMENTED, + .name = "ECHO_RESPONSE", + .type = LGTD_LIFX_ECHO_RESPONSE + }, + { + UNIMPLEMENTED, + .name = "SET_DIM_ABSOLUTE", + .type = LGTD_LIFX_SET_DIM_ABSOLUTE + }, + { + UNIMPLEMENTED, + .name = "SET_DIM_RELATIVE", + .type = LGTD_LIFX_SET_DIM_RELATIVE + }, + { + UNIMPLEMENTED, + .name = "GET_WIFI_STATE", + .type = LGTD_LIFX_GET_WIFI_STATE + }, + { + UNIMPLEMENTED, + .name = "SET_WIFI_STATE", + .type = LGTD_LIFX_SET_WIFI_STATE + }, + { + UNIMPLEMENTED, + .name = "WIFI_STATE", + .type = LGTD_LIFX_WIFI_STATE + }, + { + UNIMPLEMENTED, + .name = "GET_ACCESS_POINTS", + .type = LGTD_LIFX_GET_ACCESS_POINTS + }, + { + UNIMPLEMENTED, + .name = "SET_ACCESS_POINTS", + .type = LGTD_LIFX_SET_ACCESS_POINTS + }, + { + UNIMPLEMENTED, + .name = "ACCESS_POINT", + .type = LGTD_LIFX_ACCESS_POINT } }; @@ -234,6 +465,24 @@ return LGTD_LIFX_WAVEFORM_INVALID; } +void +lgtd_lifx_wire_print_nsec_timestamp(uint64_t nsec_ts, char *buf, int bufsz) +{ + assert(buf); + assert(bufsz > 0); + + time_t ts = LGTD_NSECS_TO_SECS(nsec_ts); + + struct tm tm_utc; + if (gmtime_r(&ts, &tm_utc)) { + int64_t usecs = LGTD_NSECS_TO_USECS(nsec_ts - LGTD_SECS_TO_NSECS(ts)); + LGTD_TM_TO_ISOTIME(&tm_utc, buf, bufsz, usecs); + return; + } + + buf[0] = '\0'; +} + static void lgtd_lifx_wire_encode_header(struct lgtd_lifx_packet_header *hdr, int flags) { @@ -423,3 +672,44 @@ pkt->tags = le64toh(pkt->tags); } + +void +lgtd_lifx_wire_decode_ip_state(struct lgtd_lifx_packet_ip_state *pkt) +{ + assert(pkt); + + pkt->signal_strength = lgtd_lifx_wire_lefloattoh(pkt->signal_strength); + pkt->tx_bytes = le32toh(pkt->tx_bytes); + pkt->rx_bytes = le32toh(pkt->rx_bytes); + pkt->reserved = le16toh(pkt->reserved); +} + +void +lgtd_lifx_wire_decode_ip_firmware_info(struct lgtd_lifx_packet_ip_firmware_info *pkt) +{ + assert(pkt); + + pkt->built_at = le64toh(pkt->built_at); + pkt->installed_at = le64toh(pkt->installed_at); + pkt->version = le32toh(pkt->version); +} + +void +lgtd_lifx_wire_decode_product_info(struct lgtd_lifx_packet_product_info *pkt) +{ + assert(pkt); + + pkt->vendor_id = le32toh(pkt->vendor_id); + pkt->product_id = le32toh(pkt->product_id); + pkt->version = le32toh(pkt->version); +} + +void +lgtd_lifx_wire_decode_runtime_info(struct lgtd_lifx_packet_runtime_info *pkt) +{ + assert(pkt); + + pkt->time = le64toh(pkt->time); + pkt->uptime = le64toh(pkt->uptime); + pkt->downtime = le64toh(pkt->downtime); +} diff --git a/lifx/wire_proto.h b/lifx/wire_proto.h --- a/lifx/wire_proto.h +++ b/lifx/wire_proto.h @@ -28,7 +28,7 @@ static inline floatle_t lgtd_lifx_wire_htolefloat(float f) { - union { float f; uint32_t i; } u = { .f = f }; + union { float f; uint32_t i; } u = { .f = f }; htole32(u.i); return u.f; } @@ -36,7 +36,7 @@ static inline floatle_t lgtd_lifx_wire_lefloattoh(float f) { - union { float f; uint32_t i; } u = { .f = f }; + union { float f; uint32_t i; } u = { .f = f }; le32toh(u.i); return u.f; } @@ -249,6 +249,30 @@ char label[LGTD_LIFX_LABEL_SIZE]; }; +struct lgtd_lifx_packet_ip_state { + floatle_t signal_strength; + uint32le_t tx_bytes; + uint32le_t rx_bytes; + uint16le_t reserved; +}; + +struct lgtd_lifx_packet_ip_firmware_info { + uint64le_t built_at; + uint64le_t installed_at; + uint32le_t version; +}; + +struct lgtd_lifx_packet_product_info { + uint32le_t vendor_id; + uint32le_t product_id; + uint32le_t version; +}; + +struct lgtd_lifx_packet_runtime_info { + uint64le_t time; + uint64le_t uptime; + uint64le_t downtime; +}; #pragma pack(pop) enum lgtd_lifx_header_flags { @@ -330,8 +354,11 @@ (tag_id_varname) != -1; \ (tag_id_varname) = lgtd_lifx_wire_next_tag_id((tag_id_varname), (tags))) - enum lgtd_lifx_waveform_type lgtd_lifx_wire_waveform_string_id_to_type(const char *, int); +void lgtd_lifx_wire_print_nsec_timestamp(uint64_t, char *, int); +#define LGTD_LIFX_WIRE_PRINT_NSEC_TIMESTAMP(ts, arr) do { \ + lgtd_lifx_wire_print_nsec_timestamp((ts), (arr), sizeof((arr))); \ +} while (0) const struct lgtd_lifx_packet_info *lgtd_lifx_wire_get_packet_info(enum lgtd_lifx_packet_type); void lgtd_lifx_wire_load_packet_info_map(void); @@ -356,3 +383,8 @@ void lgtd_lifx_wire_decode_tags(struct lgtd_lifx_packet_tags *); void lgtd_lifx_wire_encode_tag_labels(struct lgtd_lifx_packet_tag_labels *); void lgtd_lifx_wire_decode_tag_labels(struct lgtd_lifx_packet_tag_labels *); + +void lgtd_lifx_wire_decode_ip_state(struct lgtd_lifx_packet_ip_state *); +void lgtd_lifx_wire_decode_ip_firmware_info(struct lgtd_lifx_packet_ip_firmware_info *); +void lgtd_lifx_wire_decode_product_info(struct lgtd_lifx_packet_product_info *); +void lgtd_lifx_wire_decode_runtime_info(struct lgtd_lifx_packet_runtime_info *); diff --git a/tests/lifx/mock_gateway.h b/tests/lifx/mock_gateway.h --- a/tests/lifx/mock_gateway.h +++ b/tests/lifx/mock_gateway.h @@ -129,3 +129,51 @@ (void)pkt_tags; } #endif + +#ifndef LGTD_LIFX_GATEWAY_HANDLE_IP_STATE +void +lgtd_lifx_gateway_handle_ip_state(struct lgtd_lifx_gateway *gw, + const struct lgtd_lifx_packet_header *hdr, + const struct lgtd_lifx_packet_ip_state *pkt) +{ + (void)gw; + (void)hdr; + (void)pkt; +} +#endif + +#ifndef LGTD_LIFX_GATEWAY_HANDLE_IP_FIRMWARE_INFO +void +lgtd_lifx_gateway_handle_ip_firmware_info(struct lgtd_lifx_gateway *gw, + const struct lgtd_lifx_packet_header *hdr, + const struct lgtd_lifx_packet_ip_firmware_info *pkt) +{ + (void)gw; + (void)hdr; + (void)pkt; +} +#endif + +#ifndef LGTD_LIFX_GATEWAY_HANDLE_PRODUCT_INFO +void +lgtd_lifx_gateway_handle_product_info(struct lgtd_lifx_gateway *gw, + const struct lgtd_lifx_packet_header *hdr, + const struct lgtd_lifx_packet_product_info *pkt) +{ + (void)gw; + (void)hdr; + (void)pkt; +} +#endif + +#ifndef LGTD_LIFX_GATEWAY_HANDLE_RUNTIME_INFO +void +lgtd_lifx_gateway_handle_runtime_info(struct lgtd_lifx_gateway *gw, + const struct lgtd_lifx_packet_header *hdr, + const struct lgtd_lifx_packet_runtime_info *pkt) +{ + (void)gw; + (void)hdr; + (void)pkt; +} +#endif diff --git a/tests/lifx/wire_proto/test_wire_proto_encode_decode_header.c b/tests/lifx/wire_proto/test_wire_proto_encode_decode_header.c --- a/tests/lifx/wire_proto/test_wire_proto_encode_decode_header.c +++ b/tests/lifx/wire_proto/test_wire_proto_encode_decode_header.c @@ -2,7 +2,7 @@ #include "wire_proto.c" -#include "test_wire_proto_utils.h" +#include "mock_gateway.h" int main(void) diff --git a/tests/lifx/wire_proto/test_wire_proto_utils.h b/tests/lifx/wire_proto/test_wire_proto_utils.h deleted file mode 100644 --- a/tests/lifx/wire_proto/test_wire_proto_utils.h +++ /dev/null @@ -1,46 +0,0 @@ -#pragma once - -void lgtd_lifx_gateway_handle_pan_gateway(struct lgtd_lifx_gateway *gw, - const struct lgtd_lifx_packet_header *hdr, - const struct lgtd_lifx_packet_pan_gateway *pkt) -{ - (void)gw; - (void)hdr; - (void)pkt; -} - -void lgtd_lifx_gateway_handle_light_status(struct lgtd_lifx_gateway *gw, - const struct lgtd_lifx_packet_header *hdr, - const struct lgtd_lifx_packet_light_status *pkt) -{ - (void)gw; - (void)hdr; - (void)pkt; -} - -void lgtd_lifx_gateway_handle_power_state(struct lgtd_lifx_gateway *gw, - const struct lgtd_lifx_packet_header *hdr, - const struct lgtd_lifx_packet_power_state *pkt) -{ - (void)gw; - (void)hdr; - (void)pkt; -} - -void lgtd_lifx_gateway_handle_tag_labels(struct lgtd_lifx_gateway *gw, - const struct lgtd_lifx_packet_header *hdr, - const struct lgtd_lifx_packet_tag_labels *pkt) -{ - (void)gw; - (void)hdr; - (void)pkt; -} - -void lgtd_lifx_gateway_handle_tags(struct lgtd_lifx_gateway *gw, - const struct lgtd_lifx_packet_header *hdr, - const struct lgtd_lifx_packet_tags *pkt) -{ - (void)gw; - (void)hdr; - (void)pkt; -} diff --git a/tests/lifx/wire_proto/test_wire_proto_waveform_table.c b/tests/lifx/wire_proto/test_wire_proto_waveform_table.c --- a/tests/lifx/wire_proto/test_wire_proto_waveform_table.c +++ b/tests/lifx/wire_proto/test_wire_proto_waveform_table.c @@ -1,6 +1,6 @@ #include "wire_proto.c" -#include "test_wire_proto_utils.h" +#include "mock_gateway.h" int main(void)