Skip to content

File uploader.h

File List > device > uploader.h

Go to the documentation of this file

#pragma once

#include <jac/link/router.h>
#include <jac/link/routerCommunicator.h>

#include <atomic>
#include <deque>
#include <filesystem>
#include <fstream>
#include <memory.h>

#include "util/lock.h"


namespace jac {


class Uploader {
public:
    enum class Command : uint8_t {
        READ_FILE = 0x01,
        WRITE_FILE = 0x02,
        DELETE_FILE = 0x03,
        LIST_DIR = 0x04,
        CREATE_DIR = 0x05,
        DELETE_DIR = 0x06,
        FORMAT_STORAGE = 0x07,
        LIST_RESOURCES = 0x08,
        READ_RESOURCE = 0x09,
        HAS_MORE_DATA = 0x10,
        LAST_DATA = 0x11,
        OK = 0x20,
        ERROR = 0x21,
        NOT_FOUND = 0x22,
        CONTINUE = 0x23,
        LOCK_NOT_OWNED = 0x24,
        GET_DIR_HASHES = 0x25,
    };

    enum class Error : uint8_t {
        UNKNOWN_COMMAND = 0x01,
        FILE_OPEN_FAILED = 0x02,
        FILE_DELETE_FAILED = 0x03,
        DIR_OPEN_FAILED = 0x04,
        DIR_CREATE_FAILED = 0x05,
        DIR_DELETE_FAILED = 0x06,
        INVALID_FILENAME = 0x07,
    };
private:
    enum class State {
        NONE,
        WAITING_FOR_DATA
    };

    State _state = State::NONE;
    std::fstream _file;
    std::function<bool(std::span<const uint8_t>)> _onData;
    std::function<bool()> _onDataComplete;

    std::unique_ptr<InputPacketCommunicator> _input;
    std::unique_ptr<OutputPacketCommunicator> _output;

    bool processPacket(int sender, std::span<const uint8_t> data);
    bool processReadFile(int sender, std::span<const uint8_t> data);
    bool processWriteFile(int sender, std::span<const uint8_t> data);
    bool processDeleteFile(int sender, std::span<const uint8_t> data);
    bool processListDir(int sender, std::span<const uint8_t> data);
    bool processCreateDir(int sender, std::span<const uint8_t> data);
    bool processDeleteDir(int sender, std::span<const uint8_t> data);
    bool processFormatStorage(int sender, std::span<const uint8_t> data);
    bool processGetHashes(int sender, std::span<const uint8_t> data);
    bool processListResources(int sender, std::span<const uint8_t> data);
    bool processReadResource(int sender, std::span<const uint8_t> data);

    std::thread _thread;
    std::atomic<bool> _stop = false;

    TimeoutLock& _devLock;

    std::filesystem::path _rootDir;
    std::function<void(std::filesystem::path)> _formatFS;

    std::unordered_map<std::string, std::span<const uint8_t>> _resources;
public:
    Uploader(
        std::unique_ptr<InputPacketCommunicator> input,
        std::unique_ptr<OutputPacketCommunicator> output,
        TimeoutLock& lock,
        std::filesystem::path rootDir,
        std::function<void(std::filesystem::path)> formatFS,
        std::unordered_map<std::string, std::span<const uint8_t>> resources
    ):
        _input(std::move(input)),
        _output(std::move(output)),
        _devLock(lock),
        _rootDir(std::move(rootDir)),
        _formatFS(std::move(formatFS)),
        _resources(std::move(resources))
    {}

    Uploader(const Uploader&) = delete;
    Uploader(Uploader&&) = delete;
    Uploader& operator=(const Uploader&) = delete;
    Uploader& operator=(Uploader&&) = delete;

    void lockTimeout();

    void start() {
        _thread = std::thread([this]() {
            while (!_stop) {
                auto res = _input->get();
                if (!res) {
                    continue;
                }
                auto [sender, data] = *res;

                _devLock.stopTimeout(sender);  // does nothing if not locked by sender
                processPacket(sender, std::span<const uint8_t>(data.begin(), data.end()));
                _devLock.resetTimeout(sender);  // does nothing if not locked by sender
            }
        });
    }

    ~Uploader() {
        _stop = true;
        _input->cancelRead();
        if (_thread.joinable()) {
            _thread.join();
        }
    }
};


} // namespace jac