2017-12-15 06:45:57

by Steve Brown

[permalink] [raw]
Subject: [PATCH V3 0/9] mesh: Add configuration commands to meshctl

From: Steve Brown <[email protected]>

This complements the Zephyr application located in the Zephyr tree at
zephyr/samples/boards/nrf52/mesh/onoff-app

V2: Break each command into a separate commit
V3: Correct CID usage in pub-set and pub-get

Steve Brown (9):
mesh: meshctl: Change command names to <cmd>-<get/set>
mesh: meshctl: Add add/get subscribe
mesh: meshctl: Add set heartbeat publish
mesh: meshctl: Add get app keys
mesh: meshctl: Add get publish
mesh: meshctl: Add set/get proxy
mesh: meshctl: Add get/set identity
mesh: meshctl: Add get/set relay
mesh: meshctl: Add Company ID parameter to pub-set and pub-get

mesh/config-client.c | 455 ++++++++++++++++++++++++++++++++++++++++++++++++---
mesh/net.c | 20 ++-
2 files changed, 449 insertions(+), 26 deletions(-)

--
2.11.0



2017-12-17 08:47:44

by Steve Brown

[permalink] [raw]
Subject: Re: [PATCH V3 3/9] mesh: meshctl: Add set heartbeat publish

On Sun, 2017-12-17 at 06:18 +0000, Stotland, Inga wrote:
> Hi Steve,
>
> On Fri, 2017-12-15 at 06:46 +0000, [email protected] wrote:
> > From: Steve Brown <[email protected]>
> >
> > Sets heartbeat for node 0100
> >
> > [config: Target = 0100]# hb-set 0100 0 0 0 0
> >
> > Set heartbeat for node 0100 status: Success
> > Destination: 0100
> > Count: 00
> > Period: 00
> > TTL: ff
> > Features: 0000
> > Net_Idx: 0000
> > ---
> >
> After applying this patch, the code fails to compile because of
> undeclared functions for get/set proxy, get/set relay, get/set ident
> and get pub.
> I believe the expectation is that the code should cleanly compile
> after
> applying each patch. Plus, you would want to keep the new added
> fuctionalities bundled together, i.e., introduce new command menu
> items
> along with the command handling (e.g., "proxy-set" & "proxy-get"
> belong
> in
> "[PATCH Vxxx 6/9] mesh: meshctl: Add set/get proxy"
>
> Regards,
>
> Inga
I messed up my tree while changing the heartbeat command. I'll sort it
out and send a V4.

Sorry for the noise.

Steve

2017-12-17 06:18:58

by Stotland, Inga

[permalink] [raw]
Subject: Re: [PATCH V3 3/9] mesh: meshctl: Add set heartbeat publish

Hi Steve,

On Fri, 2017-12-15 at 06:46 +0000, [email protected] wrote:
> From: Steve Brown <[email protected]>
>
> Sets heartbeat for node 0100
>
> [config: Target = 0100]# hb-set 0100 0 0 0 0
>
> Set heartbeat for node 0100 status: Success
> Destination: 0100
> Count: 00
> Period: 00
> TTL: ff
> Features: 0000
> Net_Idx: 0000
> ---
> mesh/config-client.c | 75
> ++++++++++++++++++++++++++++++++++++++++++++++++++++
> mesh/net.c | 20 +++++++++++++-
> 2 files changed, 94 insertions(+), 1 deletion(-)
>
> diff --git a/mesh/config-client.c b/mesh/config-client.c
> index aa7414cc3..270a8ca36 100644
> --- a/mesh/config-client.c
> +++ b/mesh/config-client.c
> @@ -258,6 +258,24 @@ static bool client_msg_recvd(uint16_t src,
> uint8_t *data,
> bt_shell_printf("Subscr Addr:\t%4.4x\n",
> get_le16(data + i));
> break;
> +
> + /* Per Mesh Profile 4.3.2.63 */
> + case OP_CONFIG_HEARTBEAT_PUB_STATUS:
> + bt_shell_printf("\nSet heartbeat for node %4.4x
> status: %s\n",
> + src,
> + data[0] == MESH_STATUS_SUCCESS ?
> "Success" :
> + mesh_status_str(data
> [0]));
> +
> + if (data[0] != MESH_STATUS_SUCCESS)
> + return true;
> +
> + bt_shell_printf("Destination:\t%4.4x\n",
> get_le16(data + 1));
> + bt_shell_printf("Count:\t\t%2.2x\n", data[3]);
> + bt_shell_printf("Period:\t\t%2.2x\n", data[4]);
> + bt_shell_printf("TTL:\t\t%2.2x\n", data[5]);
> + bt_shell_printf("Features:\t%4.4x\n", get_le16(data
> + 6));
> + bt_shell_printf("Net_Idx:\t%4.4x\n", get_le16(data +
> 8));
> + break;
> }
>
> return true;
> @@ -712,6 +730,46 @@ static void cmd_sub_get(int argc, char *argv[])
> bt_shell_printf("Failed to send \"GET SUB GET\"\n");
> }
>
> +static void cmd_set_hb(int argc, char *argv[])
> +{
> + uint16_t n;
> + uint8_t msg[32];
> + int parm_cnt;
> +
> + if (IS_UNASSIGNED(target)) {
> + bt_shell_printf("Destination not set\n");
> + return;
> + }
> +
> + n = mesh_opcode_set(OP_CONFIG_HEARTBEAT_PUB_SET, msg);
> +
> + parm_cnt = read_input_parameters(argc, argv);
> + if (parm_cnt != 5) {
> + bt_shell_printf("Bad arguments: %s\n", argv[1]);
> + return;
> + }
> +
> + /* Per Mesh Profile 4.3.2.62 */
> + /* Publish address */
> + put_le16(parms[0], msg + n);
> + n += 2;
> + /* Count Log */
> + msg[n++] = parms[1];
> + /* Period Log */
> + msg[n++] = parms[2];
> + /* Heartbeat TTL */
> + msg[n++] = DEFAULT_TTL;
> + /* Features */
> + put_le16(parms[3], msg + n);
> + n += 2;
> + /* NetKey Index */
> + put_le16(parms[4], msg + n);
> + n += 2;
> +
> + if (!config_send(msg, n))
> + bt_shell_printf("Failed to send \"SET HEARTBEAT
> PUBLICATION\"\n");
> +}
> +
> static void cmd_get_ttl(int argc, char *argv[])
> {
> cmd_default(OP_CONFIG_DEFAULT_TTL_GET);
> @@ -741,6 +799,23 @@ static const struct bt_shell_menu cfg_menu = {
> {"pub-set", "<ele_addr> <pub_addr> <app_idx> "
> "<period (step|res)> <re-xmt (count|per)>
> <model>",
> cmd_set_pub, "Set
> publication"},
> + {"proxy-set", "<proxy>", cmd_s
> et_proxy,
> + "Set proxy state"},
> + {"proxy-get", NULL, cm
> d_get_proxy,
> + "Get proxy state"},
> + {"ident-set", "<net_idx> <state>", cmd
> _set_ident,
> + "Set node identity
> state"},
> + {"ident-get", "<net_idx>", cmd
> _get_ident,
> + "Get node identity
> state"},
> + {"relay-set", "<relay> <rexmt count> <rexmt
> steps>",
> + cmd_set_rela
> y,
> + "Set relay"},
> + {"relay-get", NULL, cm
> d_get_relay,
> + "Get relay"},
> + {"pub-get", "<ele_addr> <model>", cmd_get_pub
> ,
> + "Get publication"},
> + {"hb-pub-set", "<pub_addr> <count> <period> <features>
> <net_idx>",
> + cmd_set_hb, "Set heartbeati
> publish"},
> {"sub-add", "<ele_addr> <sub_addr> <model id>",
> cmd_sub_add, "Subscription add"},
> {"sub-get", "<ele_addr> <model id>",
> diff --git a/mesh/net.c b/mesh/net.c
> index 421dc6955..20dfcb8a8 100644
> --- a/mesh/net.c
> +++ b/mesh/net.c
> @@ -1399,6 +1399,24 @@ static bool ctl_rxed(uint16_t net_idx,
> uint32_t iv_index,
> uint8_t *trans, uint16_t len)
> {
> /* TODO: Handle control messages */
> +
> + /* Per Mesh Profile 3.6.5.10 */
> + if (trans[0] == NET_OP_HEARTBEAT) {
> + uint16_t feat = get_be16(trans + 2);
> +
> + bt_shell_printf("HEARTBEAT src: %4.4x dst: %4.4x \
> + TTL: %2.2x feat: %s%s%s%s\n",
> + src, dst, trans[1],
> + (feat & MESH_FEATURE_RELAY) ? "relay
> " : "",
> + (feat & MESH_FEATURE_PROXY) ? "proxy
> " : "",
> + (feat & MESH_FEATURE_FRIEND) ?
> "friend " : "",
> + (feat & MESH_FEATURE_LPN) ? "lpn" :
> "");
> + return true;
> + }
> +
> + bt_shell_printf("unrecognized control message src:%4.4x
> dst:%4.4x len:%d\n",
> + src, dst, len);
> + print_byte_array("msg: ", trans, len);
> return false;
> }
>
> @@ -2098,7 +2116,7 @@ bool net_access_layer_send(uint8_t ttl,
> uint16_t src, uint32_t dst,
> if (!result)
> return false;
>
> - segN = SEG_MAX(len + sizeof(uint32_t));
> + segN = SEG_MAX(len + sizeof(mic32));
>
> /* Only one ACK required SAR message per destination at a
> time */
> if (segN && IS_UNICAST(dst)) {

After applying this patch, the code fails to compile because of
undeclared functions for get/set proxy, get/set relay, get/set ident
and get pub.
I believe the expectation is that the code should cleanly compile after
applying each patch. Plus, you would want to keep the new added
fuctionalities bundled together, i.e., introduce new command menu items
along with the command handling (e.g., "proxy-set" & "proxy-get" belong
in
"[PATCH Vxxx 6/9] mesh: meshctl: Add set/get proxy"

Regards,

Inga


Attachments:
smime.p7s (3.19 kB)

2017-12-16 16:43:54

by Steve Brown

[permalink] [raw]
Subject: Re: [PATCH V3 7/9] mesh: meshctl: Add get/set identity

Hi Johan,

On Sat, 2017-12-16 at 16:49 +0100, Johan Hedberg wrote:
> Hi Steve,
>
> On Fri, Dec 15, 2017, [email protected] wrote:
> > + case OP_NODE_IDENTITY_STATUS:
> > + if (len != 4)
> > + return true;
> > + bt_shell_printf("Network index 0x%04x has "
> > + "Node Identity state 0x%02x %s\n",
> > + get_le16(data + 1), data[3],
> > + mesh_status_str(data[0]));
> > +
> > case OP_CONFIG_PROXY_STATUS:
>
> This throws the following compilation error for me:
>
> mesh/config-client.c: In function ‘client_msg_recvd’:
> mesh/config-client.c:165:3: error: this statement may fall through [-
> Werror=implicit-fallthrough=]
> bt_shell_printf("Network index 0x%04x has "
> ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> "Node Identity state 0x%02x %s\n",
> ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> get_le16(data + 1), data[3],
> ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> mesh_status_str(data[0]));
> ~~~~~~~~~~~~~~~~~~~~~~~~~
> mesh/config-client.c:170:2: note: here
> case OP_CONFIG_RELAY_STATUS:
> ^~~~
> cc1: all warnings being treated as errors
>
> The above looks like a valid error, i.e. it seems there's a missing
> break or return statement in your patch.
>
> You might want to upgrade to a newer gcc version if you're not
> getting
> these errors. At least version 7.2.1 from Fedora 27 give me these.
>
> Johan

Spot on. An out of date compiler.

I'll send a V4 after I get Inga's comments.

Thanks,

Steve


2017-12-16 15:49:32

by Johan Hedberg

[permalink] [raw]
Subject: Re: [PATCH V3 7/9] mesh: meshctl: Add get/set identity

Hi Steve,

On Fri, Dec 15, 2017, [email protected] wrote:
> + case OP_NODE_IDENTITY_STATUS:
> + if (len != 4)
> + return true;
> + bt_shell_printf("Network index 0x%04x has "
> + "Node Identity state 0x%02x %s\n",
> + get_le16(data + 1), data[3],
> + mesh_status_str(data[0]));
> +
> case OP_CONFIG_PROXY_STATUS:

This throws the following compilation error for me:

mesh/config-client.c: In function ‘client_msg_recvd’:
mesh/config-client.c:165:3: error: this statement may fall through [-Werror=implicit-fallthrough=]
bt_shell_printf("Network index 0x%04x has "
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
"Node Identity state 0x%02x %s\n",
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
get_le16(data + 1), data[3],
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
mesh_status_str(data[0]));
~~~~~~~~~~~~~~~~~~~~~~~~~
mesh/config-client.c:170:2: note: here
case OP_CONFIG_RELAY_STATUS:
^~~~
cc1: all warnings being treated as errors

The above looks like a valid error, i.e. it seems there's a missing
break or return statement in your patch.

You might want to upgrade to a newer gcc version if you're not getting
these errors. At least version 7.2.1 from Fedora 27 give me these.

Johan

2017-12-15 06:46:06

by Steve Brown

[permalink] [raw]
Subject: [PATCH V3 9/9] mesh: meshctl: Add Company ID parameter to pub-set and pub-get

From: Steve Brown <[email protected]>

Add a CID parameter to both commands similar to bind.
Correct the prior assumption that a model id > 0xffff
was a vendor model.

pub-set 0100 c000 1 0 0 1000
03 0001 00c0 01 00 ff 00 00 0010

pub-set 0100 c000 1 0 0 1000 1
03 0001 00c0 01 00 ff 00 00 0100 0010
---
mesh/config-client.c | 21 +++++++++++----------
1 file changed, 11 insertions(+), 10 deletions(-)

diff --git a/mesh/config-client.c b/mesh/config-client.c
index 0118b1853..0f197e322 100644
--- a/mesh/config-client.c
+++ b/mesh/config-client.c
@@ -780,7 +780,7 @@ static void cmd_set_pub(int argc, char *argv[])
n = mesh_opcode_set(OP_CONFIG_MODEL_PUB_SET, msg);

parm_cnt = read_input_parameters(argc, argv);
- if (parm_cnt != 6) {
+ if (parm_cnt != 6 && parm_cnt != 7) {
bt_shell_printf("Bad arguments\n");
return;
}
@@ -800,8 +800,8 @@ static void cmd_set_pub(int argc, char *argv[])
/* Publish retransmit count & interval steps */
msg[n++] = parms[4];
/* Model Id */
- if (parms[5] > 0xffff) {
- put_le16(parms[5] >> 16, msg + n);
+ if (parm_cnt == 7) {
+ put_le16(parms[6], msg + n);
put_le16(parms[5], msg + n + 2);
n += 4;
} else {
@@ -827,7 +827,7 @@ static void cmd_get_pub(int argc, char *argv[])
n = mesh_opcode_set(OP_CONFIG_MODEL_PUB_GET, msg);

parm_cnt = read_input_parameters(argc, argv);
- if (parm_cnt != 2) {
+ if (parm_cnt != 2 && parm_cnt != 3) {
bt_shell_printf("Bad arguments: %s\n", argv[1]);
return;
}
@@ -836,8 +836,8 @@ static void cmd_get_pub(int argc, char *argv[])
put_le16(parms[0], msg + n);
n += 2;
/* Model Id */
- if (parms[1] > 0xffff) {
- put_le16(parms[1] >> 16, msg + n);
+ if (parm_cnt == 3) {
+ put_le16(parms[2], msg + n);
put_le16(parms[1], msg + n + 2);
n += 4;
} else {
@@ -1012,8 +1012,11 @@ static const struct bt_shell_menu cfg_menu = {
{"ttl-get", NULL, cmd_get_ttl,
"Get default TTL"},
{"pub-set", "<ele_addr> <pub_addr> <app_idx> "
- "<period (step|res)> <re-xmt (count|per)> <model>",
- cmd_set_pub, "Set publication"},
+ "<per (step|res)> <re-xmt (cnt|per)> <mod id> "
+ "[cid]",
+ cmd_set_pub, "\n\t\t\t\t\t\t Set publication"},
+ {"pub-get", "<ele_addr> <mod id> [cid]", cmd_get_pub,
+ "Get publication"},
{"proxy-set", "<proxy>", cmd_set_proxy,
"Set proxy state"},
{"proxy-get", NULL, cmd_get_proxy,
@@ -1027,8 +1030,6 @@ static const struct bt_shell_menu cfg_menu = {
"Set relay"},
{"relay-get", NULL, cmd_get_relay,
"Get relay"},
- {"pub-get", "<ele_addr> <model>", cmd_get_pub,
- "Get publication"},
{"hb-pub-set", "<pub_addr> <count> <period> <features> <net_idx>",
cmd_set_hb, "Set heartbeati publish"},
{"sub-add", "<ele_addr> <sub_addr> <model id>",
--
2.11.0


2017-12-15 06:46:05

by Steve Brown

[permalink] [raw]
Subject: [PATCH V3 8/9] mesh: meshctl: Add get/set relay

From: Steve Brown <[email protected]>

Sets the relay state in node 0100 to 1

[config: Target = 0100]# relay-set 1 0 0

Node 0100 Relay state: 0x01 count: 0 steps: 0

Fetches the relay state of node 0100

[config: Target = 0100]# relay-get

Node 0100 Relay state: 0x01 count: 0 steps: 0
---
mesh/config-client.c | 37 +++++++++++++++++++++++++++++++++++++
1 file changed, 37 insertions(+)

diff --git a/mesh/config-client.c b/mesh/config-client.c
index f4012ad02..0118b1853 100644
--- a/mesh/config-client.c
+++ b/mesh/config-client.c
@@ -167,6 +167,14 @@ static bool client_msg_recvd(uint16_t src, uint8_t *data,
get_le16(data + 1), data[3],
mesh_status_str(data[0]));

+ case OP_CONFIG_RELAY_STATUS:
+ if (len != 2)
+ return true;
+ bt_shell_printf("Node %4.4x Relay state: 0x%02x"
+ " count: %d steps: %d\n",
+ src, data[0], data[1]>>5, data[1] & 0x1f);
+ break;
+
case OP_CONFIG_PROXY_STATUS:
if (len != 1)
return true;
@@ -705,6 +713,35 @@ static void cmd_get_proxy(int argc, char *argv[])
cmd_default(OP_CONFIG_PROXY_GET);
}

+static void cmd_set_relay(int argc, char *argv[])
+{
+ uint16_t n;
+ uint8_t msg[2 + 2 + 4];
+ int parm_cnt;
+
+ if (!verify_config_target(target))
+ return;
+
+ n = mesh_opcode_set(OP_CONFIG_RELAY_SET, msg);
+
+ parm_cnt = read_input_parameters(argc, argv);
+ if (parm_cnt != 3) {
+ bt_shell_printf("bad arguments\n");
+ return;
+ }
+
+ msg[n++] = parms[0];
+ msg[n++] = (parms[1] << 5) | parms[2];
+
+ if (!config_send(msg, n))
+ bt_shell_printf("Failed to send \"SET RELAY\"\n");
+}
+
+static void cmd_get_relay(int argc, char *argv[])
+{
+ cmd_default(OP_CONFIG_RELAY_GET);
+}
+
static void cmd_set_ttl(int argc, char *argv[])
{
uint16_t n;
--
2.11.0


2017-12-15 06:46:04

by Steve Brown

[permalink] [raw]
Subject: [PATCH V3 7/9] mesh: meshctl: Add get/set identity

From: Steve Brown <[email protected]>

Setting identity will cause connectable identity
beacons to be transmitted. The connect command
has an option to connect in this mode.

Sets the identity state to 1 in node 0100 on network
0000.

[config: Target = 0100]# ident-set 0 1
Network index 0x0000 has Node Identity state 0x01 Success

Fetched the identity state from node 0100

[config: Target = 0100]# ident-get 0
Network index 0x0000 has Node Identity state 0x01 Success
---
mesh/config-client.c | 57 ++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 57 insertions(+)

diff --git a/mesh/config-client.c b/mesh/config-client.c
index 2d132dbef..f4012ad02 100644
--- a/mesh/config-client.c
+++ b/mesh/config-client.c
@@ -159,6 +159,14 @@ static bool client_msg_recvd(uint16_t src, uint8_t *data,
prov_db_add_binding(node, addr - src, mod_id, app_idx);
break;

+ case OP_NODE_IDENTITY_STATUS:
+ if (len != 4)
+ return true;
+ bt_shell_printf("Network index 0x%04x has "
+ "Node Identity state 0x%02x %s\n",
+ get_le16(data + 1), data[3],
+ mesh_status_str(data[0]));
+
case OP_CONFIG_PROXY_STATUS:
if (len != 1)
return true;
@@ -619,6 +627,55 @@ static void cmd_bind(int argc, char *argv[])
bt_shell_printf("Failed to send \"MODEL APP BIND\"\n");
}

+static void cmd_set_ident(int argc, char *argv[])
+{
+ uint16_t n;
+ uint8_t msg[2 + 3 + 4];
+ int parm_cnt;
+
+ if (!verify_config_target(target))
+ return;
+
+ n = mesh_opcode_set(OP_NODE_IDENTITY_SET, msg);
+
+ parm_cnt = read_input_parameters(argc, argv);
+ if (parm_cnt != 2) {
+ bt_shell_printf("bad arguments\n");
+ return;
+ }
+
+ put_le16(parms[0], msg + n);
+ n += 2;
+ msg[n++] = parms[1];
+
+ if (!config_send(msg, n))
+ bt_shell_printf("Failed to send \"SET IDENTITY\"\n");
+}
+
+static void cmd_get_ident(int argc, char *argv[])
+{
+ uint16_t n;
+ uint8_t msg[2 + 2 + 4];
+ int parm_cnt;
+
+ if (!verify_config_target(target))
+ return;
+
+ n = mesh_opcode_set(OP_NODE_IDENTITY_GET, msg);
+
+ parm_cnt = read_input_parameters(argc, argv);
+ if (parm_cnt != 1) {
+ bt_shell_printf("bad arguments\n");
+ return;
+ }
+
+ put_le16(parms[0], msg + n);
+ n += 2;
+
+ if (!config_send(msg, n))
+ bt_shell_printf("Failed to send \"GET IDENTITY\"\n");
+}
+
static void cmd_set_proxy(int argc, char *argv[])
{
uint16_t n;
--
2.11.0


2017-12-15 06:46:02

by Steve Brown

[permalink] [raw]
Subject: [PATCH V3 5/9] mesh: meshctl: Add get publish

From: Steve Brown <[email protected]>

Get the publish address for model 1001 in element 0100

[config: Target = 0100]# pub-get 0100 1001

Set publication for node 0100 status: Success
Publication address: 0xc000
Period: 0 ms
Retransmit count: 0
Retransmit Interval Steps: 0
---
mesh/config-client.c | 36 ++++++++++++++++++++++++++++++++++++
1 file changed, 36 insertions(+)

diff --git a/mesh/config-client.c b/mesh/config-client.c
index 3d021269c..9872ee11d 100644
--- a/mesh/config-client.c
+++ b/mesh/config-client.c
@@ -683,6 +683,42 @@ static void cmd_set_pub(int argc, char *argv[])
bt_shell_printf("Failed to send \"SET MODEL PUBLICATION\"\n");
}

+static void cmd_get_pub(int argc, char *argv[])
+{
+ uint16_t n;
+ uint8_t msg[32];
+ int parm_cnt;
+
+ if (IS_UNASSIGNED(target)) {
+ bt_shell_printf("Destination not set\n");
+ return;
+ }
+
+ n = mesh_opcode_set(OP_CONFIG_MODEL_PUB_GET, msg);
+
+ parm_cnt = read_input_parameters(argc, argv);
+ if (parm_cnt != 2) {
+ bt_shell_printf("Bad arguments: %s\n", argv[1]);
+ return;
+ }
+
+ /* Element Address */
+ put_le16(parms[0], msg + n);
+ n += 2;
+ /* Model Id */
+ if (parms[1] > 0xffff) {
+ put_le16(parms[1] >> 16, msg + n);
+ put_le16(parms[1], msg + n + 2);
+ n += 4;
+ } else {
+ put_le16(parms[1], msg + n);
+ n += 2;
+ }
+
+ if (!config_send(msg, n))
+ bt_shell_printf("Failed to send \"GET MODEL PUBLICATION\"\n");
+}
+
static void cmd_sub_add(int argc, char *argv[])
{
uint16_t n;
--
2.11.0


2017-12-15 06:46:03

by Steve Brown

[permalink] [raw]
Subject: [PATCH V3 6/9] mesh: meshctl: Add set/get proxy

From: Steve Brown <[email protected]>

Set the current proxy state of node 0100
Setting the proxy state to 0 will disconnect
the GATT proxy connection.

[config: Target = 0100]# proxy-set 1

Node 0100 Proxy state: 0x01

Get the current proxy state of element 0100

[config: Target = 0100]# proxy-get

Node 0100 Proxy state: 0x01
---
mesh/config-client.c | 36 ++++++++++++++++++++++++++++++++++++
1 file changed, 36 insertions(+)

diff --git a/mesh/config-client.c b/mesh/config-client.c
index 9872ee11d..2d132dbef 100644
--- a/mesh/config-client.c
+++ b/mesh/config-client.c
@@ -159,6 +159,13 @@ static bool client_msg_recvd(uint16_t src, uint8_t *data,
prov_db_add_binding(node, addr - src, mod_id, app_idx);
break;

+ case OP_CONFIG_PROXY_STATUS:
+ if (len != 1)
+ return true;
+ bt_shell_printf("Node %4.4x Proxy state: 0x%02x\n",
+ src, data[0]);
+ break;
+
case OP_CONFIG_DEFAULT_TTL_STATUS:
if (len != 1)
return true;
@@ -612,6 +619,35 @@ static void cmd_bind(int argc, char *argv[])
bt_shell_printf("Failed to send \"MODEL APP BIND\"\n");
}

+static void cmd_set_proxy(int argc, char *argv[])
+{
+ uint16_t n;
+ uint8_t msg[2 + 1 + 4];
+ int parm_cnt;
+
+ if (!verify_config_target(target))
+ return;
+
+ n = mesh_opcode_set(OP_CONFIG_PROXY_SET, msg);
+
+ parm_cnt = read_input_parameters(argc, argv);
+ if (parm_cnt != 1) {
+ bt_shell_printf("bad arguments");
+ return;
+ }
+
+ msg[n++] = parms[0];
+ msg[n++] = parms[1];
+
+ if (!config_send(msg, n))
+ bt_shell_printf("Failed to send \"SET PROXY\"\n");
+}
+
+static void cmd_get_proxy(int argc, char *argv[])
+{
+ cmd_default(OP_CONFIG_PROXY_GET);
+}
+
static void cmd_set_ttl(int argc, char *argv[])
{
uint16_t n;
--
2.11.0


2017-12-15 06:46:00

by Steve Brown

[permalink] [raw]
Subject: [PATCH V3 3/9] mesh: meshctl: Add set heartbeat publish

From: Steve Brown <[email protected]>

Sets heartbeat for node 0100

[config: Target = 0100]# hb-set 0100 0 0 0 0

Set heartbeat for node 0100 status: Success
Destination: 0100
Count: 00
Period: 00
TTL: ff
Features: 0000
Net_Idx: 0000
---
mesh/config-client.c | 75 ++++++++++++++++++++++++++++++++++++++++++++++++++++
mesh/net.c | 20 +++++++++++++-
2 files changed, 94 insertions(+), 1 deletion(-)

diff --git a/mesh/config-client.c b/mesh/config-client.c
index aa7414cc3..270a8ca36 100644
--- a/mesh/config-client.c
+++ b/mesh/config-client.c
@@ -258,6 +258,24 @@ static bool client_msg_recvd(uint16_t src, uint8_t *data,
bt_shell_printf("Subscr Addr:\t%4.4x\n",
get_le16(data + i));
break;
+
+ /* Per Mesh Profile 4.3.2.63 */
+ case OP_CONFIG_HEARTBEAT_PUB_STATUS:
+ bt_shell_printf("\nSet heartbeat for node %4.4x status: %s\n",
+ src,
+ data[0] == MESH_STATUS_SUCCESS ? "Success" :
+ mesh_status_str(data[0]));
+
+ if (data[0] != MESH_STATUS_SUCCESS)
+ return true;
+
+ bt_shell_printf("Destination:\t%4.4x\n", get_le16(data + 1));
+ bt_shell_printf("Count:\t\t%2.2x\n", data[3]);
+ bt_shell_printf("Period:\t\t%2.2x\n", data[4]);
+ bt_shell_printf("TTL:\t\t%2.2x\n", data[5]);
+ bt_shell_printf("Features:\t%4.4x\n", get_le16(data + 6));
+ bt_shell_printf("Net_Idx:\t%4.4x\n", get_le16(data + 8));
+ break;
}

return true;
@@ -712,6 +730,46 @@ static void cmd_sub_get(int argc, char *argv[])
bt_shell_printf("Failed to send \"GET SUB GET\"\n");
}

+static void cmd_set_hb(int argc, char *argv[])
+{
+ uint16_t n;
+ uint8_t msg[32];
+ int parm_cnt;
+
+ if (IS_UNASSIGNED(target)) {
+ bt_shell_printf("Destination not set\n");
+ return;
+ }
+
+ n = mesh_opcode_set(OP_CONFIG_HEARTBEAT_PUB_SET, msg);
+
+ parm_cnt = read_input_parameters(argc, argv);
+ if (parm_cnt != 5) {
+ bt_shell_printf("Bad arguments: %s\n", argv[1]);
+ return;
+ }
+
+ /* Per Mesh Profile 4.3.2.62 */
+ /* Publish address */
+ put_le16(parms[0], msg + n);
+ n += 2;
+ /* Count Log */
+ msg[n++] = parms[1];
+ /* Period Log */
+ msg[n++] = parms[2];
+ /* Heartbeat TTL */
+ msg[n++] = DEFAULT_TTL;
+ /* Features */
+ put_le16(parms[3], msg + n);
+ n += 2;
+ /* NetKey Index */
+ put_le16(parms[4], msg + n);
+ n += 2;
+
+ if (!config_send(msg, n))
+ bt_shell_printf("Failed to send \"SET HEARTBEAT PUBLICATION\"\n");
+}
+
static void cmd_get_ttl(int argc, char *argv[])
{
cmd_default(OP_CONFIG_DEFAULT_TTL_GET);
@@ -741,6 +799,23 @@ static const struct bt_shell_menu cfg_menu = {
{"pub-set", "<ele_addr> <pub_addr> <app_idx> "
"<period (step|res)> <re-xmt (count|per)> <model>",
cmd_set_pub, "Set publication"},
+ {"proxy-set", "<proxy>", cmd_set_proxy,
+ "Set proxy state"},
+ {"proxy-get", NULL, cmd_get_proxy,
+ "Get proxy state"},
+ {"ident-set", "<net_idx> <state>", cmd_set_ident,
+ "Set node identity state"},
+ {"ident-get", "<net_idx>", cmd_get_ident,
+ "Get node identity state"},
+ {"relay-set", "<relay> <rexmt count> <rexmt steps>",
+ cmd_set_relay,
+ "Set relay"},
+ {"relay-get", NULL, cmd_get_relay,
+ "Get relay"},
+ {"pub-get", "<ele_addr> <model>", cmd_get_pub,
+ "Get publication"},
+ {"hb-pub-set", "<pub_addr> <count> <period> <features> <net_idx>",
+ cmd_set_hb, "Set heartbeati publish"},
{"sub-add", "<ele_addr> <sub_addr> <model id>",
cmd_sub_add, "Subscription add"},
{"sub-get", "<ele_addr> <model id>",
diff --git a/mesh/net.c b/mesh/net.c
index 421dc6955..20dfcb8a8 100644
--- a/mesh/net.c
+++ b/mesh/net.c
@@ -1399,6 +1399,24 @@ static bool ctl_rxed(uint16_t net_idx, uint32_t iv_index,
uint8_t *trans, uint16_t len)
{
/* TODO: Handle control messages */
+
+ /* Per Mesh Profile 3.6.5.10 */
+ if (trans[0] == NET_OP_HEARTBEAT) {
+ uint16_t feat = get_be16(trans + 2);
+
+ bt_shell_printf("HEARTBEAT src: %4.4x dst: %4.4x \
+ TTL: %2.2x feat: %s%s%s%s\n",
+ src, dst, trans[1],
+ (feat & MESH_FEATURE_RELAY) ? "relay " : "",
+ (feat & MESH_FEATURE_PROXY) ? "proxy " : "",
+ (feat & MESH_FEATURE_FRIEND) ? "friend " : "",
+ (feat & MESH_FEATURE_LPN) ? "lpn" : "");
+ return true;
+ }
+
+ bt_shell_printf("unrecognized control message src:%4.4x dst:%4.4x len:%d\n",
+ src, dst, len);
+ print_byte_array("msg: ", trans, len);
return false;
}

@@ -2098,7 +2116,7 @@ bool net_access_layer_send(uint8_t ttl, uint16_t src, uint32_t dst,
if (!result)
return false;

- segN = SEG_MAX(len + sizeof(uint32_t));
+ segN = SEG_MAX(len + sizeof(mic32));

/* Only one ACK required SAR message per destination at a time */
if (segN && IS_UNICAST(dst)) {
--
2.11.0


2017-12-15 06:46:01

by Steve Brown

[permalink] [raw]
Subject: [PATCH V3 4/9] mesh: meshctl: Add get app keys

From: Steve Brown <[email protected]>

List application keys of model 1000 in element 0100.

[config: Target = 0100]# app-get 0100 1000

Model App Key list for node 0100 length: 7 status: Success
Element Addr: 0100
Model ID: 1000
Model App Key: 0001
---
mesh/config-client.c | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 52 insertions(+)

diff --git a/mesh/config-client.c b/mesh/config-client.c
index 270a8ca36..3d021269c 100644
--- a/mesh/config-client.c
+++ b/mesh/config-client.c
@@ -259,6 +259,24 @@ static bool client_msg_recvd(uint16_t src, uint8_t *data,
get_le16(data + i));
break;

+ /* Per Mesh Profile 4.3.2.50 */
+ case OP_MODEL_APP_LIST:
+ bt_shell_printf("\nModel App Key list for node %4.4x "
+ "length: %u status: %s\n", src, len,
+ data[0] == MESH_STATUS_SUCCESS ? "Success" :
+ mesh_status_str(data[0]));
+
+ if (data[0] != MESH_STATUS_SUCCESS)
+ return true;
+
+ bt_shell_printf("Element Addr:\t%4.4x\n", get_le16(data + 1));
+ bt_shell_printf("Model ID:\t%4.4x\n", get_le16(data + 3));
+
+ for (i = 5; i < len; i += 2)
+ bt_shell_printf("Model App Key:\t%4.4x\n",
+ get_le16(data + i));
+ break;
+
/* Per Mesh Profile 4.3.2.63 */
case OP_CONFIG_HEARTBEAT_PUB_STATUS:
bt_shell_printf("\nSet heartbeat for node %4.4x status: %s\n",
@@ -730,6 +748,37 @@ static void cmd_sub_get(int argc, char *argv[])
bt_shell_printf("Failed to send \"GET SUB GET\"\n");
}

+static void cmd_get_app(int argc, char *argv[])
+{
+ uint16_t n;
+ uint8_t msg[32];
+ int parm_cnt;
+
+ if (IS_UNASSIGNED(target)) {
+ bt_shell_printf("Destination not set\n");
+ return;
+ }
+
+ n = mesh_opcode_set(OP_MODEL_APP_GET, msg);
+
+ parm_cnt = read_input_parameters(argc, argv);
+ if (parm_cnt != 2) {
+ bt_shell_printf("Bad arguments: %s\n", argv[1]);
+ return;
+ }
+
+ /* Per Mesh Profile 4.3.2.49 */
+ /* Element Address */
+ put_le16(parms[0], msg + n);
+ n += 2;
+ /* Model ID */
+ put_le16(parms[1], msg + n);
+ n += 2;
+
+ if (!config_send(msg, n))
+ bt_shell_printf("Failed to send \"GET APP GET\"\n");
+}
+
static void cmd_set_hb(int argc, char *argv[])
{
uint16_t n;
@@ -821,6 +870,9 @@ static const struct bt_shell_menu cfg_menu = {
{"sub-get", "<ele_addr> <model id>",
cmd_sub_get, "Subscription get"},

+ {"app-get", "<ele_addr> <model id>",
+ cmd_get_app, "Get App Keys"},
+
{} },
};

--
2.11.0


2017-12-15 06:45:59

by Steve Brown

[permalink] [raw]
Subject: [PATCH V3 2/9] mesh: meshctl: Add add/get subscribe

From: Steve Brown <[email protected]>

Adds a subscription for group address c000 to
model 1000 in element 0100.

[config: Target = 0100]# sub-add 0100 c000 1000

Subscription changed for node 0100 status: Success
ModelId 1000
Element Addr: 0100
Subscr Addr: c000

Gets the current subscription list for model 1000
on element 0100.

[config: Target = 0100]# sub-get 0100 1000

Subscription list for node 0100 length: 7 status: Success
Element Addr: 0100
Model ID: 1000
Subscr Addr: c000
---
mesh/config-client.c | 107 +++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 107 insertions(+)

diff --git a/mesh/config-client.c b/mesh/config-client.c
index 51adf2c52..aa7414cc3 100644
--- a/mesh/config-client.c
+++ b/mesh/config-client.c
@@ -61,6 +61,7 @@ static bool client_msg_recvd(uint16_t src, uint8_t *data,
uint8_t ele_idx;
struct mesh_publication pub;
int n;
+ uint16_t i;

if (mesh_opcode_get(data, len, &opcode, &n)) {
len -= n;
@@ -220,6 +221,43 @@ static bool client_msg_recvd(uint16_t src, uint8_t *data,
prov_db_node_set_model_pub(node, ele_idx, mod_id,
node_model_pub_get(node, ele_idx, mod_id));
break;
+
+ /* Per Mesh Profile 4.3.2.19 */
+ case OP_CONFIG_MODEL_SUB_STATUS:
+ bt_shell_printf("\nSubscription changed"
+ " for node %4.4x status: %s\n", src,
+ data[0] == MESH_STATUS_SUCCESS ? "Success" :
+ mesh_status_str(data[0]));
+
+ if (data[0] != MESH_STATUS_SUCCESS)
+ return true;
+
+ bt_shell_printf("Element Addr:\t%4.4x\n", get_le16(data + 1));
+ bt_shell_printf("Subscr Addr:\t%4.4x\n", get_le16(data + 3));
+ bt_shell_printf("Model ID:\t%4.4x\n", get_le16(data + 5));
+ break;
+
+ /* TODO */
+ /* Save subscription info in database */
+
+ /* Per Mesh Profile 4.3.2.27 */
+ case OP_CONFIG_MODEL_SUB_LIST:
+
+ bt_shell_printf("\nSubscription list for node %4.4x "
+ "length: %u status: %s\n", src, len,
+ data[0] == MESH_STATUS_SUCCESS ? "Success" :
+ mesh_status_str(data[0]));
+
+ if (data[0] != MESH_STATUS_SUCCESS)
+ return true;
+
+ bt_shell_printf("Element Addr:\t%4.4x\n", get_le16(data + 1));
+ bt_shell_printf("Model ID:\t%4.4x\n", get_le16(data + 3));
+
+ for (i = 5; i < len; i += 2)
+ bt_shell_printf("Subscr Addr:\t%4.4x\n",
+ get_le16(data + i));
+ break;
}

return true;
@@ -609,6 +647,71 @@ static void cmd_set_pub(int argc, char *argv[])
bt_shell_printf("Failed to send \"SET MODEL PUBLICATION\"\n");
}

+static void cmd_sub_add(int argc, char *argv[])
+{
+ uint16_t n;
+ uint8_t msg[32];
+ int parm_cnt;
+
+ if (IS_UNASSIGNED(target)) {
+ bt_shell_printf("Destination not set\n");
+ return;
+ }
+
+ n = mesh_opcode_set(OP_CONFIG_MODEL_SUB_ADD, msg);
+
+ parm_cnt = read_input_parameters(argc, argv);
+ if (parm_cnt != 3) {
+ bt_shell_printf("Bad arguments: %s\n", argv[1]);
+ return;
+ }
+
+ /* Per Mesh Profile 4.3.2.19 */
+ /* Element Address */
+ put_le16(parms[0], msg + n);
+ n += 2;
+ /* Subscription Address */
+ put_le16(parms[1], msg + n);
+ n += 2;
+ /* SIG Model ID */
+ put_le16(parms[2], msg + n);
+ n += 2;
+
+ if (!config_send(msg, n))
+ bt_shell_printf("Failed to send \"ADD SUBSCRIPTION\"\n");
+}
+
+static void cmd_sub_get(int argc, char *argv[])
+{
+ uint16_t n;
+ uint8_t msg[32];
+ int parm_cnt;
+
+ if (IS_UNASSIGNED(target)) {
+ bt_shell_printf("Destination not set\n");
+ return;
+ }
+
+ n = mesh_opcode_set(OP_CONFIG_MODEL_SUB_GET, msg);
+
+ parm_cnt = read_input_parameters(argc, argv);
+ if (parm_cnt != 2) {
+ bt_shell_printf("Bad arguments: %s\n", argv[1]);
+ return;
+ }
+
+ /* Per Mesh Profile 4.3.2.27 */
+ /* Element Address */
+ put_le16(parms[0], msg + n);
+ n += 2;
+ /* Model ID */
+ put_le16(parms[1], msg + n);
+ n += 2;
+
+ if (!config_send(msg, n))
+ bt_shell_printf("Failed to send \"GET SUB GET\"\n");
+}
+
static void cmd_get_ttl(int argc, char *argv[])
{
cmd_default(OP_CONFIG_DEFAULT_TTL_GET);
@@ -638,6 +741,10 @@ static const struct bt_shell_menu cfg_menu = {
{"pub-set", "<ele_addr> <pub_addr> <app_idx> "
"<period (step|res)> <re-xmt (count|per)> <model>",
cmd_set_pub, "Set publication"},
+ {"sub-add", "<ele_addr> <sub_addr> <model id>",
+ cmd_sub_add, "Subscription add"},
+ {"sub-get", "<ele_addr> <model id>",
+ cmd_sub_get, "Subscription get"},

{} },
};
--
2.11.0


2017-12-15 06:45:58

by Steve Brown

[permalink] [raw]
Subject: [PATCH V3 1/9] mesh: meshctl: Change command names to <cmd>-<get/set>

From: Steve Brown <[email protected]>

Fix lines over 80 chars
Move cmd_default()
Add parameter to pub-set to control retransmit count
---
mesh/config-client.c | 78 +++++++++++++++++++++++++++-------------------------
1 file changed, 41 insertions(+), 37 deletions(-)

diff --git a/mesh/config-client.c b/mesh/config-client.c
index 3d618b6a6..51adf2c52 100644
--- a/mesh/config-client.c
+++ b/mesh/config-client.c
@@ -170,9 +170,9 @@ static bool client_msg_recvd(uint16_t src, uint8_t *data,
if (len != 12 && len != 14)
return true;

- bt_shell_printf("\nSet publication for node %4.4x status: %s\n", src,
- data[0] == MESH_STATUS_SUCCESS ? "Success" :
- mesh_status_str(data[0]));
+ bt_shell_printf("\nSet publication for node %4.4x status: %s\n",
+ src, data[0] == MESH_STATUS_SUCCESS ?
+ "Success" : mesh_status_str(data[0]));

if (data[0] != MESH_STATUS_SUCCESS)
return true;
@@ -189,6 +189,7 @@ static bool client_msg_recvd(uint16_t src, uint8_t *data,
pub.ttl = data[7];
pub.period = data[8];
n = (data[8] & 0x3f);
+ bt_shell_printf("Publication address: 0x%04x\n", pub.u.addr16);
switch (data[8] >> 6) {
case 0:
bt_shell_printf("Period: %d ms\n", n * 100);
@@ -206,7 +207,8 @@ static bool client_msg_recvd(uint16_t src, uint8_t *data,

pub.retransmit = data[9];
bt_shell_printf("Retransmit count: %d\n", data[9] >> 5);
- bt_shell_printf("Retransmit Interval Steps: %d\n", data[9] & 0x1f);
+ bt_shell_printf("Retransmit Interval Steps: %d\n",
+ data[9] & 0x1f);

ele_idx = ele_addr - node_get_primary(node);

@@ -219,6 +221,7 @@ static bool client_msg_recvd(uint16_t src, uint8_t *data,
node_model_pub_get(node, ele_idx, mod_id));
break;
}
+
return true;
}

@@ -287,6 +290,23 @@ static bool config_send(uint8_t *buf, uint16_t len)

}

+static void cmd_default(uint32_t opcode)
+{
+ uint16_t n;
+ uint8_t msg[32];
+
+ if (IS_UNASSIGNED(target)) {
+ bt_shell_printf("Destination not set\n");
+ return;
+ }
+
+ n = mesh_opcode_set(opcode, msg);
+
+ if (!config_send(msg, n))
+ bt_shell_printf("Failed to send command (opcode 0x%x)\n",
+ opcode);
+}
+
static void cmd_get_composition(int argc, char *argv[])
{
uint16_t n;
@@ -556,7 +576,7 @@ static void cmd_set_pub(int argc, char *argv[])
n = mesh_opcode_set(OP_CONFIG_MODEL_PUB_SET, msg);

parm_cnt = read_input_parameters(argc, argv);
- if (parm_cnt != 5) {
+ if (parm_cnt != 6) {
bt_shell_printf("Bad arguments\n");
return;
}
@@ -574,14 +594,14 @@ static void cmd_set_pub(int argc, char *argv[])
/* Publish period step count and step resolution */
msg[n++] = parms[3];
/* Publish retransmit count & interval steps */
- msg[n++] = (1 << 5) + 2;
+ msg[n++] = parms[4];
/* Model Id */
- if (parms[4] > 0xffff) {
- put_le16(parms[4] >> 16, msg + n);
- put_le16(parms[4], msg + n + 2);
+ if (parms[5] > 0xffff) {
+ put_le16(parms[5] >> 16, msg + n);
+ put_le16(parms[5], msg + n + 2);
n += 4;
} else {
- put_le16(parms[4], msg + n);
+ put_le16(parms[5], msg + n);
n += 2;
}

@@ -589,23 +609,6 @@ static void cmd_set_pub(int argc, char *argv[])
bt_shell_printf("Failed to send \"SET MODEL PUBLICATION\"\n");
}

-static void cmd_default(uint32_t opcode)
-{
- uint16_t n;
- uint8_t msg[32];
-
- if (IS_UNASSIGNED(target)) {
- bt_shell_printf("Destination not set\n");
- return;
- }
-
- n = mesh_opcode_set(opcode, msg);
-
- if (!config_send(msg, n))
- bt_shell_printf("Failed to send command (opcode 0x%x)\n",
- opcode);
-}
-
static void cmd_get_ttl(int argc, char *argv[])
{
cmd_default(OP_CONFIG_DEFAULT_TTL_GET);
@@ -614,27 +617,28 @@ static void cmd_get_ttl(int argc, char *argv[])
static const struct bt_shell_menu cfg_menu = {
.name = "config",
.entries = {
- {"target", "<unicast>", cmd_set_node,
+ {"target", "<unicast>", cmd_set_node,
"Set target node to configure"},
- {"get-composition", "[<page_num>]", cmd_get_composition,
+ {"composition-get", "[<page_num>]", cmd_get_composition,
"Get Composition Data"},
- {"add-netkey", "<net_idx>", cmd_add_net_key,
+ {"netkey-add", "<net_idx>", cmd_add_net_key,
"Add network key"},
- {"del-netkey", "<net_idx>", cmd_del_net_key,
+ {"netkey-del", "<net_idx>", cmd_del_net_key,
"Delete network key"},
- {"add-appkey", "<app_idx>", cmd_add_app_key,
+ {"appkey-add", "<app_idx>", cmd_add_app_key,
"Add application key"},
- {"del-appkey", "<app_idx>", cmd_del_app_key,
+ {"appkey-del", "<app_idx>", cmd_del_app_key,
"Delete application key"},
{"bind", "<ele_idx> <app_idx> <mod_id> [cid]",
cmd_bind, "Bind app key to a model"},
- {"set-ttl", "<ttl>", cmd_set_ttl,
+ {"ttl-set", "<ttl>", cmd_set_ttl,
"Set default TTL"},
- {"get-ttl", NULL, cmd_get_ttl,
+ {"ttl-get", NULL, cmd_get_ttl,
"Get default TTL"},
- {"set-pub", "<ele_addr> <pub_addr> <app_idx> "
- "<period (step|res)> <model>",
+ {"pub-set", "<ele_addr> <pub_addr> <app_idx> "
+ "<period (step|res)> <re-xmt (count|per)> <model>",
cmd_set_pub, "Set publication"},
+
{} },
};

--
2.11.0