Preface
Goal: Monitor excel file changes and show the result in browser using websocket.
This is a multiparts article.
8: Multiple Connection Demo
Using different ports.
Remember the connection?
WebSocket("ws://localhost:8765")
Sure we can add more port, to serve more user.
Spreadsheet File
Excel as Data Source
Instead of one data source, we can obtain data from two sources.
You can download the sources here
We need to display both data in one HTML page in a browser.
Python Source Code
The source is very similar as previous code.
Class Skeleton
The same as previous script.
The only different is the port and data source.
example = Xl2WebExample(
'/home/epsi/awatch', 'test-b.xlsx',
'localhost', 8767)
asyncio.run(example.main())
Prepare in CLI
Run both script, because we need to display two panel in our html page.
Sender 16a
❯ python 16a-request.py
Sender 17b
❯ python 17b-request.py
We will get the result later, after we load the page in web browser.
Web Source Code: HTML
Client Side
Load both javascript, that each have differen websocket port.
<head>
<title>Mission Accomplished</title>
<link rel="stylesheet" href="bulma.min.css">
<script src="16a-script.js"></script>
<script src="17b-script.js"></script>
</head>
Web Source Code: Javascript
// Loop Entry Point
function startWS(){
const websocket = new WebSocket("ws://localhost:8767");
websocket.onopen = function () {...};
websocket.onmessage = function(event) {...};
websocket.onclose = function() {...};
}
const time_b = document.getElementById("time-b");
const status_b = document.getElementById("status-b");
const value_b1 = document.getElementById("value-b1");
const value_b2 = document.getElementById("value-b2");
const value_b3 = document.getElementById("value-b3");
Now our page is ready to be launched.
Browser Result
Right click your HTML file from your file manager using right click, and open it using any web browser.
The page should contained data from both excel sources.
Class: Init
Additional connections
property,
initialized as empty set.
class Xl2WebExample:
def __init__(self, filepath, filename, site, port):
# save initial parameter
self.filepath = filepath
self.filename = filename
self.site = site
self.port = port
# websocket broadcast collection
self.connections = set()
![Python: Broadcast: Init][13b-py-init]
Output in CLI
Again check both script, for any logs.
Sender 16a
❯ python 16a-request.py
Hello There.
Timestamp : Wed Jan 4 11:21:38 2023
File Modified : None
Data 1: 1700
Data 2: 1500
Data 3: 2023
Sender 17b
❯ python 17b-request.py
Hello There.
Timestamp : Wed Jan 4 11:21:38 2023
File Modified : None
Data 1: 1234
Data 2: 2017
Data 3: 1105
Windows User
For comfort in windows environment,
you can make a batch file such as run.bat
,
that contain these two lines:
start cmd /k python 16a-request.py
start cmd /k python 17b-request.py
Rookie Issue
Multiple connection from different client.
There is still unsolved issue however.
In this figure below,#### Class: Init
Additional connections
property,
initialized as empty set.
class Xl2WebExample:
def __init__(self, filepath, filename, site, port):
# save initial parameter
self.filepath = filepath
self.filename = filename
self.site = site
self.port = port
# websocket broadcast collection
self.connections = set()
![Python: Broadcast: Init][13b-py-init] the connection can only be read by only one client. To be precise, the last one. Ignoring the previous websocket.
This means, when I open other client, such as other tab in the same browser, the page in web browser, would not be updated to the latest value. Even when the connection status opened, the page won’t receive any data.
The problems lies here. Remember that, we are dealing with concurrency. Setting class property as below, will override previous value. Thus we got the latest websocket.
# websocket handler
async def __handler(self, websocket, path):
self.websocket = websocket
What we should do is, using function argument, instead. Propagate the websocket for each process.
async def __handler(self, websocket, path):
task_localfile = asyncio.create_task(
self.__monitor_localfile(websocket))
task_webclient = asyncio.create_task(
self.__monitor_webclient(websocket))
# run these two coroutines concurrently
await(task_localfile)
await(task_webclient)
This is not about, broadcast vs send, but rather switching paradigm to concurrency.
What is Next 🤔?
It works on my computer
We are done. Not perfect, but usable enough. But I haven’t test this in network, nor in real TV.
I have something to do these days, I will be back next week, with tidier code. Separating generic logic flow and custom data setting.
Consider continue reading [ Excel - Monitor - Python Inheritance ].