Skip to content

Commit

Permalink
Add brokers command line tools
Browse files Browse the repository at this point in the history
  • Loading branch information
nicolas-rabault committed Jan 9, 2024
1 parent 38d2d01 commit ebff67b
Show file tree
Hide file tree
Showing 3 changed files with 228 additions and 2 deletions.
138 changes: 138 additions & 0 deletions pyluos/tools/robus_broker.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
from simple_websocket_server import WebSocketServer, WebSocket
import json
import argparse


class RobusEmulator(WebSocket):
prev_node = None
ptpa = False # Linked to prev
ptpb = False # Linked to next
ptpa_poke = False
ptpb_poke = False
next_node = None
msg_index = 0

def handle(self):
if isinstance(self.data, str):
# This is a PTP command
print("\nI receive : " + str(self.data) +
" from " + str(self.address))

# PTP emulation:
# Because PTP have been designed for real time response, the Robus algorythm is not really friendly to PTP management over WebSocket.
# This broker have to drive data in a specific way allowing to make it work.
# Robus HAL will send messages only during PTP reset state and read line.
# Read_line mean Poke. When we have this we can set the line depending on the presence of another node and save this poke state on the line
# The next reset received will need to be send to the other node.
#
# if (ptp line read (PTP up) :
# if (a node is connected) :
# send state 1 to the other node
# send state 1 back
# pass this ptp to poking
# else :
# send state 0 back
# if (PTP down and ptp is poking) :
# send state to the other node

# PTPA
if self.data[3] == 'A':
# We get a PTPA data

if (self.data[4] == '1'):
if (self.prev_node != None):
print("\t\tPTPB1 val sent to the node",
str(self.prev_node.address))
self.prev_node.send_message("PTPB1")
print("\t\tPTPA1 val sent back to the node",
str(self.address))
self.send_message("PTPA1")
self.prev_node.ptpb_poke = True
self.ptpa_poke = True
else:
print("\t\tPTPA0 val sent back to the node",
str(self.address))
self.send_message("PTPA0")

if (self.data[4] == '0' and self.ptpa_poke == True and self.prev_node != None):
print("\t\tPTPB0 val sent to the node",
str(self.prev_node.address))
self.prev_node.send_message("PTPB0")
self.prev_node.ptpb_poke = False
self.ptpa_poke = False

# PTPB
if self.data[3] == 'B':
# We get a PTPB data

if (self.data[4] == '1'):
if (self.next_node != None):
print("\t\tPTPA1 val sent to the node",
str(self.next_node.address))
self.next_node.send_message("PTPA1")
print("\t\tPTPB1 val sent back to the node",
str(self.address))
self.send_message("PTPB1")
self.next_node.ptpa_poke = True
self.ptpb_poke = True
else:
print("\t\tPTPB0 val sent back to the node",
str(self.address))
self.send_message("PTPB0")

if (self.data[4] == '0' and self.ptpb_poke == True and self.next_node != None):
print("\t\tPTPA0 val sent to the node",
str(self.next_node.address))
self.next_node.send_message("PTPA0")
self.next_node.ptpa_poke = False
self.ptpb_poke = False

else:
# This is a broadcast message
print(str(self.msg_index)+" Data received from " + str(self.address))
self.msg_index += 1
for client in clients:
if client != self:
client.send_message(self.data)

def connected(self):
print(self.address, 'connected')
clients.append(self)
# Save links to other nodes
if len(clients) >= 2:
self.prev_node = clients[-2]
self.prev_node.next_node = clients[-1]
print("connect PTPB of " + str(self.prev_node.address) +
" with PTPA of " + str(self.address))

def handle_close(self):
print(self.address, 'closed')
# Save links to other nodes
if self.next_node != None:
self.next_node.prev_node = self.prev_node
if self.prev_node != None:
self.prev_node.next_node = self.next_node
clients.remove(self)


## Parse arguments ##
parser = argparse.ArgumentParser(description='Robus WebSocket emulator broker\n',
formatter_class=argparse.RawTextHelpFormatter)
# General arguments
parser.add_argument("-p", "--port", metavar="PORT", action="store",
help="The port used by the websocket.\n"
"By default port = 8000.\n",
default=8000)
parser.add_argument("--ip", metavar="IP", action="store",
help="The ip used by the websocket.\n"
"By default ip = '127.0.0.1'.\n",
default='127.0.0.1')

args = parser.parse_args()
clients = []

server = WebSocketServer(args.ip, args.port, RobusEmulator)
print("WebSocket Robus emulation is deprecaated since Luos_engine 3.1.0, please consider using a WebSocket network instead.\n")
print("WebSocket Robus emulation opened on " +
str(args.ip) + ":" + str(args.port))
server.serve_forever()
85 changes: 85 additions & 0 deletions pyluos/tools/ws_broker.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
from simple_websocket_server import WebSocketServer, WebSocket
import json
import argparse

PING = 0
END = 1
OK = 2
NOK = 3

class WsBroker(WebSocket):
pinged = False
next_node = None
global pinger

def handle(self):
if len(self.data) == 1:
if len(pinger) == 0:
# Data should be a ping
if self.data[0] != PING:
print("Error: received data is not a ping, received data is " + str(self.data[0]))
else:
pinger.append(self)
find_someone = False
# This is a ping command, find the next unpinged node and ping it
for client in clients:
if client != self and client.pinged == False:
# We have someone to ping
find_someone = True
client.pinged = True
client.send_message([PING])
# ack the ping to the sender
self.send_message([OK])
break
if find_someone == False:
# We have no one to ping, this branch is finished, we can send a NOK to this ping and reset the pinged state of all nodes
self.send_message([NOK])
pinger.remove(pinger[0])
for client in clients:
client.pinged = False
else:
# Data should be an end
if self.data[0] != END:
print("Error: received data is not an end, received data is " + str(self.data[0]) + " from " + str(self.address))
else:
# send the end to the pinger
pinger[0].send_message([END])
#remove the pinger
pinger.remove(pinger[0])
else:
# This is a broadcast message
#print(str(len(self.data)) + str(" Data received from " + str(self.address)))
for client in clients:
if client != self:
client.send_message(self.data)

def connected(self):
print(self.address, 'connected\n')
clients.append(self)

def handle_close(self):
print(self.address, 'closed')
clients.remove(self)


## Parse arguments ##
parser = argparse.ArgumentParser(description='Luos_engine WebSocket network broker\n',
formatter_class=argparse.RawTextHelpFormatter)
# General arguments
parser.add_argument("-p", "--port", metavar="PORT", action="store",
help="The port used by the websocket.\n"
"By default port = 8000.\n",
default=8000)
parser.add_argument("--ip", metavar="IP", action="store",
help="The ip used by the websocket.\n"
"By default ip = '127.0.0.1'.\n",
default='127.0.0.1')

args = parser.parse_args()
clients = []
pinger = []

server = WebSocketServer(args.ip, args.port, WsBroker)
print("Luos_engine WebSocket network broker opened on " +
str(args.ip) + ":" + str(args.port))
server.serve_forever()
7 changes: 5 additions & 2 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@
'anytree',
'crc8',
'ipython',
'requests'
'requests',
'simple_websocket_server==0.4.2'
],
extras_require={
'tests': ['pytest', 'flake8'],
Expand All @@ -44,7 +45,9 @@
'pyluos-usb2ws = pyluos.tools.usb2ws:main',
'pyluos-bootloader = pyluos.tools.bootloader:main',
'pyluos-shell = pyluos.tools.shell:main',
'pyluos-discover = pyluos.tools.discover:main'
'pyluos-discover = pyluos.tools.discover:main',
'pyluos-ws-broker = pyluos.tools.ws_broker:main',
'pyluos-robus-broker = pyluos.tools.robus_broker:main'
],
},
)

0 comments on commit ebff67b

Please sign in to comment.