Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
105 changes: 97 additions & 8 deletions clients/nutclient.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -406,6 +406,8 @@ void Socket::write(const std::string& str)throw(nut::IOException)
*
*/

const Feature Client::TRACKING = "TRACKING";

Client::Client()
{
}
Expand Down Expand Up @@ -479,6 +481,19 @@ bool Client::hasDeviceCommand(const std::string& dev, const std::string& name)th
return names.find(name) != names.end();
}

bool Client::hasFeature(const Feature& feature)throw(NutException)
{
try
{
// If feature is known, querying it won't throw an exception.
isFeatureEnabled(feature);
return true;
}
catch(...)
{
return false;
}
}

/*
*
Expand Down Expand Up @@ -693,20 +708,20 @@ std::map<std::string,std::map<std::string,std::vector<std::string> > > TcpClient
return map;
}

void TcpClient::setDeviceVariable(const std::string& dev, const std::string& name, const std::string& value)throw(NutException)
TrackingID TcpClient::setDeviceVariable(const std::string& dev, const std::string& name, const std::string& value)throw(NutException)
{
std::string query = "SET VAR " + dev + " " + name + " " + escape(value);
detectError(sendQuery(query));
return sendTrackingQuery(query);
}

void TcpClient::setDeviceVariable(const std::string& dev, const std::string& name, const std::vector<std::string>& values)throw(NutException)
TrackingID TcpClient::setDeviceVariable(const std::string& dev, const std::string& name, const std::vector<std::string>& values)throw(NutException)
{
std::string query = "SET VAR " + dev + " " + name;
for(size_t n=0; n<values.size(); ++n)
{
query += " " + escape(values[n]);
}
detectError(sendQuery(query));
return sendTrackingQuery(query);
}

std::set<std::string> TcpClient::getDeviceCommandNames(const std::string& dev)throw(NutException)
Expand All @@ -727,9 +742,9 @@ std::string TcpClient::getDeviceCommandDescription(const std::string& dev, const
return get("CMDDESC", dev + " " + name)[0];
}

void TcpClient::executeDeviceCommand(const std::string& dev, const std::string& name, const std::string& param)throw(NutException)
TrackingID TcpClient::executeDeviceCommand(const std::string& dev, const std::string& name, const std::string& param)throw(NutException)
{
detectError(sendQuery("INSTCMD " + dev + " " + name + " " + param));
return sendTrackingQuery("INSTCMD " + dev + " " + name + " " + param);
}

void TcpClient::deviceLogin(const std::string& dev)throw(NutException)
Expand All @@ -753,6 +768,60 @@ int TcpClient::deviceGetNumLogins(const std::string& dev)throw(NutException)
return atoi(num.c_str());
}

TrackingResult TcpClient::getTrackingResult(const TrackingID& id)throw(NutException)
{
if (id.empty())
{
return TrackingResult::SUCCESS;
}

std::string result = sendQuery("GET TRACKING " + id);

if (result == "PENDING")
{
return TrackingResult::PENDING;
}
else if (result == "SUCCESS")
{
return TrackingResult::SUCCESS;
}
else if (result == "ERR UNKNOWN")
{
return TrackingResult::UNKNOWN;
}
else if (result == "ERR INVALID-ARGUMENT")
{
return TrackingResult::INVALID_ARGUMENT;
}
else
{
return TrackingResult::FAILURE;
}
}

bool TcpClient::isFeatureEnabled(const Feature& feature)throw(NutException)
{
std::string result = sendQuery("GET " + feature);
detectError(result);

if (result == "ON")
{
return true;
}
else if (result == "OFF")
{
return false;
}
else
{
throw NutException("Unknown feature result " + result);
}
}
void TcpClient::setFeature(const Feature& feature, bool status)throw(NutException)
{
std::string result = sendQuery("SET " + feature + " " + (status ? "ON" : "OFF"));
detectError(result);
}

std::vector<std::string> TcpClient::get
(const std::string& subcmd, const std::string& params) throw(NutException)
Expand Down Expand Up @@ -968,6 +1037,26 @@ std::string TcpClient::escape(const std::string& str)
return res;
}

TrackingID TcpClient::sendTrackingQuery(const std::string& req)throw(NutException)
{
std::string reply = sendQuery(req);
detectError(reply);
std::vector<std::string> res = explode(reply);

if (res.size() == 1 && res[0] == "OK")
{
return TrackingID("");
}
else if (res.size() == 3 && res[0] == "OK" && res[1] == "TRACKING")
{
return TrackingID(res[2]);
}
else
{
throw NutException("Unknown query result");
}
}

/*
*
* Device implementation
Expand Down Expand Up @@ -1142,10 +1231,10 @@ Command Device::getCommand(const std::string& name)throw(NutException)
return Command(NULL, "");
}

void Device::executeCommand(const std::string& name, const std::string& param)throw(NutException)
TrackingID Device::executeCommand(const std::string& name, const std::string& param)throw(NutException)
{
if (!isOk()) throw NutException("Invalid device");
getClient()->executeDeviceCommand(getName(), name, param);
return getClient()->executeDeviceCommand(getName(), name, param);
}

void Device::login()throw(NutException)
Expand Down
51 changes: 44 additions & 7 deletions clients/nutclient.h
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,25 @@ class TimeoutException : public IOException
virtual ~TimeoutException() throw() {}
};

/**
* Cookie given when performing async action, used to redeem result at a later date.
*/
typedef std::string TrackingID;

/**
* Result of an async action.
*/
typedef enum
{
UNKNOWN,
PENDING,
SUCCESS,
INVALID_ARGUMENT,
FAILURE,
} TrackingResult;

typedef std::string Feature;

/**
* A nut client is the starting point to dialog to NUTD.
* It can connect to an NUTD then retrieve its device list.
Expand Down Expand Up @@ -232,14 +251,14 @@ class Client
* \param name Variable name
* \param value Variable value
*/
virtual void setDeviceVariable(const std::string& dev, const std::string& name, const std::string& value)throw(NutException)=0;
virtual TrackingID setDeviceVariable(const std::string& dev, const std::string& name, const std::string& value)throw(NutException)=0;
/**
* Intend to set the value of a variable.
* \param dev Device name
* \param name Variable name
* \param value Variable value
*/
virtual void setDeviceVariable(const std::string& dev, const std::string& name, const std::vector<std::string>& values)throw(NutException)=0;
virtual TrackingID setDeviceVariable(const std::string& dev, const std::string& name, const std::vector<std::string>& values)throw(NutException)=0;
/** \} */

/**
Expand Down Expand Up @@ -273,7 +292,7 @@ class Client
* \param name Command name
* \param param Additional command parameter
*/
virtual void executeDeviceCommand(const std::string& dev, const std::string& name, const std::string& param="")throw(NutException)=0;
virtual TrackingID executeDeviceCommand(const std::string& dev, const std::string& name, const std::string& param="")throw(NutException)=0;
/** \} */

/**
Expand All @@ -294,6 +313,18 @@ class Client
virtual void deviceMaster(const std::string& dev)throw(NutException)=0;
virtual void deviceForcedShutdown(const std::string& dev)throw(NutException)=0;

/**
* Retrieve the result of a tracking ID.
* \param id Tracking ID.
*/
virtual TrackingResult getTrackingResult(const TrackingID& id)throw(NutException)=0;

virtual bool hasFeature(const Feature& feature)throw(NutException);
virtual bool isFeatureEnabled(const Feature& feature)throw(NutException)=0;
virtual void setFeature(const Feature& feature, bool status)throw(NutException)=0;

static const Feature TRACKING;

protected:
Client();
};
Expand Down Expand Up @@ -378,22 +409,28 @@ class TcpClient : public Client
virtual std::vector<std::string> getDeviceVariableValue(const std::string& dev, const std::string& name)throw(NutException);
virtual std::map<std::string,std::vector<std::string> > getDeviceVariableValues(const std::string& dev)throw(NutException);
virtual std::map<std::string,std::map<std::string,std::vector<std::string> > > getDevicesVariableValues(const std::set<std::string>& devs)throw(NutException);
virtual void setDeviceVariable(const std::string& dev, const std::string& name, const std::string& value)throw(NutException);
virtual void setDeviceVariable(const std::string& dev, const std::string& name, const std::vector<std::string>& values)throw(NutException);
virtual TrackingID setDeviceVariable(const std::string& dev, const std::string& name, const std::string& value)throw(NutException);
virtual TrackingID setDeviceVariable(const std::string& dev, const std::string& name, const std::vector<std::string>& values)throw(NutException);

virtual std::set<std::string> getDeviceCommandNames(const std::string& dev)throw(NutException);
virtual std::string getDeviceCommandDescription(const std::string& dev, const std::string& name)throw(NutException);
virtual void executeDeviceCommand(const std::string& dev, const std::string& name, const std::string& param="")throw(NutException);
virtual TrackingID executeDeviceCommand(const std::string& dev, const std::string& name, const std::string& param="")throw(NutException);

virtual void deviceLogin(const std::string& dev)throw(NutException);
virtual void deviceMaster(const std::string& dev)throw(NutException);
virtual void deviceForcedShutdown(const std::string& dev)throw(NutException);
virtual int deviceGetNumLogins(const std::string& dev)throw(NutException);

virtual TrackingResult getTrackingResult(const TrackingID& id)throw(NutException);

virtual bool isFeatureEnabled(const Feature& feature)throw(NutException);
virtual void setFeature(const Feature& feature, bool status)throw(NutException);

protected:
std::string sendQuery(const std::string& req)throw(nut::IOException);
void sendAsyncQueries(const std::vector<std::string>& req)throw(IOException);
static void detectError(const std::string& req)throw(nut::NutException);
TrackingID sendTrackingQuery(const std::string& req)throw(nut::NutException);

std::vector<std::string> get(const std::string& subcmd, const std::string& params = "")
throw(nut::NutException);
Expand Down Expand Up @@ -541,7 +578,7 @@ class Device
* \param name Command name.
* \param param Additional command parameter
*/
void executeCommand(const std::string& name, const std::string& param="")throw(NutException);
TrackingID executeCommand(const std::string& name, const std::string& param="")throw(NutException);

/**
* Login current client's user for the device.
Expand Down
65 changes: 58 additions & 7 deletions docs/new-clients.txt
Original file line number Diff line number Diff line change
Expand Up @@ -52,27 +52,78 @@ and commands with an object-oriented API in C++ and C.
For more information, refer to the linkman:libnutclient[3] manual page.

#include <iostream>
#include <unistd.h>
#include <stdlib.h>

#include <nutclient.h>

using namespace nut;
using namespace std;

int main(int argc, char** argv)
int main(int argc, char **argv)
Copy link
Copy Markdown
Member

@aquette aquette Mar 11, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@boricj nitpicking somehow, but:
a comment here in the code would be helpful to depict that
// argv[1] is the mandatory NUT device name (@localhost), used to list variables from
// argv[2] is an optional command. When provided, it will be executed and possibly with status tracking enabled
I'll merge the PR and do it myself to avoid another 1d delay, so FYI and for future PR ;)

{
Client *client;
try
{
// Connection
Client* client = new TcpClient("localhost", 3493);
Device mydev = client->getDevice("myups");
cout << mydev.getDescription() << endl;
Variable var = mydev.getVariable("device.model");
cout << var.getValue()[0] << endl;
client = new TcpClient("localhost", 3493);

if (argc >= 2)
{
// Reading data from device
Device mydev = client->getDevice(argv[1]);
cout << "Description: " << mydev.getDescription() << endl;
Variable var = mydev.getVariable("device.model");
cout << "Model: " << var.getValue()[0] << endl;

if (argc >= 3)
{
// Authenticate to NUT server
const char *user = getenv("NUT_USER");
const char *password = getenv("NUT_PASSWD");
client->authenticate(user ? user : "", password ? password : "");

// Enable command tracking, if available
if (client->hasFeature(Client::TRACKING))
{
cout << "Server can do command tracking" << std::endl;
client->setFeature(Client::TRACKING, true);
}
else
{
std::cout << "Server can't do command tracking" << std::endl;
}

// Perform an asynchronous command
TrackingID id = mydev.executeCommand(argv[2]);
TrackingResult result;
do
{
sleep(1);
result = client->getTrackingResult(id);
}
while (result == PENDING);

// Display result of command
const char *output = "<UNRECOGNIZED>";
switch (result)
{
case SUCCESS: output = "SUCCESS"; break;
case FAILURE: output = "FAILURE"; break;
case UNKNOWN: output = "UNKNOWN"; break;
}
cout << "Command sent, result=" << output << endl;
}
}
}
catch(NutException& ex)
catch (NutException &ex)
{
cerr << "Unexpected problem : " << ex.str() << endl;
}
delete client;
return 0;
}


Configuration helpers
~~~~~~~~~~~~~~~~~~~~~
Expand Down
Loading