Initial commit

This commit is contained in:
Sven Schmalle 2019-12-30 03:22:12 +01:00
commit e0afabe358
12 changed files with 2504 additions and 0 deletions

4
.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
pic/
fbox.arm
.vscode/
config.json

7
BUILD.ps1 Normal file
View File

@ -0,0 +1,7 @@
echo "Build Linux/arm..."
$env:GOOS="linux"; $env:GOARCH="arm"; go build -ldflags="-s -w" -o ./fbox.arm
#echo "UPX - Compress Static Binaries"
#..\upx-3.95-win64\upx.exe -9 ./fbox.arm
pause

18
README.md Normal file
View File

@ -0,0 +1,18 @@
# GoFotoBox
Zum kompilieren folgende Go-Abhängigkeiten installieren:
- `go get github.com/gorilla/mux`
Kompilieren:
`$env:GOOS="linux"; $env:GOARCH="arm"; go build -o fbox.arm`
Beispiel config.json:
```
{
"Host":"http://127.0.0.1",
"Port":"8000",
"Template":"default",
"MQTTServer": "127.0.0.1",
"MQTTPort": 8080
}
```

221
main.go Normal file
View File

@ -0,0 +1,221 @@
package main
import (
"encoding/json"
"fmt"
"io/ioutil"
"log"
"net/http"
"os"
"os/exec"
"path"
"path/filepath"
"runtime"
"strconv"
"strings"
"time"
"github.com/gorilla/mux"
)
var config = readConfig("")
// our main function
func main() {
// Ausgeben der Config-Optionen
fmt.Println("Host: " + config.Host)
fmt.Println("Post: " + config.Port)
fmt.Println("Template: " + config.Template)
fmt.Println("MQTTServer: " + config.MQTTServer)
fmt.Println("MQTTPort: " + strconv.Itoa(config.MQTTPort))
fmt.Println("OS: " + config.OS)
router := mux.NewRouter()
//router.HandleFunc("/_api/md/{pagename:.*}", getRawPage).Methods("GET")
router.HandleFunc("/_api/config", getConfig).Methods("GET")
router.HandleFunc("/_api/aufnahme", getAufnahme).Methods("GET")
router.HandleFunc("/_api/aufnahme", postAufnahme).Methods("POST")
router.HandleFunc("/_api/kamerabild", getKamerabild).Methods("GET")
router.HandleFunc("/_api/fotoliste", getFotoliste).Methods("GET")
router.PathPrefix("/pic/").Handler(http.StripPrefix("/pic/", http.FileServer(http.Dir("pic")))).Methods("GET")
router.PathPrefix("/").Handler(http.FileServer(http.Dir("web"))).Methods("GET")
log.Fatal(http.ListenAndServe(":"+config.Port, router))
}
func getConfig(w http.ResponseWriter, r *http.Request) {
json.NewEncoder(w).Encode(config)
}
func getAufnahme(w http.ResponseWriter, r *http.Request) {
// raspistill -t 99999999 -p '100,140,800,600'
if config.OS == "linux" {
cmd := exec.Command("killall", "raspistill")
err := cmd.Run()
check(err)
//cmd = exec.Command("/usr/bin/raspistill", "-t 1 -o /home/pi/fbox/pic/tmp.jpg")
//cmd = exec.Command("bash", "-c", `/usr/bin/raspistill -p '100,140,800,600' -t 1 -o /home/pi/fbox/pic/tmp.jpg`)
cmd = exec.Command("bash", "-c", `/usr/bin/raspistill -op 0 -p '100,140,800,600' -t 1 -o /home/pi/fbox/pic/tmp.jpg`)
err = cmd.Run()
check(err)
} else {
copyfile("./pic/Testbild.jpg", "./pic/tmp.jpg")
}
json.NewEncoder(w).Encode("tmp.jpg")
}
func postAufnahme(w http.ResponseWriter, r *http.Request) {
//fmt.Println("YYYY-MM-DD : ", currentTime.Format("2006-01-02"))
currentTime := time.Now()
Ordner := currentTime.Format("2006-01-02")
if !directoryExists(Ordner) {
os.MkdirAll(path.Join("/home/pi/fbox/pic", Ordner), os.ModePerm)
}
//fmt.Println("YYYY-MM-DD hh:mm:ss : ", currentTime.Format("2006-01-02 15:04:05"))
Foto := currentTime.Format("150405")
copyfile("./pic/tmp.jpg", path.Join("/home/pi/fbox/pic", Ordner, Foto+".jpg"))
json.NewEncoder(w).Encode(path.Join(Ordner, Foto+".jpg"))
}
func getKamerabild(w http.ResponseWriter, r *http.Request) {
if config.OS == "linux" {
//cmd := exec.Command("/usr/bin/raspistill", "-t 99999999 -p '100,140,800,600'")
cmd := exec.Command("bash", "-c", `/usr/bin/raspistill -t 99999999 -p '100,140,800,600'`)
err := cmd.Run()
check(err)
}
json.NewEncoder(w).Encode("OK")
}
func getFotoliste(w http.ResponseWriter, r *http.Request) {
//Livestream der Kamera beenden
if config.OS == "linux" {
cmd := exec.Command("killall", "raspistill")
err := cmd.Run()
check(err)
}
// Datum der config auslesen
f, err := os.Open("./config.json")
check(err)
statinfo, err := f.Stat()
check(err)
ConfigTime := statinfo.ModTime()
//fmt.Println(ConfigTime)
// Dateien herausfinden die neuer als das Config-Datm sind
//picfiles, err := ioutil.ReadDir("./pic")
//check(err)
var files []string
filepath.Walk("./pic", func(filepath string, file os.FileInfo, err error) error {
tmpPath := strings.Replace(filepath, "\\", "/", -1)
if file.Mode().IsRegular() {
//if file.ModTime() > ConfigTime {
//fmt.Print(file.Name() + ": ")
//fmt.Println(file.ModTime())
if file.Name() != "Testbild.jpg" && file.Name() != "TestbildKamera.jpg" && file.Name() != "tmp.jpg" {
if inTimeSpan(ConfigTime, time.Now(), file.ModTime()) {
files = append(files, tmpPath)
}
}
}
return nil
})
/*
for _, file := range picfiles {
if file.Mode().IsRegular() {
//if file.ModTime() > ConfigTime {
fmt.Print(file.Name() + ": ")
fmt.Println(file.ModTime())
if inTimeSpan(ConfigTime, time.Now(), file.ModTime()) {
files = append(files, file.Name())
}
}
}
*/
json.NewEncoder(w).Encode(files)
}
//--------------------------------------------------------------------------
// Typen
//--------------------------------------------------------------------------
type Configuration struct {
Host string
Port string
Template string
MQTTServer string
MQTTPort int
OS string
}
//--------------------------------------------------------------------------
// Hilfsfunktionen
//--------------------------------------------------------------------------
func check(e error) {
if e != nil {
fmt.Println(e)
}
}
func fileExists(filename string) bool {
info, err := os.Stat(filename)
if os.IsNotExist(err) {
return false
}
return !info.IsDir()
}
func directoryExists(filename string) bool {
info, err := os.Stat(filename)
if os.IsNotExist(err) {
return false
}
return info.IsDir()
}
func copyfile(source string, destination string) {
input, err := ioutil.ReadFile(source)
check(err)
err = ioutil.WriteFile(destination, input, 0644)
check(err)
}
func readConfig(filename string) *Configuration {
// initialize conf with default values.
conf := &Configuration{
Host: "http://127.0.0.1",
Port: "8000",
Template: "default",
MQTTServer: "127.0.0.1",
MQTTPort: 8080,
OS: runtime.GOOS,
}
b, err := ioutil.ReadFile("./config.json")
if err != nil {
return conf
}
if err = json.Unmarshal(b, conf); err != nil {
return conf
}
return conf
}
func inTimeSpan(start, end, check time.Time) bool {
return check.After(start) && check.Before(end)
}

BIN
web/bilder/geburtstag/1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

BIN
web/bilder/geburtstag/2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

BIN
web/bilder/geburtstag/3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 187 KiB

278
web/index.html Normal file
View File

@ -0,0 +1,278 @@
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<link rel=stylesheet href="/misc/bootstrap.min.css">
<style>
html {
position: relative;
min-height: 100%;
}
body {
margin-top: 60px;
}
</style>
</head>
<body>
<div id="TemplateTextHeader" style="position:fixed;top:0px;left:100px">
<center>
<p id="TemplateHeaderText" style="font-size:35px">
Herzlich Willkommen zur Fotobox!<br>
Drücke einfach auf "Aufnahme" und los geht es!
</p>
</center>
</div>
<!-- Template Bild Oben rechts / 1.png-->
<div id="TemplatePicOR" style="position:fixed;top:0px;right:0px">
<img src="/bilder/geburtstag/1.png">
</div>
<!-- Template Bilder rechts / 2.png-->
<div id="TemplatePicR1" style="position:fixed;top:300px;right:0px">
<img src="/bilder/geburtstag/2.png">
<div style="position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);">
<font size="12"><p id="TemplatePicR1Text">Aufnehmen</p></font>
</div>
</div>
<div id="TemplatePicR2" style="position:fixed;top:450px;right:0px">
<img src="/bilder/geburtstag/2.png">
<div style="position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);">
<font size="12"><p id="TemplatePicR2Text">Ansehen</p></font>
</div>
</div>
<div id="TemplatePicR3" style="position:fixed;top:600px;right:0px">
<img src="/bilder/geburtstag/2.png">
<div style="position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);">
<font size="12"><p id="TemplatePicR3Text"> - </p></font>
</div>
</div>
<!-- Template Bild unten / 3.png-->
<div id="TemplatePicUN" style="position:fixed;bottom:0px;left:0px">
<img src="/bilder/geburtstag/3.png">
</div>
<!-- Bild Kamera / TestbildKamera.jpg-->
<div id="TemplateKameraBild" style="position:fixed;top:140px;left:100px">
<img id="TemplateKameraTestBild" src="/pic/TestbildKamera.jpg">
</div>
<script language=javascript src="/misc/jquery.min.js"></script>
<script language=javascript src="/misc/mqttws31.js"></script>
<script type="text/javascript">
var PageMode = "start";
var AnzeigeFoto = "";
$(document).ready(function() {
// Laden der Config
$.ajax({
method: "GET",
contentType:'application/json; charset=utf-8',
url: '/_api/config',
dataType: "json",
data: "",
success: function(content){
LoadTemplate(content.Template);
MakeMQTTConnection(content.MQTTServer, content.MQTTPort);
// Wenn auf dem Pi ausgeführt wird, soll das Testbild nicht angezeigt werden
if(content.OS!="windows") {
$("#TemplateKameraTestBild").remove();
}
}
});
});
function LoadTemplate(TemplateName) {
$("#TemplatePicOR").html('<img src="/bilder/'+TemplateName+'/1.png">');
$("#TemplatePicR1").html(LoadTemplateButtonRechts(TemplateName,"TemplatePicR1Text","2.png","Aufnehmen"));
$("#TemplatePicR2").html(LoadTemplateButtonRechts(TemplateName,"TemplatePicR2Text","2.png","Ansehen"));
$("#TemplatePicR3").html(LoadTemplateButtonRechts(TemplateName,"TemplatePicR3Text","2.png","- - - - - "));
$("#TemplatePicUN").html('<img src="/bilder/'+TemplateName+'/3.png">');
}
function LoadTemplateButtonRechts(TemplateName, Pid, Bild, Text) {
var html_ButtonRechts = '<img src="/bilder/'+TemplateName+'/'+Bild+'">'+
'<div style="position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);">'+
'<font size="12"><p id="'+Pid+'">'+Text+'</p></font>'+
'</div>';
return html_ButtonRechts;
}
function MakeMQTTConnection(MQTTServer, MQTTPort) {
var MQTTClient = new Messaging.Client(MQTTServer, MQTTPort, "myclientid_" + parseInt(Math.random() * 100, 10));
var options = {
timeout: 3,
onSuccess: function () {
console.log("MQTT Client Connected!");
MQTTClient.subscribe('/pin'+'/#', {qos: 2});
},
onFailure: function (message) {
console.log("Connection failed: " + message.errorMessage);
}
};
MQTTClient.connect(options);
MQTTClient.onConnectionLost = function (responseObject) {
console.log("connection lost: " + responseObject.errorMessage);
};
MQTTClient.onMessageArrived = function (message) {
console.log('Payload: Topic: ' + message.destinationName + ' | ' + message.payloadString);
if(message.destinationName=="/pin/17") {
if(message.payloadString=="1") {
if(PageMode=="vorschau") {
Fotospeichern();
} else if(PageMode=="FotosAnschauen") {
FotosAnschauen("vor");
} else {
Aufnahme();
}
}
} else if(message.destinationName=="/pin/21") {
if(message.payloadString=="1") {
if(PageMode=="start") {
FotosAnschauen("start");
} else if(PageMode=="vorschau") {
Abbrechen();
} else if(PageMode=="FotosAnschauen") {
FotosAnschauen("zurueck");
}else {
//Aufnahme();
}
}
} else if(message.destinationName=="/pin/22") {
if(message.payloadString=="1") {
if(PageMode=="FotosAnschauen") {
Abbrechen();
}
}
}
}
}
async function Aufnahme() {
// x sec warten vor Aufnahme
for (i = 5; i >0 ; i--) {
console.log(i);
$("#TemplateHeaderText").html('Foto wird in '+i+' sec aufgenommen.<br>Bitte lächeln!');
await new Promise(r => setTimeout(r, 1000));
}
// Aufnahme starten
$.ajax({
method: "GET",
contentType:'application/json; charset=utf-8',
url: '/_api/aufnahme',
dataType: "json",
data: "",
success: function(content){
$("#TemplateKameraBild").html('<img width="800px" src="/pic/'+content+'?'+parseInt(Math.random() * 100, 10)+'">');
$("#TemplatePicR1Text").html('Speichern');
$("#TemplatePicR2Text").html('Abbrechen');
//$("#TemplatePicR3Text").html('<img src="/pic/'+content+'">');
PageMode = "vorschau";
}
});
}
function Fotospeichern() {
$.ajax({
method: "POST",
contentType:'application/json; charset=utf-8',
url: '/_api/aufnahme',
dataType: "json",
data: "",
success: function(content){
SetTemplateStart();
}
});
}
function FotosAnschauen(voroderzurueck) {
console.log("FotosAnschauen");
PageMode="FotosAnschauen";
$("#TemplatePicR1Text").html('Vor');
$("#TemplatePicR2Text").html('Zurück');
$("#TemplatePicR3Text").html('Abbrechen');
$.ajax({
method: "GET",
contentType:'application/json; charset=utf-8',
url: '/_api/fotoliste',
dataType: "json",
data: "",
success: function(content){
console.log("voroderzurueck: "+voroderzurueck);
// Wenn der Menüpunkt "Anschauen" ausgewöhlt wird, muss as letzte Bild angezeigt werden
if(voroderzurueck=="start") {
var Foto = content[content.length-1];
AnzeigeFoto=Foto;
$("#TemplateKameraBild").html('<img id="TemplateKameraTestBild" width="800px" height="600px" src="/'+Foto+'">');
}
if(voroderzurueck=="vor") {
var Foto = content[content.indexOf(AnzeigeFoto)+1]
if(Foto!==undefined) {
AnzeigeFoto=Foto;
$("#TemplateKameraBild").html('<img id="TemplateKameraTestBild" width="800px" height="600px" src="/'+Foto+'">');
}
}
if(voroderzurueck=="zurueck") {
var Foto = content[content.indexOf(AnzeigeFoto)-1]
if(Foto!==undefined) {
AnzeigeFoto=Foto;
$("#TemplateKameraBild").html('<img id="TemplateKameraTestBild" width="800px" height="600px" src="/'+Foto+'">');
}
}
console.log(AnzeigeFoto);
console.log("PageMode: "+PageMode);
}
});
}
function Abbrechen() {
SetTemplateStart();
}
//----------------------------------------------------------------------
// Hilsfunktionen
function sleep(milliseconds) {
return new Promise(resolve => setTimeout(resolve, milliseconds));
}
function SetTemplateStart() {
$("#TemplateHeaderText").html('Herzlich Willkommen zur Fotobox!<br>Drücke einfach auf "Aufnahme" und los geht es!');
$("#TemplateKameraBild").html('<img src="/pic/TestbildKamera.jpg">');
$("#TemplatePicR1Text").html('Aufnehmen');
$("#TemplatePicR2Text").html('Ansehen');
$("#TemplateKameraTestBild").remove();
PageMode = "start";
$.ajax({
method: "GET",
contentType:'application/json; charset=utf-8',
url: '/_api/kamerabild',
dataType: "json",
data: "",
success: function(content){
}
});
}
</script>
</body>
</html>

5
web/misc/bootstrap.min.css vendored Normal file

File diff suppressed because one or more lines are too long

7
web/misc/bootstrap.min.js vendored Normal file

File diff suppressed because one or more lines are too long

6
web/misc/jquery.min.js vendored Normal file

File diff suppressed because one or more lines are too long

1958
web/misc/mqttws31.js Normal file

File diff suppressed because it is too large Load Diff