Compare commits

...

3 Commits

Author SHA1 Message Date
James Pace 7f9ca762be Add identiy and version number. Some generic cleanup. 2026-09-16 19:51:01 -04:00
James Pace 991a44b332 Support multiple videos. 2026-09-12 13:17:55 +00:00
James Pace 2d5e570625 Fix login pagename. 2026-08-27 08:46:50 -04:00
8 changed files with 118 additions and 17 deletions

View File

@ -33,11 +33,13 @@ class Api:
web.get('/api/ping', self.ping),
web.get('/api/uptime', self.uptime),
web.get('/api/build_info', self.build_info),
web.get('/api/version_number', self.version_number),
web.get('/api/env', self.env),
web.get('/api/status', self.status),
web.get('/api/identity', self.identity),
web.get("/api/position", self.position),
web.get("/api/diagnostics", self.diagnostics),
web.post("/api/login", self.login),
web.get("/api/available_streams", self.available_streams),
web.post("/api/mediamtx/auth", self.mediamtx_auth),
web.get("/api/mediamtx/login", self.mediamtx_login),
web.static("/assets", ui_static_directory),
@ -133,6 +135,17 @@ class Api:
resp = {"uptime": self._facts.get_uptime()}
return web.json_response(resp)
async def version_number(self, request):
version = self._facts.get_version()
if not version:
resp = {"status": False, "message": "version not found."}
return web.json_response(resp)
resp = {
"status": True,
"version": version
}
return web.json_response(resp)
async def build_info(self, request):
project_state = self._facts.get_buildinfo()
if not project_state:
@ -148,11 +161,11 @@ class Api:
env = self._facts.get_env()
return web.json_response(env)
async def status(self, request):
status = self._facts.get_status()
if not status:
status = "Nothing received!"
resp = {"message": status}
async def identity(self, request):
identity = self._facts.get_identity()
if not identity:
identity = "Nothing received!"
resp = {"message": identity}
return web.json_response(resp)
async def position(self, request):
@ -176,3 +189,9 @@ class Api:
resp["message"] = diagnostics
return web.json_response(resp)
async def available_streams(self, request):
resp = {}
resp["streams"] = self._facts.get_video_streams()
resp["port"] = self._facts.get_video_display_port()
return web.json_response(resp)

View File

@ -30,7 +30,7 @@ class Facts:
rclpy.init()
self._node = rclpy.create_node('am_i_up')
self._status_sub = self._node.create_subscription(String, "status", self._status_callback, 1)
self._ident_sub = self._node.create_subscription(String, "identity", self._identity_callback, 1)
self._navsat_sub = self._node.create_subscription(NavSatFix, "navsatfix", self._navsat_callback, 1)
self._diagnostics_sub = self._node.create_subscription(DiagnosticArray, "/diagnostics_agg", self._diag_callback, 1)
@ -39,7 +39,10 @@ class Facts:
self._password = self._node.declare_parameter("password", "password").value
self._status_string = None
self._video_streams = self._node.declare_parameter("video_streams", [""]).value
self._video_display_port = self._node.declare_parameter("video_display_port", "8889").value
self._identity_string = None
self._lat_long = None
self._diag_msg = None
@ -53,8 +56,8 @@ class Facts:
def get_lat_long(self):
return self._lat_long
def get_status(self):
return self._status_string
def get_identity(self):
return self._identity_string
def get_listen_port(self):
return self._listen_port
@ -116,11 +119,37 @@ class Facts:
print("Can't find build info.\n{}".format(e))
return project_state_content
def get_version(self):
# Find the share directory for 'build_info_getter'.
build_info_getter_directory = None
try:
build_info_getter_directory = get_package_share_directory('build_info_getter')
except Exception as e:
print("Can't find build info.\n{}".format(e))
return None
# Find and read the version file in int.
version_number_file = build_info_getter_directory + "/version_number"
version_number = None
try:
with open(version_number_file, 'r') as file_obj:
version_number = file_obj.readline().rstrip()
except Exception as e:
# We either didn't load the file or couldn't read it.
print("Can't find build info.\n{}".format(e))
return version_number
def get_password(self):
return self._password
def _status_callback(self, msg):
self._status_string = msg.data
def get_video_streams(self):
return self._video_streams
def get_video_display_port(self):
return self._video_display_port
def _identity_callback(self, msg):
self._identity_string = msg.data
def _diag_callback(self, msg):
self._diag_msg = msg

View File

@ -22,7 +22,6 @@ def main(args=None):
parser.add_argument("--host", default="http://localhost:8000")
args = parser.parse_args()
client = Client(args.host)
if args.action == 'ping':
@ -35,6 +34,10 @@ def main(args=None):
asyncio.run(client.call_env())
if args.action == 'diagnostics':
asyncio.run(client.call_diagnostics())
if args.action == 'identity':
asyncio.run(client.call_identity())
if args.action == 'version':
asyncio.run(client.call_version())
class Client():
def __init__(self, host):
@ -83,6 +86,22 @@ class Client():
async with session.get(f'{self._host}/api/env') as resp:
print(await resp.text())
async def call_identity(self):
print("Calling identity.")
async with aiohttp.ClientSession() as session:
await self.login(session)
async with session.get(f'{self._host}/api/identity') as resp:
print(await resp.text())
async def call_version(self):
print("Calling version.")
async with aiohttp.ClientSession() as session:
await self.login(session)
async with session.get(f'{self._host}/api/version_number') as resp:
print(await resp.text())
async def call_diagnostics(self):
print("Calling diagnostics.")

View File

@ -3,7 +3,7 @@
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Bootstrap HTML Example</title>
<title>Login</title>
<link href="server_assets/bootstrap.css" rel="stylesheet">
<style>
.vert-padded {
@ -27,7 +27,7 @@
</div>
<script>
console.log("Hello");
console.log("Hi Developer! This page lives with the server package NOT the ui package.");
document.getElementById('loginForm').addEventListener('submit', async (event) => {
console.log("submit");
event.preventDefault();

View File

@ -6,9 +6,23 @@
<param from="$(find-pkg-share am_i_up)/params/image2rtsp.yaml"/>
</node>
<!-- j7s threed -->
<node pkg="data_simulator" exec="frame_publisher" name="frame_publisher" />
<node pkg="j7s_threed" exec="j7s_threed_node" name="threed_node">
<param from="$(find-pkg-share j7s_threed)/params/threed_world.yaml" allow_substs="true"/>
<remap from="image" to="threed_image"/>
</node>
<node pkg="image2rtsp" exec="image2rtsp" name="image2rtsp">
<env name="GST_DEBUG" value=""/> <!-- Setting to 3 can help if its not working -->
<param from="$(find-pkg-share am_i_up)/params/j7s_threed_image2rtsp.yaml"/>
</node>
<!-- Vehicle Position -->
<node pkg="data_simulator" exec="position_publisher" name="position_publisher" />
<!-- Identity -->
<node pkg="data_simulator" exec="identity_publisher" name="identity_publisher" />
<!-- Diagnostics -->
<node pkg="data_simulator" exec="diagnostic_publisher" name="diagnostic_1">
<param name="diag_level" value="OK"/>

View File

@ -1,4 +1,8 @@
/**:
ros__parameters:
ui_pkg: "am_i_up_ui"
listen_port: "8000"
listen_port: "8000"
video_display_port: "8889"
video_streams:
- image
- threed_image

View File

@ -0,0 +1,14 @@
/**:
ros__parameters:
topic: "threed_image"
mountpoint: "/threed_image"
port: "8560"
local_only: True
default_pipeline: |
( appsrc name=imagesrc do-timestamp=true min-latency=0 max-latency=0 max-bytes=1000 is-live=true !
videoconvert !
videoscale !
video/x-raw, framerate=30/1, width=1280, height=720 !
x264enc tune=zerolatency bitrate=500 key-int-max=30 !
video/x-h264, profile=baseline !
rtph264pay name=pay0 pt=96 )

View File

@ -3,4 +3,6 @@ authHTTPAddress: http://127.0.0.1:8000/api/mediamtx/auth
authHTTPExclude: []
paths:
image:
source: rtsp://127.0.0.1:8559/image
source: rtsp://127.0.0.1:8559/image
threed_image:
source: rtsp://127.0.0.1:8560/threed_image