Commit 3f69f321 authored by Martin Cífka's avatar Martin Cífka
Browse files

fixed stop shooting, bracketing mem-leak; removed AsyncFileReader/Streamer...

fixed stop shooting, bracketing mem-leak; removed AsyncFileReader/Streamer (not working correctly & needless)
parent 87014bbd
Loading
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -41,7 +41,7 @@ fi

if [ "$run" = true ] ; then
    cd build
    sleep 1 && tar cf - . | nc 192.168.42.1 1234 -w4 &
    sleep 2 && tar cf - . | nc 192.168.42.1 1234 -q1 &
    cd ..
    ./telnet.exp /tmp/"$outfile"
fi
+2 −0
Original line number Diff line number Diff line
@@ -28,6 +28,8 @@ public:
        
        QuerySessionHolder = 1793,
        
        PowerOff = 6155,
        
    };
    
    enum class ParamCameraState
+94 −49
Original line number Diff line number Diff line
@@ -4,19 +4,18 @@
#include <vector>
#include <thread>

#include <unistd.h>

#include "commander.h"

using namespace std;


Commander::Commander() :
    _sock(TcpSocket(&_reader)),
    _sock(TcpSocket(&_reader, this)),
    _token(0),
        
    _iso((Codes::ISO)-1),
    _speed((Codes::Speed)-1),
    _mode((Codes::ParamMode)-1),
        
    _bracketingStep(0),
    _bracketingHalfCount(0),
@@ -160,6 +159,31 @@ bool Commander::setBracketingBuiltIn(Codes::ParamBracketingBuiltIn bracketing)
}


bool Commander::setMode(Codes::ParamMode mode)
{
    if (_mode != mode)
    {
        if (setParam(Codes::MsgId::GetSetMode, mode))
            _mode = mode;
        else
            return false;
    }
    return true;
}

bool Commander::getMode()
{
    int mode;
    if (getParam(Codes::MsgId::GetSetMode, mode))
    {
        _mode = (Codes::ParamMode)mode;
        return true;
    }
    else
        return false;
}


template <typename T>
bool Commander::setParam(Codes::MsgId msgid, T param)
{
@@ -178,7 +202,7 @@ bool Commander::getParam(Codes::MsgId msgid, T& param_out)
        return false;

    string key = "param";
    if (!jsonPtr->containsKey(key) || !(*jsonPtr)[key].is<T>())
    if (!(*jsonPtr)[key].is<T>())
    {
            cerr << "Error: unexpected JSON response: \"" << key << "\" = " << (*jsonPtr)[key] << endl;
            return false;
@@ -206,7 +230,6 @@ bool Commander::setBracketing(size_t evStep, size_t halfCount)
            auto first = it - halfCount * evStep;
            auto last  = it + halfCount * evStep;
            
            
            // check if first & last are not out of range
            if (first  < speedOrder.begin() ||
                first >= speedOrder.end()   ||
@@ -214,8 +237,11 @@ bool Commander::setBracketing(size_t evStep, size_t halfCount)
                last >= speedOrder.end() )
                    return false;

            for (it = first; it <= last; it += evStep)
            for (int i = 0; i < 1 + halfCount * 2; ++i)
            {
                vect.push_back(*it);
                it += evStep;
            }
            
            _bracketingVector = vect;
            _bracketingStep = evStep;
@@ -275,16 +301,14 @@ bool Commander::shootBracketing()
        if (!shootNormal())
            return false;
            
        unique_lock lock(_stopShootingMutex);
        if (_stopShootingCV.wait_for(lock, chrono::milliseconds(300), [this](){return _stopShooting;}))
        {
        lock_guard lock(_stopShootingMutex);
        if (_stopShooting)
        {
            _stopShooting = false;
            return false;
        }
            // if this thread is notified before timeout: stop shooting
            cout << "shooting stopped" << endl;
            return true; 
        }
            
        this_thread::sleep_for(chrono::milliseconds(300));
        // else continue shooting
    }
    
    // set back shutter speed
@@ -356,9 +380,9 @@ bool Commander::shootTimelapse(bool bracketing)
        unique_lock lock(_stopShootingMutex);
        if (_stopShootingCV.wait_until(lock, next, [this](){return _stopShooting;}))
        {
            // if this thread is notified before timeout: stop shooting
            cout << "shooting stopped" << endl;
            _stopShooting = false;
            return false; // if thread is notified before timeout: stop shooting
            return true; 
        }
        // else (timeout): continue shooting
    }
@@ -375,6 +399,16 @@ void Commander::stopShooting()

bool Commander::shoot()
{
    if (_mode != Codes::ParamMode::Photo)
    {
        cout << "Setting mode to PHOTO" << endl;
        if (!setMode(Codes::ParamMode::Photo))
        {   
            cerr << "Error: Cannot set camera mode to PHOTO" << endl;
            return false;
        }
    }
    
    if (_bracketingStep == 0 || _bracketingHalfCount == 0)
    {
        if (_timelapseInterval == 0 || _timelapseCount == 0)
@@ -402,7 +436,7 @@ bool Commander::start()
    _executorThr = thread(&Commander::runExecutor, this);
    this_thread::sleep_for(chrono::milliseconds(50));
    
    if (!isReceiverRunning() || !getToken() || !getCameraSettings())
    if (!isReceiverRunning() || !getToken() || !getCameraSettings() || !getMode())
        return false;
    
    return true;
@@ -473,8 +507,30 @@ bool Commander::runReceiver()

void Commander::enqueueCommand(const std::string& cmd)
{
    auto jsonPtr = make_shared<DynamicJsonDocument>(JSON_OBJECT_SIZE(7)+90);
    auto err = deserializeJson(*jsonPtr, cmd);
        
    if (err)
    {
        cerr << err.c_str() << " : " << cmd << endl;
        return;
    }

    if(!(*jsonPtr)["cmd"].is<string>())
    {
        cerr << "Invalid data from user: " << cmd << endl;
        return;
    }
    
    if ((*jsonPtr)["cmd"] == "STOP")
    {
        stopShooting();
        cout << "(Enqueued: " << cmd << ")" << endl;
        return;
    }
    
    lock_guard lock(_executorQueueMutex);
    _executorQueue.push(cmd);
    _executorQueue.push(jsonPtr);
    cout << "(Enqueued: " << cmd << ")" << endl;
    _executorQueueCV.notify_all();
}
@@ -484,7 +540,7 @@ void Commander::runExecutor()
{
    while (true)
    {
        string msg;
        shared_ptr<DynamicJsonDocument> jsonPtr;
        {
            // wait until some command is in the queue
            unique_lock lock(_executorQueueMutex);
@@ -494,54 +550,43 @@ void Commander::runExecutor()
                else
                    return true;
            });
            msg = move(_executorQueue.front());
            jsonPtr = _executorQueue.front();
            _executorQueue.pop();
        }
        cout << endl << "Executing: " << msg << endl;
        DynamicJsonDocument json(JSON_OBJECT_SIZE(7+90)); //TODO: find needed json capacity
        auto err = deserializeJson(json, msg);
        
        
        if (err)
        {
            cerr << err.c_str() << " : " << msg << endl; 
            continue;
        }
        
        string key = "cmd";
        if(!json[key].is<string>())
        {
            cerr << "Invalid data from user: " << msg << endl; 
            continue;
            lock_guard lock(_stopShootingMutex);
            _stopShooting = false;
        }
        
        
        if (json[key] == "SHOOT")
        if ((*jsonPtr)["cmd"] == "SHOOT")
        {
            if (!(json["speed"].is<int>() &&
                  json["iso"].is<int>() &&
                  json["bracketingHalf"].is<int>() &&
                  json["bracketingEV"].is<int>() && 
                  json["timelapseCount"].is<int>() &&
                  json["timelapseInterval"].is<int>()))
            if (!((*jsonPtr)["speed"].is<int>() &&
                  (*jsonPtr)["iso"].is<int>() &&
                  (*jsonPtr)["bracketingHalf"].is<int>() &&
                  (*jsonPtr)["bracketingEV"].is<int>() && 
                  (*jsonPtr)["timelapseCount"].is<int>() &&
                  (*jsonPtr)["timelapseInterval"].is<int>()))
                  // whitebalance
                  // compensation
                  //
            {
                cerr << "Invalid data from user: " << msg << endl; 
                string str;
                serializeJson(*jsonPtr, str);
                cerr << "Invalid data from user: " << str << endl; 
                continue;
            }
            if ( !(setISO((Codes::ISO)(int)json["iso"]) && setSpeed((Codes::Speed)(int)json["speed"])))
            if ( !setISO((Codes::ISO)(int)((*jsonPtr)["iso"])) || !setSpeed((Codes::Speed)(int)((*jsonPtr)["speed"])))
            {
                //TODO: send warning
                continue;
            }
            setBracketing((int)json["bracketingEV"], (int)json["bracketingHalf"]);
            setTimelapse((int)json["timelapseInterval"], (int)json["timelapseCount"]);
            setBracketing((int)(*jsonPtr)["bracketingEV"], (int)(*jsonPtr)["bracketingHalf"]);
            setTimelapse((int)(*jsonPtr)["timelapseInterval"], (int)(*jsonPtr)["timelapseCount"]);

            if (!shoot())
            {
                //TODO: send warning
                cerr << "Shooting failed." << endl;
            }

        }
+9 −4
Original line number Diff line number Diff line
@@ -27,12 +27,14 @@ public:
    
private:
    friend class Server;
    std::thread _receiverThr;
    friend class TcpSocket;
    
    std::thread _receiverThr; // thread for receiving messages from camera
    bool _receiverRunning;
    std::mutex _receiverRunningMutex;
    
    std::thread _executorThr;
    std::queue<std::string> _executorQueue;
    std::thread _executorThr; // thread for executiong commands from user
    std::queue<std::shared_ptr<DynamicJsonDocument>> _executorQueue;
    std::mutex _executorQueueMutex;
    std::condition_variable _executorQueueCV;
    
@@ -42,6 +44,7 @@ private:
    
    Codes::ISO _iso;
    Codes::Speed _speed;
    Codes::ParamMode _mode;
    
    size_t _bracketingStep;
    size_t _bracketingHalfCount;
@@ -56,7 +59,7 @@ private:
    std::mutex _stopShootingMutex;
    std::condition_variable _stopShootingCV;
    
    Server* _server;
    Server* _server; // HTTP and WebSocket server

    
    bool connect();
@@ -97,7 +100,9 @@ private:
    bool setISO(Codes::ISO iso);
    bool setSpeed(Codes::Speed speed);
    void setTimelapse(size_t interval, size_t count);
    bool setMode(Codes::ParamMode mode);
    
    bool getMode();
    bool getToken();
    bool getCameraSettings();
    
+0 −130
Original line number Diff line number Diff line
#include <map>
#include <cstring>
#include <fstream>
#include <sstream>
#include <iostream>
#include <future>

/* This is just a very simple and inefficient demo of async responses,
 * please do roll your own variant or use a database or Node.js's async
 * features instead of this really bad demo */
struct AsyncFileReader {
private:
    /* The cache we have in memory for this file */
    std::string cache;
    int cacheOffset;
    bool hasCache;

    /* The pending async file read (yes we only support one pending read) */
    std::function<void(std::string_view)> pendingReadCb;

    int fileSize;
    std::string fileName;
    std::ifstream fin;
    uWS::Loop *loop;

public:
    /* Construct a demo async. file reader for fileName */
    AsyncFileReader(std::string fileName) : fileName(fileName) {
        fin.open(fileName, std::ios::binary);

        // get fileSize
        fin.seekg(0, fin.end);
        fileSize = fin.tellg();

        //std::cout << "File size is: " << fileSize << std::endl;

        // cache up 1 mb!
        cache.resize(1024 * 1024);

        //std::cout << "Caching 1 MB at offset = " << 0 << std::endl;
        fin.seekg(0, fin.beg);
        fin.read(cache.data(), cache.length());
        cacheOffset = 0;
        hasCache = true;

        // get loop for thread

        loop = uWS::Loop::get();
    }

    /* Returns any data already cached for this offset */
    std::string_view peek(int offset) {
        /* Did we hit the cache? */
        if (hasCache && offset >= cacheOffset && ((offset - cacheOffset) < cache.length())) {
            /* Cache hit */
            //std::cout << "Cache hit!" << std::endl;

            /*if (fileSize - offset < cache.length()) {
                std::cout << "LESS THAN WHAT WE HAVE!" << std::endl;
            }*/

            int chunkSize = std::min<int>(fileSize - offset, cache.length() - offset + cacheOffset);

            return std::string_view(cache.data() + offset - cacheOffset, chunkSize);
        } else {
            /* Cache miss */
            //std::cout << "Cache miss!" << std::endl;
            return std::string_view(nullptr, 0);
        }
    }

    /* Asynchronously request more data at offset */
    void request(int offset, std::function<void(std::string_view)> cb) {

        // in this case, what do we do?
        // we need to queue up this chunk request and callback!
        // if queue is full, either block or close the connection via abort!
        if (!hasCache) {
            // already requesting a chunk!
            std::cout << "ERROR: already requesting a chunk!" << std::endl;
            return;
        }

        // disable cache
        hasCache = false;

        std::async(std::launch::async, [this, cb, offset]() {
            //std::cout << "ASYNC Caching 1 MB at offset = " << offset << std::endl;



            // den har stängts! öppna igen!
            if (!fin.good()) {
                fin.close();
                //std::cout << "Reopening fin!" << std::endl;
                fin.open(fileName, std::ios::binary);
            }
            fin.seekg(offset, fin.beg);
            fin.read(cache.data(), cache.length());

            cacheOffset = offset;

            loop->defer([this, cb, offset]() {

                int chunkSize = std::min<int>(cache.length(), fileSize - offset);

                // båda dessa sker, wtf?
                if (chunkSize == 0) {
                    std::cout << "Zero size!?" << std::endl;
                }

                if (chunkSize != cache.length()) {
                    std::cout << "LESS THAN A CACHE 1 MB!" << std::endl;
                }

                hasCache = true;
                cb(std::string_view(cache.data(), chunkSize));
            });
        });
    }

    /* Abort any pending async. request */
    void abort() {

    }

    int getFileSize() {
        return fileSize;
    }
};
Loading