FastAPI StreamingResponse with picamera2 - browser refresh problem - raspberry-pi

I'm trying to make FastAPI server which streams MJPEG from Raspberry Pi via picamera2 library. It works, but when I reload browser on /mjpeg multiple times, it will stuck. But with this example it works perfectly - you can reload browsers as many times as you want. Can you spot the problem?
For server I'm using uvicorn with default settings.
Thanks!
import io
import os
from threading import Condition
from fastapi import FastAPI, Request
from fastapi.middleware.cors import CORSMiddleware
from picamera2 import Picamera2
from picamera2.encoders import JpegEncoder
from picamera2.outputs import FileOutput
from fastapi.responses import HTMLResponse, StreamingResponse
app = FastAPI()
app.add_middleware(
CORSMiddleware,
allow_origins="http://localhost:8000",
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
class StreamingOutput(io.BufferedIOBase):
def __init__(self):
self.frame = None
self.condition = Condition()
def write(self, buf):
with self.condition:
self.frame = buf
self.condition.notify_all()
picam2 = Picamera2()
picam2.configure(picam2.create_video_configuration(main={"size": (640, 480)}))
output = StreamingOutput()
picam2.start_recording(JpegEncoder(), FileOutput(output))
def get_frame():
try:
while True:
with output.condition:
output.condition.wait()
frame = output.frame
yield (
b"--frame\r\n" b"Content-Type: image/jpeg\r\n\r\n" + frame + b"\r\n"
)
except Exception as e:
print("Error! Frames")
#app.get("/mjpeg", response_class=StreamingResponse)
def mjpeg(request: Request):
try:
frames = get_frame()
response = StreamingResponse(
frames,
headers={
"Cache-Control": "no-cache, private",
"Pragma": "no-cache",
},
media_type="multipart/x-mixed-replace; boundary=frame",
)
return response
except Exception as e:
print("Error! Route")

Related

Why does kivy keep freezing when using python sockets?

I'm working on a basic client-server desktop app project using kivy & sockets & threading.
The client & server work on it's own, however when I try to integrate it with kivy, python & kivy don't want to respond & yet no definitive error pops up.
Could i have some ideas as to how to fix this?
This is the code that freezes when i run it, if i take away the import server_sock it works as a general gui and doesnt freeze.
import kivy
from kivy.app import App
from kivy.uix.label import Label
from kivy.uix.gridlayout import GridLayout
from kivy.uix.textinput import TextInput
from kivy.uix.button import Button
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.clock import Clock
import server_sock
import sys
kivy.require("2.1.0") #latest version
class ConnectPage(GridLayout):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.cols = 2
self.add_widget(Label(text="IP:"))
self.ip = TextInput(multiline=False)
self.add_widget(self.ip)
self.add_widget(Label(text="PORT:"))
self.port = TextInput(multiline=False)
self.add_widget(self.port)
self.add_widget(Label(text="USERNAME:"))
self.user = TextInput(multiline=False)
self.add_widget(self.user)
self.join = Button(text="Join")
self.join.bind(on_press=self.join_button)
self.add_widget(Label())
self.add_widget(self.join)
def join_button(self, instance):
port = self.port.text
ip = self.ip.text
user = self.user.text
info = f"Attempting to join {ip}:{port} as {user}"
chat_app.info_page.update_info(info)
chat_app.screen_manager.current = "Info"
Clock.shedule_once(self.connect,1)
def connect(self, _):
port = int(self.port.text)
ip = self.ip.text
user = self.user.text
try:
server_sock.connect(ip, port)
chat_app.create_chat_page()
chat_app.screen_manager.current = "Chat"
except:
show_error(message="not gonna happen")
class InfoPage(GridLayout):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.cols = 1
self.message = Label(halign="center", valign="middle", font_size="30")
self.message.bind(width=self.update_text_width)
self.add_widget(self.message)
def update_info(self,message):
self.message.text = message
def update_text_width(self, *_):
self.message.text_size = (self.message.width*0.9, None)
class ChatPage(GridLayout):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.cols = 1
self.add_widget(Label(text="Hey at least it works till now"))
class ChatApp(App):
def build(self):
self.screen_manager = ScreenManager()
self.connect_page = ConnectPage()
screen = Screen(name="Connect")
screen.add_widget(self.connect_page)
self.screen_manager.add_widget(screen)
self.info_page = InfoPage()
screen= Screen(name="Info")
screen.add_widget(self.info_page)
self.screen_manager.add_widget(screen)
return self.screen_manager
def create_chat_page(self):
self.chat_page = ChatPage()
screen = Screen(name="Chat")
screen.add_widget(self.chat_page)
self.screen_manager.add_widget(screen)
def show_error(message):
chat_app.info_page.update_info(message)
chat_app.screen_manager.current = "Info"
Clock.shedule_once(sys.exit, 10)
if __name__ == "__main__":
chat_app =ChatApp()
chat_app.run()
This is the server_sock file
import socket
import threading
import socket
HOST = '127.0.0.1'
PORT = 55555
server = socket.socket(
socket.AF_INET,
socket.SOCK_STREAM
)
try:
server.bind((HOST, PORT))
except:
print(f'unable to bind to {HOST} and {PORT}')
server.listen()
print(f"Listening for connections on {HOST}: {PORT}")
clients = []
nicknames = []
def broadcast(message):
for client in clients:
client.send(message)
def message_recv(client):
while True:
try:
message = client.recv(2048)
broadcast(message)
except:
index = clients.index(client)
clients.remove(client)
nickname = nicknames[index]
broadcast(f'{nickname} left the chat'.encode('ascii'))
nicknames.remove(nickname)
break
def recieve():
while True:
client, address = server.accept()
print(f"Connected with {str(address)}")
client.send("SOMETHING".encode('ascii'))
nickname = client.recv(2048).decode('ascii')
nicknames.append(nickname)
clients.append(client)
print(f"Nickname of the client is {nickname}")
broadcast(f"{nickname} joined the chat".encode('ascii'))
client.send("Connected to the server".encode('ascii'))
thread = threading.Thread(target=message_recv, args=(client,))
thread.start()
print("Server is listening")
recieve()

pymodbus read_input_registers error : ModbusIOException' object has no attribute 'registers'

I'm using pymodbus (v3.0.2) on ubuntu linux server 22.04
using to read modbus rtu device (masibus datalogger)
when I'm trying to read input register, it some time returning the data which is correct, and most of the time giving this error
"ModbusIOException' object has no attribute 'registers'
here is my code to
#!/usr/bin/python3
import time
import datetime
from datetime import timedelta
import mysql.connector
from mysql.connector import Error
from pymodbus.client import ModbusSerialClient
client = ModbusSerialClient(
method="rtu",
port="/dev/ttyUSB0",
stopbits=2,
bytesize=8,
parity="N",
baudrate=9600)
connection = client.connect()
if connection is True:
print("Modbus Connection Successful")
def read_mbr():
readreg = client.read_input_registers(0, 15, unit=1).registers
# print(f'Data = {readreg}')
return readreg
while(1):
try:
data = read_mbr()
print(data)
except Exception as e:
print(e)
time.sleep(1)
time.sleep(1)
I've tried to add more time detail, but did not worked.

ChannelsLiveServerTestCase equivalent for pytest

In pytest-django there is a builtin fixture live_server though it seems like this server (that is actually based on LiveServerTestCase) can't handle web-sockets or at least won't interact with my asgi.py module.
How can one mimic that fixture in order to use ChannelsLiveServerTestCase instead? Or anything else that will run a test-database and will be able to serve an ASGI application?
My goal eventually is to have as close to production environment as possible, for testing and being able to test interaction between different Consumers.
P.S: I know I can run manage.py testserver <Fixture> on another thread / process by overriding django_db_setup though I seek for a better solution.
You can implement a channels_live_server fixture based on the implementations of:
live_server fixture, which instantiates
LiveServer helper, which starts LiveServerThread, and
ChannelsLiveServerTestCase, which starts DaphneProcess.
#medihack implemented it at https://github.com/pytest-dev/pytest-django/issues/1027:
from functools import partial
from channels.routing import get_default_application
from daphne.testing import DaphneProcess
from django.contrib.staticfiles.handlers import ASGIStaticFilesHandler
from django.core.exceptions import ImproperlyConfigured
from django.db import connections
from django.test.utils import modify_settings
def make_application(*, static_wrapper):
# Module-level function for pickle-ability
application = get_default_application()
if static_wrapper is not None:
application = static_wrapper(application)
return application
class ChannelsLiveServer:
host = "localhost"
ProtocolServerProcess = DaphneProcess
static_wrapper = ASGIStaticFilesHandler
serve_static = True
def __init__(self) -> None:
for connection in connections.all():
if connection.vendor == "sqlite" and connection.is_in_memory_db():
raise ImproperlyConfigured(
"ChannelsLiveServer can not be used with in memory databases"
)
self._live_server_modified_settings = modify_settings(ALLOWED_HOSTS={"append": self.host})
self._live_server_modified_settings.enable()
get_application = partial(
make_application,
static_wrapper=self.static_wrapper if self.serve_static else None,
)
self._server_process = self.ProtocolServerProcess(self.host, get_application)
self._server_process.start()
self._server_process.ready.wait()
self._port = self._server_process.port.value
def stop(self) -> None:
self._server_process.terminate()
self._server_process.join()
self._live_server_modified_settings.disable()
#property
def url(self) -> str:
return f"http://{self.host}:{self._port}"
#pytest.fixture
def channels_live_server(request):
server = ChannelsLiveServer()
request.addfinalizer(server.stop)
return server
#aaron's solution can't work, due to pytest-django conservative approach for database access.
another proccess wouldn't be aware that your test has database access permissions therefore you won't have database access. (here is a POC)
Using a scoped fixture of daphne Server suffice for now.
import threading
import time
from functools import partial
from django.contrib.staticfiles.handlers import ASGIStaticFilesHandler
from django.core.exceptions import ImproperlyConfigured
from django.db import connections
from django.test.utils import modify_settings
from daphne.server import Server as DaphneServer
from daphne.endpoints import build_endpoint_description_strings
def get_open_port() -> int:
import socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind(("", 0))
s.listen(1)
port = s.getsockname()[1]
s.close()
return port
def make_application(*, static_wrapper):
# Module-level function for pickle-ability
if static_wrapper is not None:
application = static_wrapper(your_asgi_app)
return application
class ChannelsLiveServer:
port = get_open_port()
host = "localhost"
static_wrapper = ASGIStaticFilesHandler
serve_static = True
def __init__(self) -> None:
for connection in connections.all():
if connection.vendor == "sqlite" and connection.is_in_memory_db():
raise ImproperlyConfigured(
"ChannelsLiveServer can not be used with in memory databases"
)
self._live_server_modified_settings = modify_settings(ALLOWED_HOSTS={"append": self.host})
self._live_server_modified_settings.enable()
get_application = partial(
make_application,
static_wrapper=self.static_wrapper if self.serve_static else None,
)
endpoints = build_endpoint_description_strings(
host=self.host, port=self.port
)
self._server = DaphneServer(
application=get_application(),
endpoints=endpoints
)
t = threading.Thread(target=self._server.run)
t.start()
for i in range(10):
time.sleep(0.10)
if self._server.listening_addresses:
break
assert self._server.listening_addresses[0]
def stop(self) -> None:
self._server.stop()
self._live_server_modified_settings.disable()
#property
def url(self) -> str:
return f"ws://{self.host}:{self.port}"
#property
def http_url(self):
return f"http://{self.host}:{self.port}"
#pytest.fixture(scope='session')
def channels_live_server(request, live_server):
server = ChannelsLiveServer()
request.addfinalizer(server.stop)
return server

Python Faust await agent.ask() doesn't return reply and hangs function calling it

I am new to Python, playing around with stuff, trying to communicate python services via Kafka using Faust.
So I have small PoC project.
Faust app definition:
# app.py
import faust as f
from models import ReadRequest, ReadResponse
app = f.App("faust-app", broker="kafka://localhost:9092", store="rocksdb://")
topics = {"read-request": app.topic("read-request", value_type=ReadRequest)}
def get_app() -> f.types.AppT:
return app
def get_topic(name: str) -> f.types.TopicT:
return topics[name]
My DB reader agent:
# reader.py
import pandas as pd
from pymongo import MongoClient
from app import get_app, get_topic
client = MongoClient()
app = get_app()
req_topic = get_topic("read-request")
#app.agent(req_topic)
async def read_request(requests):
async for request in requests:
db = client.test
coll = db[request["collection"]]
result = coll.find(request["query"])
df = pd.DataFrame(result)
response = {
"id": request["id"],
"data": list(df.loc[:, df.columns != "_id"].to_dict(orient="records")),
}
print(response) # debug <1>
yield response
Model definitions:
# models.py
import faust as f
class ReadRequest(f.Record):
id: int
collection: str
query: dict
Test agent.ask()
# test.py
import asyncio
from reader import read_request
from models import ReadRequest
async def run():
result = await read_request.ask(ReadRequest(id=1, collection="test", query={}))
print(result) # debug <2>
if __name__ == "__main__":
asyncio.run(run())
So I have zookeeper, kafka server, mongodb and faust worker reader running. Everything is using out of the box configs.
When I run python3 test.py I see debug <1> print output as expected, but debug <2> never goes off and execution hangs there.
Faust docs say
So I assume that I am doing everything right.
Anyone has any clues?

flask/MongoDB error on local server using raspberry pi3 - raspbian os

i've made a local server using flask and mongoDB which works great on windows, but when i moved my code to the raspberry pi, i've got an error which i couldn't figure out why it occurs.
the code im using:
1) for the flask server
from flask import Flask
from flask import jsonify
from flask import request
import pymongo
import time
import datetime
import json
app = Flask(__name__)
client = pymongo.MongoClient("localhost", 27017)
db = client['mqtt-db']
obs_collection = db['mqtt-collection']
#app.route("/obs")
def obs():
data_str = request.args.get("data")
print data_str
data = json.loads(data_str)
print data
data["date"] = datetime.datetime.now()
obs_collection.save(data)
return "success"
#app.route("/get_obs")
def get_obs():
res = []
for row in obs_collection.find():
del row['_id']
res.append(row)
return jsonify(res)
#app.route("/delete_all")
def delete_all():
res = obs_collection.delete_many({})
return jsonify({"deleted": res.deleted_count})
if __name__ == "__main__":
app.run(host="0.0.0.0", debug=True)
2) script for inserting messages into db , using mqtt protocol:
import paho.mqtt.client as mqtt
import pymongo
import json
import datetime
topic = "sensor"
host = "10.0.0.6"
client = pymongo.MongoClient("localhost", 27017)
db = client['mqtt-db']
mqtt_collection = db['mqtt-collection']
# The callback for when the client receives a CONNACK response from the server.
def on_connect(client, userdata, flags, rc):
print("Connected with result code "+str(rc))
# Subscribing in on_connect() means that if we lose the connection and
# reconnect then subscriptions will be renewed.
client.subscribe(topic)
# The callback for when a PUBLISH message is received from the server.
def on_message(client, userdata, msg):
data_str = str(msg.payload)
data = json.loads(data_str)
print data_str
print data
data["date"] = datetime.datetime.now()
mqtt_collection.save(data)
print(msg.topic+" "+str(msg.payload))
client = mqtt.Client()
client.on_connect = on_connect
client.on_message = on_message
client.connect(host, 1883, 60)
# Blocking call that processes network traffic, dispatches callbacks and
# handles reconnecting.
# Other loop*() functions are available that give a threaded interface and a
# manual interface.
client.loop_forever()
the error occurs when i try to retrieve data from the server using "get_obs" function.
the error is: "Value Error: dictionary update sequence element #0 has length 4; 2 is required"
appreciate your help.
as #davidism suggested, the solution was to update to the latest version of Flask