Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-6.0 required=3.0 tests=HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_PATCH,MAILING_LIST_MULTI,SPF_PASS,USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 1FB22C43381 for ; Fri, 8 Mar 2019 22:57:53 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id CA407206DF for ; Fri, 8 Mar 2019 22:57:52 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726393AbfCHW5w (ORCPT ); Fri, 8 Mar 2019 17:57:52 -0500 Received: from mga07.intel.com ([134.134.136.100]:41083 "EHLO mga07.intel.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726324AbfCHW5w (ORCPT ); Fri, 8 Mar 2019 17:57:52 -0500 X-Amp-Result: SKIPPED(no attachment in message) X-Amp-File-Uploaded: False Received: from orsmga003.jf.intel.com ([10.7.209.27]) by orsmga105.jf.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384; 08 Mar 2019 14:57:51 -0800 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.58,456,1544515200"; d="scan'208";a="132507497" Received: from ingas-nuc1.sea.intel.com ([10.251.144.43]) by orsmga003.jf.intel.com with ESMTP; 08 Mar 2019 14:57:50 -0800 From: Inga Stotland To: linux-bluetooth@vger.kernel.org Cc: brian.gix@intel.com, johan.hedberg@gmail.com, luiz.dentz@gmail.com, Inga Stotland Subject: [PATCH BlueZ 1/1] test: Add unified test for mesh node example app Date: Fri, 8 Mar 2019 14:57:49 -0800 Message-Id: <20190308225749.20469-1-inga.stotland@intel.com> X-Mailer: git-send-email 2.17.2 Sender: linux-bluetooth-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-bluetooth@vger.kernel.org This adds one script, test-mesh, to replace three test-join, example-onoff-server and example-onoff-client. This is menu driven test that allows provisioning (join) and/or connecting existing (attach) nodes. --- test/agent.py | 10 +- test/example-onoff-client | 288 ---------------- test/example-onoff-server | 365 -------------------- test/test-mesh | 703 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 711 insertions(+), 655 deletions(-) delete mode 100644 test/example-onoff-client delete mode 100644 test/example-onoff-server create mode 100755 test/test-mesh diff --git a/test/agent.py b/test/agent.py index 22c92f952..bd78b6000 100755 --- a/test/agent.py +++ b/test/agent.py @@ -3,7 +3,12 @@ import sys import dbus import dbus.service -import dbus.mainloop.glib + +try: + from termcolor import colored, cprint + set_green = lambda x: colored(x, 'green', attrs=['bold']) +except ImportError: + set_green = lambda x: x AGENT_IFACE = 'org.bluez.mesh.ProvisionAgent1' AGENT_PATH = "/mesh/test/agent" @@ -37,4 +42,5 @@ class Agent(dbus.service.Object): @dbus.service.method(AGENT_IFACE, in_signature="su", out_signature="") def DisplayNumeric(self, type, value): - print("DisplayNumeric type=", type, " number=", value) + print(set_cyan("DisplayNumeric type="), type, + set_cyan(" number="), set_green(value)) diff --git a/test/example-onoff-client b/test/example-onoff-client deleted file mode 100644 index e4a87eb12..000000000 --- a/test/example-onoff-client +++ /dev/null @@ -1,288 +0,0 @@ -#!/usr/bin/env python3 - -import sys -import struct -import numpy -import dbus -import dbus.service -import dbus.exceptions - -try: - from gi.repository import GObject -except ImportError: - import gobject as GObject -from dbus.mainloop.glib import DBusGMainLoop - -MESH_SERVICE_NAME = 'org.bluez.mesh' -DBUS_PROP_IFACE = 'org.freedesktop.DBus.Properties' -DBUS_OM_IFACE = 'org.freedesktop.DBus.ObjectManager' - -MESH_NETWORK_IFACE = 'org.bluez.mesh.Network1' -MESH_NODE_IFACE = 'org.bluez.mesh.Node1' -MESH_ELEMENT_IFACE = 'org.bluez.mesh.Element1' - -VENDOR_ID_NONE = 0xffff - -app = None -bus = None -mainloop = None -node = None -token = numpy.uint64(0x76bd4f2372477600) - -def unwrap(item): - if isinstance(item, dbus.Boolean): - return bool(item) - if isinstance(item, (dbus.UInt16, dbus.Int16, dbus.UInt32, dbus.Int32, - dbus.UInt64, dbus.Int64)): - return int(item) - if isinstance(item, dbus.Byte): - return bytes([int(item)]) - if isinstance(item, dbus.String): - return item - if isinstance(item, (dbus.Array, list, tuple)): - return [unwrap(x) for x in item] - if isinstance(item, (dbus.Dictionary, dict)): - return dict([(unwrap(x), unwrap(y)) for x, y in item.items()]) - - print('Dictionary item not handled') - print(type(item)) - return item - -def attach_app_cb(node_path, dict_array): - print('Mesh application registered ', node_path) - print(type(node_path)) - print(type(dict_array)) - print(dict_array) - - els = unwrap(dict_array) - print("Get Elements") - for el in els: - print(el) - idx = struct.unpack('b', el[0])[0] - print('Configuration for Element ', end='') - print(idx) - models = el[1] - - element = app.get_element(idx) - element.set_model_config(models) - - obj = bus.get_object(MESH_SERVICE_NAME, node_path) - global node - node = dbus.Interface(obj, MESH_NODE_IFACE) - -def error_cb(error): - print('D-Bus call failed: ' + str(error)) - -def generic_reply_cb(): - print('D-Bus call done') - -def interfaces_removed_cb(object_path, interfaces): - if not mesh_net: - return - - if object_path == mesh_net[2]: - print('Service was removed') - mainloop.quit() - -class Application(dbus.service.Object): - - def __init__(self, bus): - self.path = '/example' - self.elements = [] - dbus.service.Object.__init__(self, bus, self.path) - - def get_path(self): - return dbus.ObjectPath(self.path) - - def add_element(self, element): - self.elements.append(element) - - def get_element(self, idx): - for ele in self.elements: - if ele.get_index() == idx: - return ele - - @dbus.service.method(DBUS_OM_IFACE, out_signature='a{oa{sa{sv}}}') - def GetManagedObjects(self): - response = {} - print('GetManagedObjects') - for element in self.elements: - response[element.get_path()] = element.get_properties() - return response - -class Element(dbus.service.Object): - PATH_BASE = '/example/ele' - - def __init__(self, bus, index): - self.path = self.PATH_BASE + format(index, '02x') - print(self.path) - self.models = [] - self.bus = bus - self.index = index - dbus.service.Object.__init__(self, bus, self.path) - - def _get_sig_models(self): - ids = [] - for model in self.models: - id = model.get_id() - vendor = model.get_vendor() - if vendor == VENDOR_ID_NONE: - ids.append(id) - return ids - - def get_properties(self): - return { - MESH_ELEMENT_IFACE: { - 'Index': dbus.Byte(self.index), - 'Models': dbus.Array( - self._get_sig_models(), signature='q') - } - } - - def add_model(self, model): - model.set_path(self.path) - self.models.append(model) - - def get_index(self): - return self.index - - def set_model_config(self, config): - print('Set element models config') - - @dbus.service.method(MESH_ELEMENT_IFACE, - in_signature="qqbay", out_signature="") - def MessageReceived(self, source, key, is_sub, data): - print('Message Received on Element ', end='') - print(self.index) - for model in self.models: - model.process_message(source, key, data) - - @dbus.service.method(MESH_ELEMENT_IFACE, - in_signature="qa{sv}", out_signature="") - - def UpdateModelConfiguration(self, model_id, config): - print('UpdateModelConfig ', end='') - print(hex(model_id)) - for model in self.models: - if model_id == model.get_id(): - model.set_config(config) - return - - @dbus.service.method(MESH_ELEMENT_IFACE, - in_signature="", out_signature="") - def get_path(self): - return dbus.ObjectPath(self.path) - -class Model(): - def __init__(self, model_id): - self.cmd_ops = [] - self.model_id = model_id - self.vendor = VENDOR_ID_NONE - self.path = None - - def set_path(self, path): - self.path = path - - def get_id(self): - return self.model_id - - def get_vendor(self): - return self.vendor - - def process_message(self, source, key, data): - print('Model process message') - - def set_publication(self, period): - self.period = period - - def set_bindings(self, bindings): - self.bindings = bindings - - def set_config(self, config): - if 'Bindings' in config: - self.bindings = config.get('Bindings') - print('Bindings: ', end='') - print(self.bindings) - if 'PublicationPeriod' in config: - self.set_publication(config.get('PublicationPeriod')) - print('Model publication period ', end='') - print(self.pub_period, end='') - print(' ms') - -class OnOffClient(Model): - def __init__(self, model_id): - Model.__init__(self, model_id) - self.cmd_ops = { 0x8201, # get - 0x8202, # set - 0x8203 } # set unacknowledged - print('OnOff Client') - - def _reply_cb(state): - print('State ', end=''); - print(state) - - def _send_message(self, dest, key, data, reply_cb): - print('OnOffClient send data') - node.Send(self.path, dest, key, data, reply_handler=reply_cb, - error_handler=error_cb) - - def get_state(self, dest, key): - opcode = 0x8201 - data = struct.pack(' 5: + print(set_error('Unknown menu option: '), opt) + main_menu() + elif opt == 1: + token_input = True; + print('Enter 16-digit hex node ID') + elif opt == 2: + if agent == None: + print(set_error('Provisioning agent not found')) + return + + join_mesh() + elif opt == 3: + if have_token == False: + print(set_error('Token is not set')) + main_menu() + return + + attach(token) + elif opt == 4: + if have_token == False: + print(set_error('Token is not set')) + main_menu() + return + + print('Remove mesh node') + mesh_net.Leave(token, reply_handler=generic_reply_cb, + error_handler=generic_error_cb) + have_token = False + elif opt == 5: + app_exit() + +def main_menu(): + print(set_cyan('1 - set node ID (token)')) + print(set_cyan('2 - join mesh network')) + print(set_cyan('3 - attach mesh node')) + print(set_cyan('4 - remove node')) + print(set_cyan('5 - exit')) + +def set_token(str): + global token + global have_token + + if len(str) != 16: + print(set_error('Expected 16 digits')) + return False + + try: + input_number = int(str, 16) + except ValueError: + print(set_error('Not a valid hexadecimal number')) + return False + + token = numpy.uint64(input_number) + have_token = True + + return True + +def join_mesh(): + uuid = bytearray.fromhex("0a0102030405060708090A0B0C0D0E0F") + + caps = ["out-numeric"] + oob = ["other"] + + random.shuffle(uuid) + uuid_str = array_to_string(uuid) + print('Joining with UUID ' + set_green(uuid_str)) + + mesh_net.Join(app.get_path(), uuid, + reply_handler=join_cb, + error_handler=join_error_cb) + +def app_exit(): + global mainloop + global app + + for el in app.elements: + for model in el.models: + if model.timer != None: + model.timer.cancel() + mainloop.quit() + +######################## +# Main entry +######################## +def main(): + + DBusGMainLoop(set_as_default=True) + + global bus + bus = dbus.SystemBus() + global mainloop + global app + global mesh_net + + if len(sys.argv) > 1 : + set_token(sys.argv[1]) + + mesh_net = dbus.Interface(bus.get_object(MESH_SERVICE_NAME, + "/org/bluez/mesh"), + MESH_NETWORK_IFACE) + mesh_net.connect_to_signal('InterfacesRemoved', interfaces_removed_cb) + + app = Application(bus) + + # Provisioning agent + if agent != None: + app.set_agent(agent.Agent(bus)) + + first_ele = Element(bus, 0x00) + second_ele = Element(bus, 0x01) + + print(set_yellow('Register OnOff Server model on element 0')) + first_ele.add_model(OnOffServer(0x1000)) + + print(set_yellow('Register OnOff Client model on element 1')) + second_ele.add_model(OnOffClient(0x1001)) + app.add_element(first_ele) + app.add_element(second_ele) + + mainloop = GLib.MainLoop() + + main_menu() + event_catcher = MenuDriver(process_input); + mainloop.run() + +if __name__ == '__main__': + main() -- 2.17.2