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

modified architecture: json receiving now runs asynchronously

parent d9893203
Loading
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -26,6 +26,8 @@ public:
        GetCameraSettings = 4364,
        GetCameraState = 4362,
        
        QuerySessionHolder = 1793,
        
    };
    
    enum class ParamCameraState
+133 −300
Original line number Diff line number Diff line
#include <cerrno>
#include <climits>
#include <iostream>
#include <vector>
#include <thread>

#include <unistd.h>

#include "commander.h"
#include <iostream>

using namespace std;


Commander::Commander() :
    _sock(TcpSocket(&_reader)),
    _token(0),
    _sock(TcpSocket()),
    _reader(CustomReader(&_sock)),
        
    _iso((Codes::ISO)-1),
    _speed((Codes::Speed)-1),
@@ -21,6 +22,7 @@ Commander::Commander() :
    _bracketingBuiltIn((Codes::ParamBracketingBuiltIn)-1)    
{};


string Commander::generateJSON(Codes::MsgId msg)
{
    DynamicJsonDocument json(JSON_OBJECT_SIZE(2));
@@ -33,6 +35,7 @@ string Commander::generateJSON(Codes::MsgId msg)
    return str;
}


template <typename T>
string Commander::generateJSON(Codes::MsgId msg, T param)
{
@@ -47,6 +50,7 @@ string Commander::generateJSON(Codes::MsgId msg, T param)
    return str;
}


template <>
string Commander::generateJSON(Codes::MsgId msg, string param)
{
@@ -61,200 +65,46 @@ string Commander::generateJSON(Codes::MsgId msg, string param)
    return str;
}

void Commander::handleMsgId(DynamicJsonDocument& json)
{
    cout << "NOTIFICATION: ";
            
    //TODO: add more ids
    switch ((int)json["msg_id"])
    {
        case (int)Codes::MsgId::GetCameraState:
        {
            cout << "Camera state: ";
            string key = "param";
            if (!json.containsKey(key) || !json[key].is<int>())
                cout << "invalid" << endl;
            else
                cout << Codes::stateToString(json[key]) << endl;
            
            break;
        }
        
        case (int)Codes::MsgId::GetSetMode:
        {
            cout << "Camera mode set to: ";
            string key = "param";
            if (!json.containsKey(key) || !json[key].is<int>() || json[key] < 0 || json[key] > 1)
                cout << "invalid" << endl;
            else
                cout << (json[key] == (int)Codes::ParamMode::Video ? "video" : "photo" ) << endl;
            break;
        }
        
        default:
            cout << _reader.history << endl;
            break;
    }
}


bool Commander::getJSON(DynamicJsonDocument& json, int& rval, Codes::MsgId expected_msg_id)
{
    // Get response
    for (int i = 0; i < 3; ++i) // make 3 attempts to receive json
    {
        _reader.resetHistory();
        errno = 0;
        auto err = deserializeJson(json, _reader);
        
        if (errno == EAGAIN || errno == EWOULDBLOCK)
        {
            cerr << "recv timed out\n";
            continue;
        }
        
        switch (err.code())
        {
            case DeserializationError::Ok:
                break;
                
            case DeserializationError::NoMemory:
                //TODO
                cerr << "Error: couldn't parse JSON (" << err.c_str() << "): " << _reader.history << endl;
                return false;
            
            //these shouldn't happen
            case DeserializationError::InvalidInput:
            case DeserializationError::TooDeep:          
            case DeserializationError::NotSupported:    
            case DeserializationError::IncompleteInput: // this won't happen, as recv (deserializeJson) would time out
                cerr << "Error: couldn't parse JSON (" << err.c_str() << "): " << _reader.history << endl;
                cerr << "Restarting socket...";
                _sock.close();
                close();
                connect();
                //TODO: try again?
                return false;
        }

        string key = "msg_id";
        if (!json.containsKey(key) || !json[key].is<int>())
        {
            cerr << "Unexpected JSON response: " << _reader.history << endl;
            continue;
        }
        else if (json[key] != (int)expected_msg_id)
        {
            handleMsgId(json);
            continue;
        }

        key = "rval";
        if (!json.containsKey(key) || !json[key].is<int>())
        {
            //TODO: Handle message without rval
            cerr << "Error: unexpected rval (\"" << key << "\" = " << json[key] << ")" << endl;
            break;
        }
        
        rval = json[key];
        return true;
    }
    return false;
}


bool Commander::str2int (const char* s, int& i, int base)
{
    char *end;
    long  l;
    errno = 0;
    l = strtol(s, &end, base);
    if (errno != 0 || *s == '\0' || *end != '\0' || l > INT_MAX || l < INT_MIN)
        return false;
    
    i = l;
    return true;
}


bool Commander::connect()
{
    _sock.setTimeout(3);
    
   if (!_sock.connect("127.0.0.1", 7878) ||
       !getToken() ||
       !getCameraSettings())
            return false;
   
    return true;
}


bool Commander::getToken()
{
    if (!_sock.send(generateJSON(Codes::MsgId::GetToken))) 
        return false;
    
    // Get response
    DynamicJsonDocument json(minCapacity);
    int rval;
    if (!getJSON(json, rval, Codes::MsgId::GetToken))
        return false;
    
    switch(rval)
    {
        case (int)Codes::Rval::CommandOk:
            break;
        default:
    shared_ptr<DynamicJsonDocument> jsonPtr;
    if (!_sock.sendRecvVerify(generateJSON(Codes::MsgId::GetToken), jsonPtr, make_pair(Codes::MsgId::GetToken,Codes::Rval::CommandOk)))
        return false;
    }
    
    
    string key = "param";
    if (!json.containsKey(key) || !json[key].is<int>() || json[key] == 0)
    if (!jsonPtr->containsKey(key) || !(*jsonPtr)[key].is<int>())
    {
            cerr << "Error: unexpected JSON response: \"" << key << "\" = " << json[key] << endl;
        cerr << "Error: unexpected JSON response: \"" << key << "\" = " << (*jsonPtr)[key] << endl;
        return false;
    }
    _token = json[key];
    _token = (*jsonPtr)[key];
    
    return true;
}

bool Commander::getCameraSettings()
{
    if (!_sock.send(generateJSON(Codes::MsgId::GetCameraSettings))) 
        return false;

    DynamicJsonDocument json(JSON_OBJECT_SIZE(26) + 370);
    int rval;
    if (!getJSON(json, rval, Codes::MsgId::GetCameraSettings))
        return false;
    
    switch(rval)
bool Commander::getCameraSettings()
{
        case (int)Codes::Rval::CommandOk:
            break;
        default:
    shared_ptr<DynamicJsonDocument> jsonPtr;
    if (!_sock.sendRecvVerify(generateJSON(Codes::MsgId::GetCameraSettings), jsonPtr, make_pair(Codes::MsgId::GetCameraSettings,Codes::Rval::CommandOk)))
        return false;
    }
    
    string key = "still_iso";
    if (!json.containsKey(key) || !json[key].is<int>())
    if (!jsonPtr->containsKey(key) || !(*jsonPtr)[key].is<int>())
    {
            cerr << "Error: unexpected JSON response: \"" << key << "\" = " << json[key] << endl;
            cerr << "Error: unexpected JSON response: \"" << key << "\" = " << (*jsonPtr)[key] << endl;
            return false;
    }
    _iso = (Codes::ISO)(int)json[key];
    _iso = (Codes::ISO)(int)(*jsonPtr)[key];
    
    key = "still_shutter";
    if (!json.containsKey(key) || !json[key].is<int>())
    if (!jsonPtr->containsKey(key) || !(*jsonPtr)[key].is<int>())
    {
            cerr << "Error: unexpected JSON response: \"" << key << "\" = " << json[key] << endl;
            cerr << "Error: unexpected JSON response: \"" << key << "\" = " << (*jsonPtr)[key] << endl;
            return false;
    }
    _speed = (Codes::Speed)(int)json[key];
    _speed = (Codes::Speed)(int)(*jsonPtr)[key];
    
    return true;
}
@@ -303,22 +153,9 @@ bool Commander::setBracketingBuiltIn(Codes::ParamBracketingBuiltIn bracketing)
template <typename T>
bool Commander::setParam(Codes::MsgId msgid, T param)
{
    if (!_sock.send(generateJSON(msgid, (int)param)))
            return false;

    // Get response
    DynamicJsonDocument json(minCapacity);
    int rval;
    if (!getJSON(json, rval, msgid))
        return false;

    switch(rval)
    {
        case (int)Codes::Rval::CommandOk:
            break;
        default:
    shared_ptr<DynamicJsonDocument> jsonPtr;
    if (!_sock.sendRecvVerify(generateJSON(msgid, (int)param), jsonPtr, make_pair(msgid,Codes::Rval::CommandOk)))
        return false;
    }
    
    return true;
}
@@ -326,47 +163,22 @@ bool Commander::setParam(Codes::MsgId msgid, T param)
template <typename T>
bool Commander::getParam(Codes::MsgId msgid, T& param_out)
{
    if (!_sock.send(generateJSON(msgid)))
            return false;
    
    // Get response
    DynamicJsonDocument json(minCapacity);
    int rval;
    if (!getJSON(json, rval, msgid))
        return false;

    switch(rval)
    {
        case (int)Codes::Rval::CommandOk:
            break;
        default:
    shared_ptr<DynamicJsonDocument> jsonPtr;
    if (!_sock.sendRecvVerify(generateJSON(msgid), jsonPtr, make_pair(msgid,Codes::Rval::CommandOk)))
        return false;
    }

    string key = "param";
    if (!json.containsKey(key) || !json[key].is<T>())
    if (!jsonPtr->containsKey(key) || !(*jsonPtr)[key].is<T>())
    {
            cerr << "Error: unexpected JSON response: \"" << key << "\" = " << json[key] << endl;
            cerr << "Error: unexpected JSON response: \"" << key << "\" = " << (*jsonPtr)[key] << endl;
            return false;
    }
    
    param_out = json[key];
    param_out = (*jsonPtr)[key];
    return true;
}


/**
 * Set commander's bracketing settings - 
 * 
 * Computes and saves 'halfCount' shutter speeds above and below currently set 
 * shutter speed, so each two 'adjecent' speeds differ !APPROXIMATELY! by 'evStep' EV
 * (shutter speeds supported by camera does not always differ exactly by 1EV,
 * e.g. ... 1/3200s,1/2000s,1/1000s ... 1/4s,1s,2s ...)
 * 
 * @param evStep number of exposure steps between two images
 * @param halfCount number of pictures to be taken above and below currently set exposure
 * @return true if setting was successful, otherwise false
 */
bool Commander::setBracketing(size_t evStep, size_t halfCount)
{
    if (_bracketingStep != evStep || _bracketingHalfCount != halfCount)
@@ -409,46 +221,25 @@ bool Commander::setBracketing(size_t evStep, size_t halfCount)

bool Commander::shootNormal()
{
    if (!_sock.send(generateJSON(Codes::MsgId::ShootNormal)))
        return false;
    
    
    // await "capture was enqueued"
    DynamicJsonDocument json(minCapacity);
    int rval;
    if (!getJSON(json, rval, Codes::MsgId::ShootNormal))
        return false;
    
    switch(rval)
    {
        case (int)Codes::Rval::CommandOk:
            break;
        default:
            return false;
    }
    
    // await "capture has finished"
    json = DynamicJsonDocument(JSON_OBJECT_SIZE(9) + 140);
    if (!getJSON(json, rval, Codes::MsgId::ImgCaptured))
        return false;
    
    switch(rval)
    vector<shared_ptr<DynamicJsonDocument>> vectorJsonPtr;
    if (!_sock.sendRecvVerify(
            generateJSON(Codes::MsgId::ShootNormal),
            vectorJsonPtr,
            {
        case (int)Codes::Rval::NotificationOk:
            break;
        default:
                make_pair(Codes::MsgId::ShootNormal, Codes::Rval::CommandOk),
                make_pair(Codes::MsgId::ImgCaptured, Codes::Rval::NotificationOk)
            })
        )   
        return false;
    }
    
    string key = "param";
    if (!json.containsKey(key) || !json[key].is<string>())
    if (!vectorJsonPtr[1]->containsKey(key) || !(*vectorJsonPtr[1])[key].is<string>())
    {
            cerr << "Error: unexpected JSON response: \"" << key << "\" = " << json[key] << endl;
            cerr << "Error: unexpected JSON response: \"" << key << "\" = " << (*vectorJsonPtr[1])[key] << endl;
            return false;
    }
    
    cout << "Image captured: " << json[key] << endl;
    
    cout << "Image captured: " << (*vectorJsonPtr[1])[key] << endl;
    return true;
}

@@ -464,7 +255,7 @@ bool Commander::shootBracketing()
        if (!shootNormal())
            return false;
        
        usleep(300000); //wait for 300ms
        this_thread::sleep_for(chrono::milliseconds(300));
    }
    
    // set back shutter speed
@@ -474,68 +265,110 @@ bool Commander::shootBracketing()
    return true;
}


bool Commander::shootBracketingBuiltIn()
{
    if (!_sock.send(generateJSON(Codes::MsgId::ShootBracketing)))
        return false;
    vector<shared_ptr<DynamicJsonDocument>> vectorJsonPtr;
    if (!_sock.sendRecvVerify(
            generateJSON(Codes::MsgId::ShootBracketing),
            vectorJsonPtr,
            {
                make_pair(Codes::MsgId::ShootBracketing, Codes::Rval::CommandOk),
                        
                make_pair(Codes::MsgId::ShootBracketing, Codes::Rval::NotificationOk),
                make_pair(Codes::MsgId::ImgCaptured, Codes::Rval::NotificationOk),
                        
    // await "bracketing was enqueued"
    DynamicJsonDocument json(minCapacity);
    int rval;
    if (!getJSON(json, rval, Codes::MsgId::ShootBracketing))
                make_pair(Codes::MsgId::ShootBracketing, Codes::Rval::NotificationOk),
                make_pair(Codes::MsgId::ImgCaptured, Codes::Rval::NotificationOk),
                        
                make_pair(Codes::MsgId::ShootBracketing, Codes::Rval::NotificationOk),
                make_pair(Codes::MsgId::ImgCaptured, Codes::Rval::NotificationOk),
            })
        )   
        return false;
    
    switch(rval)
    for (int i = 2; i < 7; i+=2)
    {
        case (int)Codes::Rval::CommandOk:
            break;
        default:
        string key = "param";
        if (!vectorJsonPtr[i]->containsKey(key) || !(*vectorJsonPtr[i])[key].is<string>())
        {
                cerr << "Error: unexpected JSON response: \"" << key << "\" = " << (*vectorJsonPtr[i])[key] << endl;
                return false;
        }

    string key = "param";
    for (int i = 0; i < 3; ++i)
        cout << "Image captured: " << (*vectorJsonPtr[i])[key] << endl;
    }
    return true;
}


bool Commander::start()
{
        json = DynamicJsonDocument(minCapacity);
        if (!getJSON(json, rval, Codes::MsgId::ShootBracketing))
    receiverThr = thread(&Commander::runReceiver, this);
    this_thread::sleep_for(chrono::milliseconds(50));
    
    if (!isReceiverRunning() || !getToken() || !getCameraSettings())
        return false;
    
        switch(rval)
    return true;
}
void Commander::stop()
{
            case (int)Codes::Rval::NotificationOk:
                break;
            default:
                return false;
    _sock.stopReceiver();
    receiverThr.join();
}

        json = DynamicJsonDocument(JSON_OBJECT_SIZE(10) + 140);
        // await "capture has finished"
        if (!getJSON(json, rval, Codes::MsgId::ImgCaptured))
bool Commander::connect()
{
   if (!_sock.connectTo("127.0.0.1", 7878))
       return false;
   
        switch(rval)
    _sock.setTimeout(1);
   
    return true;
}

void Commander::disconnect()
{    
            case (int)Codes::Rval::NotificationOk:
                break;
            default:
                return false;
    _token = 0;
    _sock.closeSocket();
}

        if (!json.containsKey(key) || !json[key].is<string>())
bool Commander::isReceiverRunning()
{
                cerr << "Error: unexpected JSON response: \"" << key << "\" = " << json[key] << endl;
                return false;
    lock_guard lock(receiverRunningMutex);
    return receiverRunning;
}

        cout << "Image captured: " << json[key] << endl;
bool Commander::runReceiver()
{
    while (true)
    {
        if (!connect())
            return false;
        
        {
        lock_guard lock(receiverRunningMutex);
        receiverRunning = true;
        }
        
    return true;
        if (_sock.runReceiver())
            break;
            
        {
        lock_guard lock(receiverRunningMutex);
        receiverRunning = false;
        }
        
        //if TcpSocket::runReceiver() returns false, reconnect
        cerr << "Restarting socket..." << endl;
        disconnect();
    }
    
void Commander::close()
    {
    _token = 0;
    _sock.close();
    lock_guard lock(receiverRunningMutex);
    receiverRunning = false;
    }
    
    return true;
}
 No newline at end of file
+41 −17
Original line number Diff line number Diff line
@@ -2,8 +2,9 @@
#define COMMANDER_H

#include <map>
#include <queue>
#include <memory>
#include <vector>
#include <thread>

#include <ArduinoJson.h>

@@ -16,7 +17,7 @@ class Commander
{
public:
    Commander();
    bool connect();
    
    
    bool setISO(Codes::ISO iso);
    bool setSpeed(Codes::Speed speed);
@@ -25,20 +26,44 @@ public:
    template <typename T> bool setParam(Codes::MsgId msgid, T param);
    template <typename T> bool getParam(Codes::MsgId msgid, T& param_out);
    
    
    /**
    * Set commander's bracketing settings - 
    * 
    * Computes and saves 'halfCount' shutter speeds above and below currently set 
    * shutter speed, so each two 'adjecent' speeds differ !APPROXIMATELY! by 'evStep' EV
    * (shutter speeds supported by camera does not always differ exactly by 1EV,
    * e.g. ... 1/3200s,1/2000s,1/1000s ... 1/4s,1s,2s ...)
    * 
    * @param evStep number of exposure steps between two images
    * @param halfCount number of pictures to be taken above and below currently set exposure
    * @return true if setting was successful, otherwise false
    */
    bool setBracketing(size_t evStep, size_t halfCount);
    
    bool shootNormal();
    bool shootBracketing();
    bool shootBracketingBuiltIn();
    
    void close();
    bool getToken();
    bool getCameraSettings();
    
    
    bool runReceiver();
    bool stopReceiver();
    bool isReceiverRunning();
    
    bool start();
    void stop();

private:
    const int minCapacity = (JSON_OBJECT_SIZE(5) + 50);
    int _token;
    TcpSocket _sock;
    std::thread receiverThr;
    bool receiverRunning;
    std::mutex receiverRunningMutex;
    
    CustomReader _reader;
    TcpSocket _sock;
    int _token;
    
    Codes::ISO _iso;
    Codes::Speed _speed;
@@ -48,19 +73,18 @@ private:
    
    Codes::ParamBracketingBuiltIn _bracketingBuiltIn;    
    
    bool getToken();
    bool getCameraSettings();
    bool getJSON(DynamicJsonDocument& json, int& rval, Codes::MsgId expected_msg_id);
    void handleMsgId(DynamicJsonDocument& json);
    
    static bool str2int (const char* s, int& i, int base = 0);
    bool connect();
    void disconnect();
    
    void handleNotification(DynamicJsonDocument& json);
    
    std::string generateJSON(Codes::MsgId msg);
    bool shootBracketing(const std::vector<Codes::Speed>& speeds);
    
    template <typename T>
    std::string generateJSON(Codes::MsgId msg, T param);
    std::string generateJSON(Codes::MsgId msg);
    
    bool shootBracketing(const std::vector<Codes::Speed>& speeds);

    std::vector<Codes::Speed> speedOrder =
    {
+4 −3
Original line number Diff line number Diff line
@@ -2,14 +2,15 @@

using namespace std;

CustomReader::CustomReader(TcpSocket* sock) : sock(sock) {};
CustomReader::CustomReader(){};

// Reads one byte, or returns -1
int CustomReader::read()
{
    if (q.empty())
    {
        string str;
        if (!sock->recv(str))
        if (!sock->receive(str))
            return -1;

        for (size_t i = 0; i < str.size(); ++i)
@@ -32,7 +33,7 @@ size_t CustomReader::readBytes(char* buffer, size_t length)
    while (q.size() < length)
    {
        std::string str;
        if (!sock->recv(str))
        if (!sock->receive(str))
            break;

        for (size_t i = 0; i < str.size(); ++i)
+3 −1
Original line number Diff line number Diff line
@@ -4,9 +4,11 @@
#include <queue>
#include "tcpSocket.h"

class TcpSocket;

struct CustomReader
{    
    CustomReader(TcpSocket* sock);
    CustomReader();

    // Reads one byte, or returns -1
    int read();
Loading