OverviewFilesCommitsIssuesDocsReleasesPeers

opencog-cogserver / opencog / cogserver / atoms / CogServerNode.cc

174 lines · cc ref:
/*
 * opencog/cogserver/atoms/CogServerNode.cc
 *
 * Copyright (c) 2025 BrainyBlaze Dynamics, LLC
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License v3 as
 * published by the Free Software Foundation and including the exceptions
 * at http://opencog.org/wiki/Licenses
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program; if not, see <https://www.gnu.org/licenses/>.
 */

#include <unordered_set>

#include <opencog/util/Logger.h>
#include <opencog/atoms/atom_types/atom_names.h>
#include <opencog/atoms/base/ClassServer.h>
#include <opencog/atoms/value/BoolValue.h>
#include <opencog/atoms/value/FloatValue.h>
#include <opencog/atoms/value/StringValue.h>
#include "CogServerNode.h"

using namespace opencog;

template class ObjectCRTP<CogServerNode>;

CogServerNode::CogServerNode(Type t, const std::string&& s)
	: ObjectCRTP<CogServerNode>(t, std::move(s)), CogServer()
{
}

CogServerNode::CogServerNode(const std::string&& s)
	: ObjectCRTP<CogServerNode>(COG_SERVER_NODE, std::move(s)), CogServer()
{
}

void CogServerNode::setAtomSpace(AtomSpace* as)
{
	Node::setAtomSpace(as);
	if (nullptr == as) return;

	// Port defaults
	Atom::setValue(as->add_atom(Predicate("*-telnet-port-*")),
	               createFloatValue(17001.0));
	Atom::setValue(as->add_atom(Predicate("*-web-port-*")),
	               createFloatValue(18080.0));
	Atom::setValue(as->add_atom(Predicate("*-mcp-port-*")),
	               createFloatValue(18888.0));

	// Prompt defaults
	Atom::setValue(as->add_atom(Predicate("*-ansi-prompt-*")),
	               createStringValue("\033[0;32mopencog\033[1;32m> \033[0m"));
	Atom::setValue(as->add_atom(Predicate("*-prompt-*")),
	               createStringValue("opencog> "));
	Atom::setValue(as->add_atom(Predicate("*-ansi-scm-prompt-*")),
	               createStringValue("\033[0;34mguile\033[1;34m> \033[0m"));
	Atom::setValue(as->add_atom(Predicate("*-scm-prompt-*")),
	               createStringValue("guile> "));

	// ANSI color enable/disable
	Atom::setValue(as->add_atom(Predicate("*-ansi-enabled-*")),
	               createBoolValue(true));

	CogServer::loadModules(get_handle());
}

/// Enable the network servers.
/// Returns true if the servers were started, false if already running.
bool CogServerNode::startServers()
{
	if (CogServer::set_running()) return false;

	// This try-catch block is a historical appendage,
	// it is here to try to protect us against ASIO insanity.
	// I think this "never happens any more", but never say never.
	try
	{
		Handle h(get_handle());
		enableNetworkServer(h);
		enableWebServer(h);
		enableMCPServer(h);
	}
	catch (const std::exception& ex)
	{
		logger().error("Failed to start server: %s", ex.what());
	}
	return true;
}

/// Disable the network servers
void CogServerNode::stopServers()
{
	disableMCPServer();
	disableWebServer();
	disableNetworkServer();
}

ValuePtr CogServerNode::getValue(const Handle& key) const
{
	// Handle the *-is-running?-* query
	if (PREDICATE_NODE == key->get_type())
	{
		static constexpr uint32_t p_is_running = dispatch_hash("*-is-running?-*");
		const std::string& pred = key->get_name();
		if (dispatch_hash(pred.c_str()) == p_is_running)
			return createBoolValue(CogServer::running());
	}

	// Pass through to base class for all other keys
	return Atom::getValue(key);
}

void CogServerNode::setValue(const Handle& key, const ValuePtr& value)
{
	// If not a PredicateNode, just store the value.
	if (PREDICATE_NODE != key->get_type())
	{
		Atom::setValue(key, value);
		return;
	}

	// Create a fast dispatch table by using case-statement
	// branching, instead of string compare.
	static constexpr uint32_t p_start = dispatch_hash("*-start-*");
	static constexpr uint32_t p_stop = dispatch_hash("*-stop-*");
	static constexpr uint32_t p_run = dispatch_hash("*-run-*");

	const std::string& pred = key->get_name();
	switch (dispatch_hash(pred.c_str()))
	{
		case p_start:
			if (not startServers()) return;
			_main_loop = new std::thread([this]() { serverLoop(); });
			return;

		case p_stop:
			if (not CogServer::running()) return;
			CogServer::stop();
			if (_main_loop)
			{
				_main_loop->join();
				delete _main_loop;
				_main_loop = nullptr;
			}
			stopServers();
			return;

		case p_run:
			startServers();
			serverLoop();
			stopServers();
			return;

		default:
			break;
	}

	// Some other predicate. Store it.
	Atom::setValue(key, value);
}

DEFINE_NODE_FACTORY(CogServerNode, COG_SERVER_NODE)

// Init function for guile load-extension. The factory registration
// happens via static constructors in DEFINE_NODE_FACTORY above.
extern "C" void opencog_servernode_init(void) {}