Skip to content
Snippets Groups Projects
Commit 11abbcd4 authored by Boris Stefanovic's avatar Boris Stefanovic
Browse files

ADD: program args, system user, autostart, daemon, etc.

parent a92173ef
No related branches found
No related tags found
No related merge requests found
Showing
with 15502 additions and 24 deletions
.idea
build
build/
workspace/
*.out
*.a
display
imageviewer
title: Image Viewer
author: Boris Stefanovic
# Image Viewer
## Je veux simplement tester le paquet. Où se trouve le TLDR ?
Ici.
Une mise en place pourrait ressembler à ceci (depuis la racine de ce projet) :
```bash
mkdir work
cd work
git clone git://git.buildroot.net/buildroot
cd buildroot
git checkout 2023.02
cat ../../buildroot/buildroot.patch
make menuconfig
make
```
En ce qui concerne le `make menuconfig`, les choix vont naturellement différer selon le système mais des exemples de l'état de `.config` à différents stades sont donnés dans `(RACINE)/buildroot/<nombre>_*.config`.
## Arborescence
### `buildroot` :
Fichiers relatifs à l'intégration dans buildroot. Parmi ceux-ci se trouve `buildroot.patch` qui est le patch à appliquer à buildroot `2023.02` pour ajouter les options de configuration de `imageviewer`.
### `journal` :
Notes prises pendant le développement de la solution, surtout en ce qui concerne l'aspect embarqué. Il s'agit d'une référence uniquement: les commandes peuvent varier quelque peu d'un système à l'autre. Celà dit, comme le document a été écrit et qu'il décrit un exemple de configuration, il a été inclus dans l'espoir qu'il puisse être utile à un hypothétique utilisateur du paquet.
### `src` :
Le code "écran", écrit en C. Une fois `make` executé, il en résulte le fichier `build/libdisplay.a`.
### `api.go` :
Le code "API", écrit en Go. Voir le `Makefile` pour les détails.
## Structure du Code
L'application utilise `cgo` pour faire communiquer le code écrit en C et celui écrit en Go. Un seul Makefile est fourni. Ce dernier se comporte comme on pourrait s'y attendre avec l'utilisation des variables d'environnement usuelles pour la compilation croisée.
## Etat d'Avancement
### Ce qui fonctionne bien
- Le serveur fonctionne en récupérant de bon nombre d'erreurs potentielles.
- Les images s'affichent en un temps respectable: bufferisation utilisée presque au maximum.
- La concurrence est gérée de façon à protéger l'intégrité des données: "si un fichier existe, il est en bon état".
- L'API démarre automatiquement lors du démarrage du système.
- Options de ligne de commande "standard" (tiret-lettre) pour paramétrer les dossiers utilisés par le programme, le port pour les communications http, la taille maximale des requêtes, etc. avec des valeurs par défaut raisonnables.
### Ce qui doit encore être fait
- Libération des ressources et fermeture "propre" du programme lors d'une commande par l'API ou lors de la réception d'un signal SIGINT.
- Création de l'utilisateur `imageviewer` et du groupe `spidev` au moyen de `imageviewer.mk`.
### Idées de fonctionalités
- Enclencher un mode "parcours de la collection" qui change d'image périodiquement après un temps prédéfini.
......@@ -11,6 +11,7 @@ package main
// #include "src/lcd.h"
import "C"
import (
"flag"
"github.com/labstack/echo/v4"
"github.com/labstack/echo/v4/middleware"
"io"
......@@ -19,17 +20,22 @@ import (
"net/http"
"os"
"os/exec"
"path"
"strconv"
"strings"
"sync"
)
const (
listenPort = "4000"
var (
listenPort = 80
reqBodySizeLimit = "2M"
dirTmp = "/var/lib/imageviewer/tmp"
dirImg = "/var/lib/imageviewer/img"
)
const (
imgFieldName = "image"
cmdConvert = "convert"
dirTmp = "/tmp/"
dirImg = "/images/"
imgFileExtension = ".ppm"
imgGeometry = "240x320"
)
......@@ -39,8 +45,21 @@ var (
currentDisplay string = "::::"
)
func main() {
func parseArgs() {
flag.IntVar(&listenPort, "l", listenPort, "port to listen on")
flag.StringVar(&reqBodySizeLimit, "b", reqBodySizeLimit, "body size limit (ex: 4M)")
flag.StringVar(&dirImg, "d", dirImg, "directory where the images will be stored")
flag.StringVar(&dirTmp, "t", dirTmp, "temporary directory used during image transformation")
flag.Parse()
}
func setup() {
parseArgs()
C.lcd_init()
}
func main() {
setup()
e := echo.New()
e.HideBanner = true
......@@ -52,7 +71,7 @@ func main() {
e.DELETE("/delete/:"+imgFieldName, Delete)
e.GET("/display/:"+imgFieldName, Display)
err := e.Start(":" + listenPort)
err := e.Start(":" + strconv.Itoa(listenPort))
if err != nil {
log.Fatalln(err)
}
......@@ -74,8 +93,8 @@ func Upload(c echo.Context) error {
}(src)
filename := formFile.Filename
tmpPath := dirTmp + filename
dstPath := dirImg + filename + imgFileExtension
tmpPath := path.Join(dirTmp, filename)
dstPath := path.Join(dirImg, filename+imgFileExtension)
lock.Lock()
defer lock.Unlock()
tmpFile, err := os.Create(tmpPath)
......@@ -95,16 +114,16 @@ func Upload(c echo.Context) error {
log.Println(err)
}
return c.String(http.StatusOK, "OK : image uploaded")
return c.String(http.StatusOK, "OK : image uploaded\n")
}
func Download(c echo.Context) error {
image := c.Param(imgFieldName)
fn := dirImg + image
fn := path.Join(dirImg, image)
lock.Lock()
defer lock.Unlock()
if _, err := os.Stat(fn); err != nil {
return c.String(http.StatusNotFound, "ERROR : cannot find file ["+fn+"]")
return c.String(http.StatusNotFound, "ERROR : cannot find file ["+fn+"]\n")
}
return c.File(fn)
}
......@@ -116,13 +135,13 @@ func List(c echo.Context) error {
defer lock.Unlock()
files, err := os.ReadDir(dirImg)
if err != nil {
return c.String(http.StatusInternalServerError, "ERROR : cannot read directory ["+dirImg+"]")
return c.String(http.StatusInternalServerError, "ERROR : cannot read directory ["+dirImg+"]\n")
}
for _, f := range files {
name := f.Name()
path := dirImg + f.Name()
fullpath := path.Join(dirImg, f.Name())
head := " "
if path == currentDisplay {
if fullpath == currentDisplay {
head = " * "
}
s.WriteString(head + "[" + name + "]\n")
......@@ -132,12 +151,11 @@ func List(c echo.Context) error {
func Delete(c echo.Context) error {
image := c.Param(imgFieldName)
imgPath := dirImg + image
imgPath := path.Join(dirImg, image)
lock.Lock()
defer lock.Unlock()
if imgPath == currentDisplay {
// easily prevents some filesystem concurrency problems
return c.String(http.StatusConflict, "ERROR : implementation prohibits deletion of currently displayed image\n")
return c.String(http.StatusConflict, "ERROR : deletion of currently displayed image is prohibited\n")
}
if err := os.Remove(imgPath); err != nil {
return c.String(http.StatusInternalServerError, "ERROR : cannot remove image (perhaps it does not exist) ["+image+"]\n")
......@@ -147,11 +165,12 @@ func Delete(c echo.Context) error {
func Display(c echo.Context) error {
image := c.Param(imgFieldName)
path := C.CString(dirImg + image)
fullpath := C.CString(path.Join(dirImg, image))
lock.Lock()
defer lock.Unlock()
if err := C.lcd_show_ppm_img(path); err != 0 {
return c.String(http.StatusNotFound, "ERROR : image not found")
if err := C.lcd_show_ppm_img(fullpath); err != 0 {
return c.String(http.StatusNotFound, "ERROR : image not found\n")
}
return c.String(http.StatusOK, "OK : image displayed")
currentDisplay = image
return c.String(http.StatusOK, "OK : image displayed\n")
}
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
diff --git a/package/Config.in b/package/Config.in
index c7bb52d278..217ea32305 100644
--- a/package/Config.in
+++ b/package/Config.in
@@ -318,6 +318,7 @@ comment "Graphic applications"
source "package/glmark2/Config.in"
source "package/glslsandbox-player/Config.in"
source "package/gnuplot/Config.in"
+ source "package/imageviewer/Config.in"
source "package/jhead/Config.in"
source "package/kmscube/Config.in"
source "package/libva-utils/Config.in"
diff --git a/package/imageviewer/Config.in b/package/imageviewer/Config.in
new file mode 100644
index 0000000000..5d3fc12810
--- /dev/null
+++ b/package/imageviewer/Config.in
@@ -0,0 +1,9 @@
+config BR2_PACKAGE_IMAGEVIEWER
+ bool "imageviewer"
+ depends on BR2_PACKAGE_HOST_GO_TARGET_ARCH_SUPPORTS
+ depends on BR2_PACKAGE_HOST_GO_TARGET_CGO_LINKING_SUPPORTS
+ select BR2_PACKAGE_IMAGEMAGICK
+ select BR2_PACKAGE_JPEG
+ select BR2_PACKAGE_LIBPNG
+ help
+ Web API for controlling the image shown by a small SPI driven LCD display.
diff --git a/package/imageviewer/S90imageviewer b/package/imageviewer/S90imageviewer
new file mode 100644
index 0000000000..6c086c5369
--- /dev/null
+++ b/package/imageviewer/S90imageviewer
@@ -0,0 +1,44 @@
+#!/bin/sh
+
+EXEC_NAME="imageviewer"
+IMAGEVIEWER_CMD="/usr/bin/${EXEC_NAME}"
+DATA_DIR="/var/lib/${EXEC_NAME}/img"
+TEMP_DIR="/tmp/${EXEC_NAME}"
+
+start() {
+ mkdir -p ${DATA_DIR}
+ chmod -R 775 ${DATA_DIR}
+ mkdir -p ${TEMP_DIR}
+ chmod -R 775 ${TEMP_DIR}
+ start-stop-daemon -S -q -x $IMAGEVIEWER_CMD -b \
+ -- -l 80 -b 2M -d ${DATA_DIR} -t ${TEMP_DIR}
+ [ $? = 0 ] && echo "OK" || echo "FAIL"
+}
+
+stop() {
+ start-stop-daemon -K -q -x $IMAGEVIEWER_CMD
+ [ $? = 0 ] && echo "OK" || echo "FAIL"
+}
+
+restart() {
+ stop
+ start
+}
+
+case "$1" in
+start)
+ start
+ ;;
+stop)
+ stop
+ ;;
+restart | reload)
+ restart
+ ;;
+*)
+ echo "Usage: $0 {start|stop|restart}"
+ exit 1
+ ;;
+esac
+
+exit $?
diff --git a/package/imageviewer/imageviewer.mk b/package/imageviewer/imageviewer.mk
new file mode 100644
index 0000000000..34b5020242
--- /dev/null
+++ b/package/imageviewer/imageviewer.mk
@@ -0,0 +1,28 @@
+IMAGEVIEWER_VERSION = origin/stable
+IMAGEVIEWER_SITE = https://gitedu.hesge.ch/boris.stefanov/imageviewer.git
+IMAGEVIEWER_SITE_METHOD = git
+
+IMAGEVIEWER_GOMOD = .
+#IMAGEVIEWER_BIN_NAME = imageviewer
+#IMAGEVIEWER_BUILD_TARGETS = ./imageviewer
+IMAGEVIEWER_INSTALL_BINS = imageviewer
+
+
+define IMAGEVIEWER_BUILD_CMDS
+ $(MAKE) $(TARGET_CONFIGURE_OPTS) -C $(@D) all
+endef
+
+define IMAGEVIEWER_INSTALL_TARGET_CMDS
+ $(INSTALL) -m 755 -D $(@D)/build/imageviewer $(TARGET_DIR)/usr/bin/imageviewer
+endef
+
+define IMAGEVIEWER_INSTALL_INIT_SYSV
+ $(INSTALL) -m 755 -D package/imageviewer/S90imageviewer $(TARGET_DIR)/etc/init.d/S90imageviewer
+endef
+
+define IMAGEVIEWER_USERS
+ imageviewer -1 spidev -1 * - - - ImageViewer user
+endef
+
+
+$(eval $(golang-package))
This diff is collapsed.
config BR2_PACKAGE_IMAGEVIEWER
bool "imageviewer"
depends on BR2_PACKAGE_HOST_GO_TARGET_ARCH_SUPPORTS
depends on BR2_PACKAGE_HOST_GO_TARGET_CGO_LINKING_SUPPORTS
select BR2_PACKAGE_IMAGEMAGICK
select BR2_PACKAGE_JPEG
select BR2_PACKAGE_LIBPNG
help
Web API for controlling the image shown by a small SPI driven LCD display.
This diff is collapsed.
IMAGEVIEWER_VERSION = origin/stable
IMAGEVIEWER_SITE = https://gitedu.hesge.ch/boris.stefanov/imageviewer.git
IMAGEVIEWER_SITE_METHOD = git
IMAGEVIEWER_GOMOD = .
#IMAGEVIEWER_BIN_NAME = imageviewer
#IMAGEVIEWER_BUILD_TARGETS = ./imageviewer
IMAGEVIEWER_INSTALL_BINS = imageviewer
define IMAGEVIEWER_BUILD_CMDS
$(MAKE) $(TARGET_CONFIGURE_OPTS) -C $(@D) all
endef
define IMAGEVIEWER_INSTALL_TARGET_CMDS
$(INSTALL) -m 755 -D $(@D)/build/imageviewer $(TARGET_DIR)/usr/bin/imageviewer
endef
define IMAGEVIEWER_INSTALL_INIT_SYSV
$(INSTALL) -m 755 -D package/imageviewer/S90imageviewer $(TARGET_DIR)/etc/init.d/S90imageviewer
endef
define IMAGEVIEWER_USERS
imageviewer -1 spidev -1 * - - - ImageViewer user
endef
$(eval $(golang-package))
journal/img/buildroot-01-target-options.png

48.5 KiB

journal/img/buildroot-02-toolchain.png

80.7 KiB

journal/img/buildroot-03-system-configuration.png

90.5 KiB

journal/img/spi-1-menuconfig.png

136 KiB

This diff is collapsed.
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment