Skip to content

node_test/NodeExample.cpp

More...

Types

Name
using std::function< void(const std::vector< std::string > &, std::shared_ptr< sgns::GeniusNode >)> CmdFunc

Functions

Name
std::atomic< bool > finished(false )
void enable_raw_mode()
void disable_raw_mode()
void clear_line()
void redraw_prompt()
std::string trim(std::string_view s)
std::string to_lower(std::string s)
void keyboard_input_thread()
bool check_arg_count(const std::vector< std::string > & args, size_t expected, const std::string & usage)
bool check_arg_count_min(const std::vector< std::string > & args, size_t min, const std::string & usage)
void cmd_info(const std::vector< std::string > & args, std::shared_ptr< sgns::GeniusNode > node)
void cmd_balance(const std::vector< std::string > & args, std::shared_ptr< sgns::GeniusNode > node)
void cmd_ds(const std::vector< std::string > & args, std::shared_ptr< sgns::GeniusNode > node)
void cmd_mint(const std::vector< std::string > & args, std::shared_ptr< sgns::GeniusNode > node)
void cmd_transfer(const std::vector< std::string > & args, std::shared_ptr< sgns::GeniusNode > node)
void cmd_price(const std::vector< std::string > & args, std::shared_ptr< sgns::GeniusNode > node)
void cmd_process(const std::vector< std::string > & , std::shared_ptr< sgns::GeniusNode > node)
void cmd_peer(const std::vector< std::string > & args, std::shared_ptr< sgns::GeniusNode > node)
void cmd_stopprocessing(const std::vector< std::string > & , std::shared_ptr< sgns::GeniusNode > node)
void cmd_quit(const std::vector< std::string > & , std::shared_ptr< sgns::GeniusNode > )
void cmd_help(const std::vector< std::string > & , std::shared_ptr< sgns::GeniusNode > )
std::vector< std::string > split_string(const std::string & str)
void process_events(std::shared_ptr< sgns::GeniusNode > genius_node)
void status_polling_thread(std::shared_ptr< sgns::GeniusNode > genius_node)
void periodic_processing(std::shared_ptr< sgns::GeniusNode > genius_node)
std::string generate_eth_private_key()
int main(int argc, char * argv[])

Attributes

Name
sgns::base::Logger logger
std::mutex keyboard_mutex
std::condition_variable cv
std::queue< std::string > events
std::string current_input
termios original_term
const char * POSENET_JSON
const std::map< std::string, CmdFunc > COMMANDS
DevConfig_st DEV_CONFIG

Detailed Description

Date: 2024-04-18 Henrique A. Klein (hklein@gnus.ai)

Types Documentation

using CmdFunc

using CmdFunc = std::function<void( const std::vector<std::string> &, std::shared_ptr<sgns::GeniusNode> )>;

Functions Documentation

function finished

std::atomic< bool > finished(
    false 
)

function enable_raw_mode

void enable_raw_mode()

function disable_raw_mode

void disable_raw_mode()

function clear_line

void clear_line()

function redraw_prompt

void redraw_prompt()

function trim

static std::string trim(
    std::string_view s
)

function to_lower

static std::string to_lower(
    std::string s
)

function keyboard_input_thread

void keyboard_input_thread()

function check_arg_count

static bool check_arg_count(
    const std::vector< std::string > & args,
    size_t expected,
    const std::string & usage
)

function check_arg_count_min

static bool check_arg_count_min(
    const std::vector< std::string > & args,
    size_t min,
    const std::string & usage
)

function cmd_info

static void cmd_info(
    const std::vector< std::string > & args,
    std::shared_ptr< sgns::GeniusNode > node
)

function cmd_balance

static void cmd_balance(
    const std::vector< std::string > & args,
    std::shared_ptr< sgns::GeniusNode > node
)

function cmd_ds

static void cmd_ds(
    const std::vector< std::string > & args,
    std::shared_ptr< sgns::GeniusNode > node
)

function cmd_mint

static void cmd_mint(
    const std::vector< std::string > & args,
    std::shared_ptr< sgns::GeniusNode > node
)

function cmd_transfer

static void cmd_transfer(
    const std::vector< std::string > & args,
    std::shared_ptr< sgns::GeniusNode > node
)

function cmd_price

static void cmd_price(
    const std::vector< std::string > & args,
    std::shared_ptr< sgns::GeniusNode > node
)

function cmd_process

static void cmd_process(
    const std::vector< std::string > & ,
    std::shared_ptr< sgns::GeniusNode > node
)

function cmd_peer

static void cmd_peer(
    const std::vector< std::string > & args,
    std::shared_ptr< sgns::GeniusNode > node
)

function cmd_stopprocessing

static void cmd_stopprocessing(
    const std::vector< std::string > & ,
    std::shared_ptr< sgns::GeniusNode > node
)

function cmd_quit

static void cmd_quit(
    const std::vector< std::string > & ,
    std::shared_ptr< sgns::GeniusNode > 
)

function cmd_help

static void cmd_help(
    const std::vector< std::string > & ,
    std::shared_ptr< sgns::GeniusNode > 
)

function split_string

static std::vector< std::string > split_string(
    const std::string & str
)

function process_events

static void process_events(
    std::shared_ptr< sgns::GeniusNode > genius_node
)

function status_polling_thread

void status_polling_thread(
    std::shared_ptr< sgns::GeniusNode > genius_node
)

function periodic_processing

static void periodic_processing(
    std::shared_ptr< sgns::GeniusNode > genius_node
)

function generate_eth_private_key

static std::string generate_eth_private_key()

function main

int main(
    int argc,
    char * argv[]
)

Attributes Documentation

variable logger

static sgns::base::Logger logger = sgns::base::createLogger( "NodeExample" );

variable keyboard_mutex

std::mutex keyboard_mutex;

variable cv

std::condition_variable cv;

variable events

std::queue< std::string > events;

variable current_input

std::string current_input;

variable original_term

termios original_term;

variable POSENET_JSON

static const char * POSENET_JSON;

variable COMMANDS

static const std::map< std::string, CmdFunc > COMMANDS                                = {
    { "help", cmd_help },
    { "?", cmd_help },
    { "info", cmd_info },
    { "balance", cmd_balance },
    { "ds", cmd_ds },
    { "mint", cmd_mint },
    { "transfer", cmd_transfer },
    { "price", cmd_price },
    { "process", cmd_process },
    { "peer", cmd_peer },
    { "stopprocessing", cmd_stopprocessing },
    { "quit", cmd_quit },
};

variable DEV_CONFIG

DevConfig_st DEV_CONFIG { "0xcafe", "0.65", "1.0", sgns::TokenID::FromBytes( { 0x00 } ), "./" };

Source code

#include <iostream>
#include <cstdlib>
#include <cstdint>
#include <atomic>
#include <random>
#include <map>
#include <functional>
#include <cctype>
#include <iomanip>
#include "base/logger.hpp"
#ifndef _WIN32
#include <termios.h>
#include <unistd.h>
#endif

#include <boost/program_options.hpp>
#include <boost/format.hpp>
#include <boost/asio.hpp>
#include "account/GeniusNode.hpp"
#include "FileManager.hpp"
#include <thread>
#include <chrono>

static sgns::base::Logger logger = sgns::base::createLogger( "NodeExample" );

std::mutex              keyboard_mutex;
std::condition_variable cv;
std::queue<std::string> events;
std::string             current_input;
std::atomic<bool>       finished( false );

#ifdef _WIN32
DWORD original_console_mode;
#else
termios original_term;
#endif

void enable_raw_mode()
{
#ifdef _WIN32
    HANDLE hInput = GetStdHandle( STD_INPUT_HANDLE );
    GetConsoleMode( hInput, &original_console_mode );
    SetConsoleMode( hInput, original_console_mode & ~( ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT ) );
#else
    tcgetattr( STDIN_FILENO, &original_term );
    termios term  = original_term;
    term.c_lflag &= ~( ICANON | ECHO );
    tcsetattr( STDIN_FILENO, TCSANOW, &term );
#endif
}

void disable_raw_mode()
{
#ifdef _WIN32
    HANDLE hInput = GetStdHandle( STD_INPUT_HANDLE );
    SetConsoleMode( hInput, original_console_mode );
#else
    tcsetattr( STDIN_FILENO, TCSANOW, &original_term );
#endif
}

void clear_line()
{
    std::cout << "\r\033[K";
}

void redraw_prompt()
{
    clear_line();
    std::cout << "> " << current_input << std::flush;
}

static std::string trim( std::string_view s )
{
    auto start = s.find_first_not_of( " \t\r\n" );
    if ( start == std::string::npos )
    {
        return {};
    }
    auto end = s.find_last_not_of( " \t\r\n" );
    return std::string( s.substr( start, end - start + 1 ) );
}

static std::string to_lower( std::string s )
{
    for ( auto &c : s )
    {
        c = static_cast<char>( std::tolower( static_cast<unsigned char>( c ) ) );
    }
    return s;
}

void keyboard_input_thread()
{
    enable_raw_mode();

    while ( !finished )
    {
        char ch;
        if ( !std::cin.get( ch ) )
        {
            // EOF (Ctrl+D) — signal quit
            std::lock_guard<std::mutex> lock( keyboard_mutex );
            events.emplace( "quit" );
            cv.notify_one();
            break;
        }

        {
            std::lock_guard<std::mutex> lock( keyboard_mutex );
            if ( ch == '\n' || ch == '\r' )
            {
                if ( !current_input.empty() )
                {
                    events.push( trim( current_input ) );
                    current_input.clear();
                    cv.notify_one();
                }
                std::cout << std::endl;
            }
            else if ( ch == 127 || ch == '\b' )
            {
                if ( !current_input.empty() )
                {
                    current_input.pop_back();
                }
            }
            else if ( std::isprint( static_cast<unsigned char>( ch ) ) ||
                      std::isspace( static_cast<unsigned char>( ch ) ) )
            {
                current_input += ch;
            }
        }

        redraw_prompt();
    }

    disable_raw_mode();
}

static bool check_arg_count( const std::vector<std::string> &args, size_t expected, const std::string &usage )
{
    if ( args.size() != expected )
    {
        logger->error( "Usage: {}", usage );
        return false;
    }
    return true;
}

static bool check_arg_count_min( const std::vector<std::string> &args, size_t min, const std::string &usage )
{
    if ( args.size() < min )
    {
        logger->error( "Usage: {}", usage );
        return false;
    }
    return true;
}

static constexpr const char *POSENET_JSON = R"({
  "name": "posenet-inference",
  "version": "1.0.0",
  "gnus_spec_version": 1.0,
  "author": "AI Assistant",
  "description": "PoseNet inference on multiple image inputs using MNN model",
  "tags": ["pose-estimation", "computer-vision", "inference"],

  "inputs": [
    {
      "name": "ballet_image",
      "source_uri_param": "https://ipfs.filebase.io/ipfs/QmdHvvEXRUgmyn1q3nkQwf9yE412Vzy5gSuGAukHRLicXA/data/ballet.data",
      "type": "texture2D",
      "description": "Ballet pose image input",
      "dimensions": {
        "width": 1350, "height": 900,
        "block_len": 4860000, "block_line_stride": 5400, "block_stride": 0,
        "chunk_line_stride": 1080, "chunk_offset": 0, "chunk_stride": 4320,
        "chunk_subchunk_height": 5, "chunk_subchunk_width": 5, "chunk_count": 25
      },
      "format": "RGBA8"
    },
    {
      "name": "frisbee_image",
      "source_uri_param": "https://ipfs.filebase.io/ipfs/QmdHvvEXRUgmyn1q3nkQwf9yE412Vzy5gSuGAukHRLicXA/data/frisbee3.data",
      "type": "texture2D",
      "description": "Frisbee pose image input",
      "dimensions": {
        "width": 512, "height": 512,
        "block_len": 786432, "block_line_stride": 1536, "block_stride": 0,
        "chunk_line_stride": 384, "chunk_offset": 0, "chunk_stride": 1152,
        "chunk_subchunk_height": 4, "chunk_subchunk_width": 4, "chunk_count": 16
      },
      "format": "RGB8"
    }
  ],

  "outputs": [
    {
      "name": "ballet_keypoints", "source_uri_param": "dummy",
      "type": "tensor", "description": "Detected keypoints for ballet image",
      "dimensions": { "width": 17, "height": 3 }, "format": "FLOAT32"
    },
    {
      "name": "frisbee_keypoints", "source_uri_param": "dummy",
      "type": "tensor", "description": "Detected keypoints for frisbee image",
      "dimensions": { "width": 17, "height": 3 }, "format": "FLOAT32"
    }
  ],

  "passes": [
    {
      "name": "ballet_pose_inference", "type": "inference",
      "description": "Run PoseNet inference on ballet image",
      "model": {
        "source_uri_param": "https://ipfs.filebase.io/ipfs/QmdHvvEXRUgmyn1q3nkQwf9yE412Vzy5gSuGAukHRLicXA/model.mnn",
        "format": "MNN", "batch_size": 1,
        "input_nodes": [
          { "name": "input", "type": "texture2D", "source": "input:ballet_image", "shape": [1, 256, 256, 4] }
        ],
        "output_nodes": [
          { "name": "output", "type": "tensor", "target": "output:ballet_keypoints", "shape": [1, 17, 3] }
        ]
      }
    },
    {
      "name": "frisbee_pose_inference", "type": "inference",
      "description": "Run PoseNet inference on frisbee image",
      "model": {
        "source_uri_param": "https://ipfs.filebase.io/ipfs/QmdHvvEXRUgmyn1q3nkQwf9yE412Vzy5gSuGAukHRLicXA/model.mnn",
        "format": "MNN", "batch_size": 1,
        "input_nodes": [
          { "name": "input", "type": "texture2D", "source": "input:frisbee_image", "shape": [1, 256, 256, 4] }
        ],
        "output_nodes": [
          { "name": "output", "type": "tensor", "target": "output:frisbee_keypoints", "shape": [1, 17, 3] }
        ]
      }
    }
  ]
})";

static void cmd_info( const std::vector<std::string> &args, std::shared_ptr<sgns::GeniusNode> node )
{
    if ( !check_arg_count( args, 1, "info" ) )
    {
        return;
    }
    logger->info( "Balance: {}", node->GetBalance() );
}

static void cmd_balance( const std::vector<std::string> &args, std::shared_ptr<sgns::GeniusNode> node )
{
    if ( !check_arg_count( args, 2, "balance <token_id>" ) )
    {
        return;
    }
    logger->info( "Balance: {}", node->GetBalance( args[1] ) );
}

static void cmd_ds( const std::vector<std::string> &args, std::shared_ptr<sgns::GeniusNode> node )
{
    if ( !check_arg_count( args, 1, "ds" ) )
    {
        return;
    }
    node->PrintDataStore();
}

static void cmd_mint( const std::vector<std::string> &args, std::shared_ptr<sgns::GeniusNode> node )
{
    if ( !check_arg_count( args, 2, "mint <amount>" ) )
    {
        return;
    }
    try
    {
        node->MintTokens( std::stoull( args[1] ), "supergenius", "", sgns::TokenID::FromBytes( { 0x00 } ) );
    }
    catch ( const std::exception &e )
    {
        logger->error( "Invalid amount: '{}' — must be a non-negative integer.", args[1] );
    }
}

static void cmd_transfer( const std::vector<std::string> &args, std::shared_ptr<sgns::GeniusNode> node )
{
    if ( !check_arg_count( args, 3, "transfer <amount> <recipient_address>" ) )
    {
        return;
    }
    try
    {
        node->TransferFunds( std::stoull( args[1] ),
                             args[2],
                             sgns::TokenID::FromBytes( { 0x00 } ),
                             std::chrono::milliseconds( sgns::GeniusNode::TIMEOUT_TRANSFER ) );
    }
    catch ( const std::exception &e )
    {
        logger->error( "Invalid amount: '{}' — must be a non-negative integer.", args[1] );
    }
}

static void cmd_price( const std::vector<std::string> &args, std::shared_ptr<sgns::GeniusNode> node )
{
    if ( !check_arg_count_min( args, 2, "price <token_id1> [token_id2 ...]" ) )
    {
        return;
    }
    std::vector<std::string> tokenIds( args.begin() + 1, args.end() );
    auto                     prices = node->GetCoinprice( tokenIds );
    for ( const auto &[token, price] : prices.value() )
    {
        logger->info( "{}: ${:.4f}", token, price );
    }
}

static void cmd_process( const std::vector<std::string> & /*args*/, std::shared_ptr<sgns::GeniusNode> node )
{
    auto jobpost = node->ProcessImage( POSENET_JSON );
    if ( !jobpost )
    {
        logger->error( "Job post error: {}", jobpost.error().message() );
    }
}

static void cmd_peer( const std::vector<std::string> &args, std::shared_ptr<sgns::GeniusNode> node )
{
    if ( !check_arg_count( args, 2, "peer <multiaddr>" ) )
    {
        return;
    }
    node->AddPeer( args[1] );
}

static void cmd_stopprocessing( const std::vector<std::string> & /*args*/, std::shared_ptr<sgns::GeniusNode> node )
{
    node->StopProcessing();
    logger->info( "Stopping processing" );
}

static void cmd_quit( const std::vector<std::string> & /*args*/, std::shared_ptr<sgns::GeniusNode> /*node*/ )
{
    finished = true;
}

static void cmd_help( const std::vector<std::string> & /*args*/, std::shared_ptr<sgns::GeniusNode> /*node*/ );

using CmdFunc = std::function<void( const std::vector<std::string> &, std::shared_ptr<sgns::GeniusNode> )>;
static const std::map<std::string, CmdFunc> COMMANDS = {
    { "help", cmd_help },
    { "?", cmd_help },
    { "info", cmd_info },
    { "balance", cmd_balance },
    { "ds", cmd_ds },
    { "mint", cmd_mint },
    { "transfer", cmd_transfer },
    { "price", cmd_price },
    { "process", cmd_process },
    { "peer", cmd_peer },
    { "stopprocessing", cmd_stopprocessing },
    { "quit", cmd_quit },
};

static void cmd_help( const std::vector<std::string> & /*args*/, std::shared_ptr<sgns::GeniusNode> /*node*/ )
{
    std::cout << "Available commands:\n"
              << "  help, ?                  Show this help\n"
              << "  info                     Display account balance\n"
              << "  balance <token_id>       Display balance for a specific token\n"
              << "  ds                       Print the data store\n"
              << "  mint <amount>            Mint tokens\n"
              << "  transfer <amt> <addr>    Transfer tokens to an address\n"
              << "  price <token1> [tokens]  Get coin prices\n"
              << "  process                  Submit a processing job\n"
              << "  peer <multiaddr>         Add a peer\n"
              << "  stopprocessing           Stop processing\n"
              << "  quit                     Exit the application\n";
}

static std::vector<std::string> split_string( const std::string &str )
{
    std::istringstream iss( str );
    return { std::istream_iterator<std::string>( iss ), std::istream_iterator<std::string>() };
}

static void process_events( std::shared_ptr<sgns::GeniusNode> genius_node )
{
    while ( !finished )
    {
        std::unique_lock<std::mutex> lock( keyboard_mutex );
        cv.wait( lock, [] { return !events.empty() || finished; } );

        while ( !events.empty() )
        {
            std::string event = std::move( events.front() );
            events.pop();
            lock.unlock();

            auto arguments = split_string( event );
            if ( arguments.empty() )
            {
                lock.lock();
                continue;
            }

            std::string cmd = to_lower( arguments[0] );
            auto        it  = COMMANDS.find( cmd );

            if ( it != COMMANDS.end() )
            {
                it->second( arguments, genius_node );
            }
            else
            {
                logger->warn( "Unknown command: '{}' — type 'help' for available commands.", cmd );
            }

            lock.lock();
        }
    }
}

void status_polling_thread( std::shared_ptr<sgns::GeniusNode> genius_node )
{
    static const char *STATUS_NAMES[] = { "DISABLED", "IDLE", "PROCESSING" };

    while ( !finished )
    {
        std::this_thread::sleep_for( std::chrono::seconds( 2 ) );
        if ( finished )
        {
            break;
        }

        auto status = genius_node->GetProcessingStatus();
        logger->info( "[Status: {} | Progress: {:.2f}%]",
                      STATUS_NAMES[static_cast<int>( status.status )],
                      status.percentage );
    }
}

static void periodic_processing( std::shared_ptr<sgns::GeniusNode> genius_node )
{
    while ( !finished )
    {
        std::this_thread::sleep_for( std::chrono::minutes( 7 ) );
        if ( finished )
        {
            break;
        }

        auto jobpost = genius_node->ProcessImage( POSENET_JSON );
        if ( !jobpost )
        {
            logger->error( "Job post error: {}", jobpost.error().message() );
        }
    }
}

static std::string generate_eth_private_key()
{
    std::random_device                      rd;
    std::mt19937                            gen( 42 );
    std::uniform_int_distribution<uint16_t> dist( 0, 255 );

    std::ostringstream oss;
    for ( int i = 0; i < 32; ++i )
    {
        oss << std::hex << std::setw( 2 ) << std::setfill( '0' ) << ( dist( gen ) & 0xFF );
    }
    return oss.str();
}

DevConfig_st DEV_CONFIG{ "0xcafe", "0.65", "1.0", sgns::TokenID::FromBytes( { 0x00 } ), "./" };

int main( int argc, char *argv[] )
{
    bool        start_processing = false; // Default behavior for "process"
    bool        is_processor     = true;  // Default value for the last parameter
    bool        is_full_node     = false;
    bool        terminal_mode    = false;
    std::string path_override;

    for ( int i = 1; i < argc; ++i )
    {
        std::string arg = argv[i];

        if ( arg == "server" )
        {
            start_processing = true;
            is_processor     = false;
            is_full_node     = true;
        }
        else if ( arg == "jobposter" )
        {
            start_processing = true;
            is_processor     = false;
            is_full_node     = false;
        }
        else if ( arg == "--full" )
        {
            is_full_node = true;
        }
        else if ( arg == "--terminal" )
        {
            terminal_mode = true;
        }
        else if ( arg.rfind( "--path=", 0 ) == 0 )
        {
            path_override = arg.substr( 7 );
        }
        else if ( ( arg == "-p" || arg == "--path" ) && i + 1 < argc )
        {
            path_override = argv[++i];
        }
    }

    if ( !path_override.empty() )
    {
        DEV_CONFIG.BaseWritePath = path_override;
        logger->info( "Using custom path: {}", path_override );
    }

    std::string eth_private_key = generate_eth_private_key();
    logger->info( "Generated Ethereum Private Key: {}", eth_private_key );

    auto node_instance = sgns::GeniusNode::New( DEV_CONFIG,
                                                eth_private_key.c_str(),
                                                true,
                                                is_processor,
                                                40101,
                                                is_full_node );

    std::thread status_thread;

    while ( node_instance->GetState() != sgns::GeniusNode::NodeState::READY )
    {
        std::this_thread::sleep_for( std::chrono::seconds( 1 ) );
    }

    std::thread input_thread;
    if ( terminal_mode )
    {
        input_thread = std::thread( keyboard_input_thread );
        logger->info( "Interactive mode. Type 'help' for available commands." );
        redraw_prompt();
    }

    auto idle_loop = []
    {
        while ( !finished )
        {
            std::this_thread::sleep_for( std::chrono::seconds( 1 ) );
        }
    };

    if ( start_processing )
    {
        std::thread processing_thread( periodic_processing, std::ref( node_instance ) );

        if ( terminal_mode )
        {
            process_events( node_instance );
        }
        else
        {
            idle_loop();
        }

        if ( processing_thread.joinable() )
        {
            processing_thread.join();
        }
    }
    else
    {
        if ( terminal_mode )
        {
            process_events( node_instance );
        }
        else
        {
            idle_loop();
        }
    }

    if ( input_thread.joinable() )
    {
        input_thread.join();
    }

    return 0;
}

Updated on 2026-06-28 at 18:54:57 -0700