Compare commits
26 Commits
0d0729cb99
...
station2-c
| Author | SHA1 | Date | |
|---|---|---|---|
| b75e4b270b | |||
| a054e368cb | |||
| db1a261012 | |||
| 7b318831f2 | |||
| 48c532a81c | |||
| c302674958 | |||
| c2df56e15e | |||
| 93fd03e717 | |||
| 3682fc1448 | |||
| f67379f85d | |||
| ac4ff7a657 | |||
| e94f342739 | |||
| e421634432 | |||
| fba81ef345 | |||
| 888ed4db9d | |||
| e89b964d27 | |||
| d74b40b7e2 | |||
| 05e5785fc0 | |||
| 716f01a5be | |||
| 5fad6f294c | |||
| 9a771e8cd4 | |||
| 8a54cedb5b | |||
| 8e3b3abe8a | |||
| b50cfeaac8 | |||
| 8ac68b6ab0 | |||
| 502822a7a1 |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -1,5 +1,7 @@
|
|||||||
# Python
|
# Python
|
||||||
__pycache__
|
__pycache__
|
||||||
|
Pipfile
|
||||||
|
Pipfile.lock
|
||||||
|
|
||||||
# CAD
|
# CAD
|
||||||
*.stl
|
*.stl
|
||||||
|
|||||||
BIN
CAD/StationA_CAD/StationA_Stack_Base.FCStd
Normal file
BIN
CAD/StationA_CAD/StationA_Stack_Base.FCStd
Normal file
Binary file not shown.
BIN
companion_software/FreeMono.ttf
Normal file
BIN
companion_software/FreeMono.ttf
Normal file
Binary file not shown.
22
companion_software/README.md
Normal file
22
companion_software/README.md
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
Setting up getty override
|
||||||
|
=========================
|
||||||
|
|
||||||
|
```console
|
||||||
|
systemctl edit getty@tty1.service
|
||||||
|
```
|
||||||
|
|
||||||
|
add
|
||||||
|
|
||||||
|
```
|
||||||
|
[Service]
|
||||||
|
ExecStart=
|
||||||
|
ExecStart=-/opt/lewis-crawler/companion_software/dashboard/entrypoint.sh
|
||||||
|
StandardInput=tty
|
||||||
|
StandardOutput=tty
|
||||||
|
```
|
||||||
|
|
||||||
|
then
|
||||||
|
|
||||||
|
```
|
||||||
|
systemctl daemon-reload; systemctl restart getty@tty1.service
|
||||||
|
```
|
||||||
9
companion_software/entrypoint.sh
Executable file
9
companion_software/entrypoint.sh
Executable file
@@ -0,0 +1,9 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
cd /opt/lewis-crawler/companion_software
|
||||||
|
|
||||||
|
git pull
|
||||||
|
|
||||||
|
pipenv install -r requirements.txt
|
||||||
|
|
||||||
|
pipenv run python -m houston -v
|
||||||
43
companion_software/houston/__main__.py
Normal file
43
companion_software/houston/__main__.py
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
# Lewis Crawler
|
||||||
|
# 2021 - 2022
|
||||||
|
# Kitsune Scientific
|
||||||
|
|
||||||
|
import logging
|
||||||
|
import argparse
|
||||||
|
import verboselogs
|
||||||
|
import coloredlogs
|
||||||
|
|
||||||
|
from houston.houston import Houston
|
||||||
|
|
||||||
|
|
||||||
|
# Get debug args
|
||||||
|
parser = argparse.ArgumentParser()
|
||||||
|
parser.add_argument(
|
||||||
|
'-d', '--debug',
|
||||||
|
help="Print lots of debugging statements",
|
||||||
|
action="store_const", dest="loglevel", const='DEBUG',
|
||||||
|
default='WARNING',
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'-v', '--verbose',
|
||||||
|
help="Be verbose",
|
||||||
|
action="store_const", dest="loglevel", const='INFO',
|
||||||
|
)
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
# Install verbose logs
|
||||||
|
verboselogs.install()
|
||||||
|
|
||||||
|
# Create a logger object.
|
||||||
|
logger = logging.getLogger('Houston_Log')
|
||||||
|
|
||||||
|
# Install colored logs
|
||||||
|
coloredlogs.install(level=args.loglevel,
|
||||||
|
logger=logger,
|
||||||
|
fmt='%(asctime)s,%(msecs)03d %(hostname)s %(levelname)s %(message)s') # noqa: E501
|
||||||
|
|
||||||
|
logger.info('Lewis Companion Software Started.')
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
app = Houston(logger)
|
||||||
|
app.run()
|
||||||
30
companion_software/houston/houston.py
Normal file
30
companion_software/houston/houston.py
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
# Lewis Crawler
|
||||||
|
# 2021 - 2022
|
||||||
|
# Kitsune Scientific
|
||||||
|
|
||||||
|
import time
|
||||||
|
|
||||||
|
from houston.robotstreamer.streamer import RobotStreamer
|
||||||
|
|
||||||
|
|
||||||
|
class Houston:
|
||||||
|
def __init__(self, logger):
|
||||||
|
# Setup logger
|
||||||
|
self.log = logger
|
||||||
|
|
||||||
|
# Setup robotstreamer
|
||||||
|
self.rs = RobotStreamer(self.log)
|
||||||
|
|
||||||
|
# We're ready to go!
|
||||||
|
self.log.success('Ready to robot!')
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
self.rs.run()
|
||||||
|
|
||||||
|
# Junk~
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
self.log.debug('Nothing to do.')
|
||||||
|
time.sleep(1)
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
break
|
||||||
3
companion_software/houston/robotstreamer/__init__.py
Normal file
3
companion_software/houston/robotstreamer/__init__.py
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
# Lewis Crawler
|
||||||
|
# 2021 - 2022
|
||||||
|
# Kitsune Scientific
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 59 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 59 KiB |
@@ -0,0 +1 @@
|
|||||||
|
ffmpeg -f image2 -loop 1 -framerate 25 -video_size 1280x720 -r 25 -i Test-Pattern.jpg -vcodec libx264 -profile:v main -preset:v medium -r 30 -g 60 -keyint_min 60 -sc_threshold 0 -b:v 2500k -maxrate 2500k -bufsize 2500k -sws_flags lanczos+accurate_rnd -acodec aac -b:a 96k -ar 48000 -ac 2 -f flv -maxrate 55k "rtmp://rtmp.robotstreamer.com/live/4619?key=jEquBubjizYDMQNe8nKjLdu88iqFpNe3sVQmGRJ2tzbxd4QJrkSSEZBQhi9UTL3k"
|
||||||
30
companion_software/houston/robotstreamer/streamer.py
Normal file
30
companion_software/houston/robotstreamer/streamer.py
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
# Lewis Crawler
|
||||||
|
# 2021 - 2022
|
||||||
|
# Kitsune Scientific
|
||||||
|
|
||||||
|
import ffmpeg
|
||||||
|
|
||||||
|
|
||||||
|
class RobotStreamer:
|
||||||
|
def __init__(self, logger):
|
||||||
|
self.log = logger
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
self.log.debug('Running RobotStreamer')
|
||||||
|
|
||||||
|
stream = ffmpeg.input(
|
||||||
|
'houston/robotstreamer/resources/Test-Pattern.jpg',
|
||||||
|
f='image2',
|
||||||
|
loop='1',
|
||||||
|
framerate=25,
|
||||||
|
video_size='1280x720')
|
||||||
|
|
||||||
|
stream = ffmpeg.output(
|
||||||
|
stream,
|
||||||
|
'http://robotstreamer.com/<secret>/1280/720',
|
||||||
|
f='mpegts',
|
||||||
|
bf=0,
|
||||||
|
muxdelay=0.001,
|
||||||
|
codec='mpeg1video')
|
||||||
|
|
||||||
|
ffmpeg.run(stream)
|
||||||
BIN
companion_software/images/Test-Pattern.png
Normal file
BIN
companion_software/images/Test-Pattern.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 17 KiB |
@@ -1,6 +0,0 @@
|
|||||||
import json
|
|
||||||
import robot_util
|
|
||||||
|
|
||||||
url = '%s/v1/get_endpoint/jsmpeg_video_capture/%s' % ('https://api.robotstreamer.com', '4618')
|
|
||||||
response = robot_util.getWithRetry(url)
|
|
||||||
print(json.loads(response))
|
|
||||||
79
companion_software/images/main.py
Normal file
79
companion_software/images/main.py
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
import time
|
||||||
|
|
||||||
|
from PIL import Image, ImageDraw, ImageFont
|
||||||
|
|
||||||
|
|
||||||
|
def testScreen(
|
||||||
|
x=1280, y=720,
|
||||||
|
image=None,
|
||||||
|
stripeList=["lightgrey",
|
||||||
|
"yellow",
|
||||||
|
"cyan",
|
||||||
|
"lightgreen",
|
||||||
|
"magenta",
|
||||||
|
"red",
|
||||||
|
"blue"]):
|
||||||
|
|
||||||
|
if image is None:
|
||||||
|
image = Image.new("RGB", (x, y))
|
||||||
|
|
||||||
|
draw = ImageDraw.Draw(image)
|
||||||
|
|
||||||
|
for i, stripe in enumerate(stripeList):
|
||||||
|
draw.rectangle((
|
||||||
|
(x/len(stripeList)) * i, 0,
|
||||||
|
(x/len(stripeList)) * (i + 1), x),
|
||||||
|
fill=stripe)
|
||||||
|
|
||||||
|
draw.rectangle((
|
||||||
|
0, y - (y/3),
|
||||||
|
x, y),
|
||||||
|
fill="black")
|
||||||
|
|
||||||
|
for i, stripe in enumerate(stripeList):
|
||||||
|
if i % 2 != 0:
|
||||||
|
_fill = "black"
|
||||||
|
else:
|
||||||
|
_fill = stripeList[len(stripeList) - i - 1]
|
||||||
|
|
||||||
|
draw.rectangle((
|
||||||
|
(x/len(stripeList)) * i, y - (y/3.1),
|
||||||
|
(x/len(stripeList)) * (i + 1), y - (y/4.9)),
|
||||||
|
fill=_fill)
|
||||||
|
|
||||||
|
draw.rectangle((
|
||||||
|
(x/(len(stripeList)/4)), y - (y/5.3),
|
||||||
|
(0), y),
|
||||||
|
fill="darkblue")
|
||||||
|
|
||||||
|
draw.rectangle((
|
||||||
|
(x/(len(stripeList)/2)), y - (y/5.3),
|
||||||
|
(x/(len(stripeList)/1)), y),
|
||||||
|
fill="white")
|
||||||
|
|
||||||
|
return image
|
||||||
|
|
||||||
|
|
||||||
|
def drawText(image):
|
||||||
|
# get a font
|
||||||
|
fnt = ImageFont.truetype("FreeMono.ttf", 50)
|
||||||
|
# get a drawing context
|
||||||
|
d = ImageDraw.Draw(image)
|
||||||
|
|
||||||
|
# draw multiline text
|
||||||
|
d.multiline_text((10, 10), str(time.time()), font=fnt, fill=(0, 0, 0))
|
||||||
|
|
||||||
|
return image
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
blank_screen = testScreen()
|
||||||
|
|
||||||
|
while True:
|
||||||
|
annotated_image = drawText(blank_screen.copy())
|
||||||
|
|
||||||
|
annotated_image.save('Test-Pattern.png')
|
||||||
|
|
||||||
|
time.sleep(2)
|
||||||
|
|
||||||
|
# testScreen().save('Test-Pattern.png')
|
||||||
@@ -1,126 +0,0 @@
|
|||||||
import os
|
|
||||||
import time
|
|
||||||
import traceback
|
|
||||||
import ssl
|
|
||||||
import urllib.request
|
|
||||||
import getpass
|
|
||||||
import json
|
|
||||||
import _thread
|
|
||||||
|
|
||||||
|
|
||||||
ConfigFilename = "/home/pi/config_" + getpass.getuser() + ".json" #this is never used
|
|
||||||
|
|
||||||
KeepAlivePeriod = 6
|
|
||||||
|
|
||||||
|
|
||||||
def times(lst, number):
|
|
||||||
return [x*number for x in lst]
|
|
||||||
|
|
||||||
|
|
||||||
def getWithRetry(url, secure=True):
|
|
||||||
|
|
||||||
for retryNumber in range(2000):
|
|
||||||
try:
|
|
||||||
print("GET", url)
|
|
||||||
if secure:
|
|
||||||
object = urllib.request.urlopen(url)
|
|
||||||
|
|
||||||
else:
|
|
||||||
ctx = ssl.create_default_context()
|
|
||||||
ctx.check_hostname = False
|
|
||||||
ctx.verify_mode = ssl.CERT_NONE
|
|
||||||
object = urllib.request.urlopen(url, context=ctx)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
break
|
|
||||||
except:
|
|
||||||
print("could not open url", url)
|
|
||||||
traceback.print_exc()
|
|
||||||
time.sleep(2)
|
|
||||||
|
|
||||||
data = object.read()
|
|
||||||
encoding = object.info().get_content_charset('utf-8')
|
|
||||||
return data.decode(encoding)
|
|
||||||
|
|
||||||
|
|
||||||
def getNoRetry(url, secure=True):
|
|
||||||
#socket test has retry
|
|
||||||
try:
|
|
||||||
print("GET", url)
|
|
||||||
if secure:
|
|
||||||
object = urllib.request.urlopen(url)
|
|
||||||
|
|
||||||
else:
|
|
||||||
ctx = ssl.create_default_context()
|
|
||||||
ctx.check_hostname = False
|
|
||||||
ctx.verify_mode = ssl.CERT_NONE
|
|
||||||
object = urllib.request.urlopen(url, context=ctx)
|
|
||||||
|
|
||||||
except:
|
|
||||||
print("could not open url", url)
|
|
||||||
return
|
|
||||||
|
|
||||||
data = object.read()
|
|
||||||
encoding = object.info().get_content_charset('utf-8')
|
|
||||||
return data.decode(encoding)
|
|
||||||
|
|
||||||
|
|
||||||
def sendSerialCommand(ser, command):
|
|
||||||
|
|
||||||
|
|
||||||
print(ser.name) # check which port was really used
|
|
||||||
ser.nonblocking()
|
|
||||||
|
|
||||||
# loop to collect input
|
|
||||||
#s = "f"
|
|
||||||
#print "string:", s
|
|
||||||
print(str(command.lower()))
|
|
||||||
ser.write(command.lower() + "\r\n") # write a string
|
|
||||||
#ser.write(s)
|
|
||||||
ser.flush()
|
|
||||||
|
|
||||||
#while ser.in_waiting > 0:
|
|
||||||
# print "read:", ser.read()
|
|
||||||
|
|
||||||
#ser.close()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def makePOST(url, data):
|
|
||||||
|
|
||||||
params = json.dumps(data).encode('utf8')
|
|
||||||
req = urllib.request.Request(url,
|
|
||||||
data=params,
|
|
||||||
headers={'content-type': 'application/json'})
|
|
||||||
response = urllib.request.urlopen(req)
|
|
||||||
return response
|
|
||||||
|
|
||||||
|
|
||||||
def sendCameraAliveMessage(apiServer, cameraID, streamKey):
|
|
||||||
|
|
||||||
print("sending camera alive message")
|
|
||||||
url = '%s/v1/set_camera_status' % (apiServer)
|
|
||||||
print("url", url)
|
|
||||||
try:
|
|
||||||
response = makePOST(url, {'camera_id': cameraID,
|
|
||||||
'camera_status': 'online',
|
|
||||||
'stream_key': streamKey,
|
|
||||||
'type': 'robot_git'})
|
|
||||||
#todo:send unified stream key here
|
|
||||||
except:
|
|
||||||
print("could not make post to", url)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def aplayFile(filename):
|
|
||||||
for hardwareNumber in (2, 0, 1):
|
|
||||||
_thread.start_new_thread(os.system, ('aplay -D plughw:%d,0 %s' % (hardwareNumber, filename),))
|
|
||||||
|
|
||||||
|
|
||||||
def handleSoundCommand(command, keyPosition):
|
|
||||||
print("command:", command, "key position:", keyPosition)
|
|
||||||
if len(command) >= 6:
|
|
||||||
if command[0:5] == "SOUND" and keyPosition == "down":
|
|
||||||
number = int(command[5:])
|
|
||||||
aplayFile('/home/pi/sound/SOUND%d.WAV' % number)
|
|
||||||
3
companion_software/requirements.txt
Normal file
3
companion_software/requirements.txt
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
coloredlogs
|
||||||
|
verboselogs
|
||||||
|
ffmpeg-python
|
||||||
Reference in New Issue
Block a user