Skip to content
This repository has been archived by the owner on Dec 20, 2024. It is now read-only.

Commit

Permalink
add panduza local broker discovery service function to script (#91)
Browse files Browse the repository at this point in the history
* add panduza local broker discovery service function to script

* Make client easier to create with local discovery, can precise a platform name to choose the platform you want to use on your local network

* Delete panduza/core/panduza_local_broker_discovery.py

* Local discovery works with alias

* ask to the user if he wants to use the discover platform

* Show the user a list of platform instead of asking him one after another

* Comments has been changed to respect new function

* change to follow new spec of local discovery answer

* function to make user friendly moving state of relay

* fix get_state_open (forgot to return the value)

---------

Co-authored-by: Damien Albisson <[email protected]>
  • Loading branch information
Darcraytore1 and Damien Albisson authored May 22, 2024
1 parent 8c00c91 commit 90e3c15
Show file tree
Hide file tree
Showing 4 changed files with 263 additions and 12 deletions.
31 changes: 25 additions & 6 deletions panduza/core/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,11 @@
import threading
import traceback

import json
import json, time
import queue
import paho.mqtt.client as mqtt

from .core import Core
from .core import Core, Panduza_local_broker_discovery

# ┌────────────────────────────────────────┐
# │ Utilities │
Expand All @@ -32,30 +32,49 @@

class Client:

def __init__(self, broker_alias=None, interface_alias=None, url=None, port=None):
def __init__(self, broker_alias=None, interface_alias=None, url=None, port=None, platform_name=None):
"""Client Constructor
The client can be build from
- Alias
OR
- Url + Port
OR
- Platform name
OR
- Nothing
Args:
alias (str, optional): connection alias. Defaults to None.
url (str, optional): broker url. Defaults to None.
port (str, optional): port url. Defaults to None.
platform_name (str, optional): name of the platform. Defaults to None.
"""
# Manage double way of loading client information
# Manage for way of loading client information

# Using broker alias
if broker_alias:
self.url, self.port = Core.BrokerInfoFromBrokerAlias(broker_alias)
elif interface_alias:
self.url, self.port = Core.BrokerInfoFromInterfaceAlias(
interface_alias)
else:

# Using url and port directly
elif (url != None and port != None):
self.url = str(url)
self.port = int(port)

# Look for platforms on the local network and use the broker informations
# of the first platform found with the given platform_name
elif (platform_name != None):
self.url, self.port = Panduza_local_broker_discovery.get_broker_info_with_name(platform_name=platform_name)

# Look for platforms on the local network and use the broker informations of the
# first platform found during discovery
else:
self.url, self.port = Panduza_local_broker_discovery.get_first_broker_info()

# Set flags
self.is_connected = False

Expand Down Expand Up @@ -90,7 +109,7 @@ def __init__(self, broker_alias=None, interface_alias=None, url=None, port=None)
# └────────────────────────────────────────┘

def connect(self):
self.log.debug("Connect to broker")
self.log.debug("Connect to broker {self.url}:" + str(self.port))
self.client.connect(self.url, self.port)
self.client.loop_start()

Expand Down
210 changes: 204 additions & 6 deletions panduza/core/core.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,188 @@
import re
import json
import socket, json, time
import logging


import panduza.core.log

# ┌────────────────────────────────────────┐
# │ Local broker discovery │
# └────────────────────────────────────────┘

class Panduza_local_broker_discovery:

# Should be with other consts
PORT_LOCAL_DISCOVERY = 53035


def panduza_local_broker_discovery():
""" Return the addresses of brokers discover on the local network
Raises:
Exception: raise if connection alias not loaded
Returns:
List[str, int]: url, port
"""

broker_addrs = []

# Get every network interfaces
interfaces = socket.getaddrinfo(host=socket.gethostname(), port=None, family=socket.AF_INET)
ips = [ip[-1][0] for ip in interfaces]

request_payload = json.dumps({"search": True})
request_payload_utf8 = request_payload.encode(
encoding="utf-8"
)

for ip in ips:
try:
# Send broadcast local discovery request on the local networks
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
sock.setblocking(False)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
sock.bind((ip, 0))
sock.sendto(request_payload_utf8, ("255.255.255.255", Panduza_local_broker_discovery.PORT_LOCAL_DISCOVERY))
time.sleep(1)

answer_payload, broker_addr_port = sock.recvfrom(1000)

# Get the name in the payload
json_answer = answer_payload.decode(
encoding="utf-8"
)

# add the platform addr, port and name to the list of broker detected
json_answer = json.loads(json_answer)
platform_info = json_answer["platform"]
broker_info = json_answer["broker"]

if (platform_info != None and broker_info != None):
platform_name = platform_info["name"]
broker_addr = broker_addr_port[0]
broker_port = broker_info["port"]

if (platform_name != None and broker_addr != None and broker_port != None):
broker_addrs.append(((broker_addr, broker_port), platform_name))


except Exception as e:
pass

sock.close()

return broker_addrs

def get_broker_info_with_name(platform_name):
""" Get the broker info of the platform discover on the local network with the given name
and chosen by the user with keyboard input
Arg:
- platform_name (str, required): name of the platform.
Raises:
NameError: if any platform find on the local network with the given name
Returns:
str, int: url, port
"""

# Find the platform with the platform name asked

list_info_brokers = Panduza_local_broker_discovery.panduza_local_broker_discovery()
brokers_with_given_name = []

for info_broker in list_info_brokers:
platform_name_detected = info_broker[1]
if (platform_name_detected == platform_name):
tmp_url, tmp_port = info_broker[0][0], 1883
# add every platform with this name to this list
brokers_with_given_name.append((tmp_url, tmp_port))


# If any platform find with the given platform_name raise a error
if (not brokers_with_given_name):
raise NameError("Any platform find on the local platform with the name: " + platform_name)

while (True):
# Print every platform discovered
print("########################################")
for i in range(len(brokers_with_given_name)):
tmp_url, tmp_port = brokers_with_given_name[i][0], brokers_with_given_name[i][1]
print(f"{i + 1} - Platform name : {platform_name}, {tmp_url}:{tmp_port}")
print("########################################\n")

# Ask to the user which platform he wants to use
print(f"Which platform do you want to use ? Enter the number at the left of the platform you want to use ? [1", end="")
for i in range(2, len(brokers_with_given_name) + 1):
print(f" or {i}", end="")
print("]")
user_choice = input()

# Check if the user has input a correct platform number else ask him
# to enter his choice a second time
if ((user_choice.isnumeric()) and (int(user_choice) >= 1) and (int(user_choice) <= len(brokers_with_given_name))):
broker_choosen = brokers_with_given_name[int(user_choice) - 1]
url, port = broker_choosen[0], broker_choosen[1]
nbr_plat = int(user_choice)
break

print(f"Platform number {nbr_plat} has been chosen with url/port : {url}:{port} and name : {platform_name}")

return url, port

def get_first_broker_info():
""" Get the broker info of the platform discover on the local network and chosen
by the user with keyboard input
Raises:
Exception: if any platform find on the local network
Returns:
str, int: url, port
"""
url = None
port = None

list_info_brokers = Panduza_local_broker_discovery.panduza_local_broker_discovery()
if (len(list_info_brokers) == 0):
# Maybe create a exception class for not findind any local platform
raise Exception("Any platform find on the local platform with the name")

while (True):
# Print every platform discovered
print("########################################")
for i in range(len(list_info_brokers)):
# Need to change the local discovery to get the broker port and not the one
# of the local discovery service
platform_name = list_info_brokers[i][1]
tmp_url, tmp_port = list_info_brokers[i][0][0], 1883
print(f"{i + 1} - Platform name : {platform_name}, {tmp_url}:{tmp_port}")
print("########################################\n")

# Ask to the user which platform he wants to use
print(f"Which platform do you want to use ? Enter the number at the left of the platform you want to use ? [1", end="")
for i in range(2, len(list_info_brokers) + 1):
print(f" or {i}", end="")
print("]")
user_choice = input()

# Check if the user has input a correct platform number else ask him
# to enter his choice a second time
if ((user_choice.isnumeric()) and (int(user_choice) >= 1) and (int(user_choice) <= len(list_info_brokers))):
broker_choosen = list_info_brokers[int(user_choice) - 1]
url, port = broker_choosen[0][0], broker_choosen[0][1]
platform_name = broker_choosen[1]
nbr_platform = int(user_choice)
break

print(f"Platform number {nbr_platform} has been chosen with url/port : {url}:{port} and name : {platform_name}")

return url, port


# Create the logger for core events
CoreLog = logging.getLogger(f"pza.core")

Expand Down Expand Up @@ -103,10 +281,30 @@ def __LoadAliasesFromDict(connections):

# Load connection
CoreLog.info(f" Load connection : {co_name} & {co_data}")
Core.Connections[co_name] = {
"url": co_data["url"],
"port": co_data["port"]
}

# if url and port precise inside alias config use it
if ("url" in co_data.keys()) and ("port" in co_data.keys()):
Core.Connections[co_name] = {
"url": co_data["url"],
"port": co_data["port"]
}
# Use the local discovery to find the broker link to the platform
# with the given name
elif ("platform_name" in co_data.keys()):
url, port = Panduza_local_broker_discovery.get_broker_info_with_name(co_data["platform_name"])
Core.Connections[co_name] = {
"url": url,
"port": port
}
# Use the local discovery to find the first platform discovered
else:
url, port = Panduza_local_broker_discovery.get_first_broker_info()
Core.Connections[co_name] = {
"url": url,
"port": port
}

# create automaticaly url,

# Load aliases
for it in co_data["interfaces"]:
Expand Down Expand Up @@ -142,7 +340,7 @@ def BrokerInfoFromBrokerAlias(alias):
###########################################################################

def BrokerInfoFromInterfaceAlias(alias):
"""Return the broker information to reach reach the interface from its alias
"""Return the broker information to reach the interface from its alias
"""
# Get alias
if alias not in Core.Aliases.keys():
Expand Down
6 changes: 6 additions & 0 deletions panduza/interfaces/relay.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,9 @@ def __post_init__(self):
if self.ensure:
self.ensure_init()

def get_state_open(self):
return self.state.open.get()

def set_state_open(self, value):
self.state.open.set(value)

28 changes: 28 additions & 0 deletions tools/test_local_discovery.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
from panduza import Client

# Test the comportement of local discovery to connect a
# client without precising url and port, the user can choose
# to use a plaform name to look for a platform with his given
# name on the local network, or just using it without parameters
# to look fot the first platform found
if __name__ == "__main__":

platform_name = "panduza_platform"

# Test with platform name

print(f"Try to find a platform on the local network with the name {platform_name}")

client = Client(platform_name=platform_name)
client.connect()

print(f"Success to connecting to the broker of the platform with the name {platform_name}\n")

# Test looking for the fist platform found

print(f"Try to find a platform on the local network")

client2 = Client()
client2.connect()

print("Success to connect to the first platform found on the local network")

0 comments on commit 90e3c15

Please sign in to comment.