From b1331c849bfc676f5b049623e16a2e049ccf387d Mon Sep 17 00:00:00 2001 From: Cyril Danilevski <cydanil@gmail.com> Date: Wed, 16 Oct 2024 18:06:04 +0200 Subject: [PATCH] Accept MPOD channel as argument --- README.md | 3 +++ mpod.cpp | 59 +++++++++++++++++++++++++++++++------------------------ mpod.hpp | 25 ++++++++++++----------- panel.hpp | 1 + pins.cpp | 2 +- rest.cpp | 51 ++++++++++++++++++++++++++++++++++++++++------- rest.hpp | 5 +++-- 7 files changed, 99 insertions(+), 47 deletions(-) diff --git a/README.md b/README.md index 3244494..1a33f6f 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,9 @@ Available routes are: - `/idn`: provides information about firmware and hardware version, and uptime. It can be queried as a heartbeat, seeing the uptime increasing. +- `/poll?ch=1`: get status of a channel. + + ### Serial Interface The serial interface is available through the micro-usb port. diff --git a/mpod.cpp b/mpod.cpp index 7270e27..c7d66b6 100644 --- a/mpod.cpp +++ b/mpod.cpp @@ -14,42 +14,37 @@ MPOD::MPOD() { } -// Create an SNMP SETREQUEST message to setup MPOD -SNMP::Message* MPOD::setup() { - // Use read/write community, not read-only - SNMP::Message* message = new SNMP::Message(SNMP::Version::V2C, "guru", SNMP::Type::SetRequest); - // In SETREQUEST, use node type and set the value. - // OUTPUT SWITCH, integer type, 0 is OFF and 1 is ON. - message->add(OID::NAMES[OID::OUTPUTSWITCH], new IntegerBER(0)); - // OUTPUT VOLTAGE is an opaque float embedded in an opaque node - message->add(OID::NAMES[OID::OUTPUTVOLTAGE], new OpaqueBER(new OpaqueFloatBER(10.0))); - // OUTPUT CURRENT is an opaque float embedded in an opaque node - message->add(OID::NAMES[OID::OUTPUTCURRENT], new OpaqueBER(new OpaqueFloatBER(0.001))); - // OUTPUT VOLTAGE RISE RATE is negative. Don't ask me why... - message->add(OID::NAMES[OID::OUTPUTVOLTAGERISERATE], new OpaqueBER(new OpaqueFloatBER(-1.0))); - return message; -} - - // Create an SNMP GETREQUEST message -SNMP::Message* MPOD::read() { +// Create an SNMP GETREQUEST message +SNMP::Message* MPOD::read(uint16_t channel) { SNMP::Message* message = new SNMP::Message(SNMP::Version::V2C, "public", SNMP::Type::GetRequest); // In GETREQUEST, values are always of type NULL. - message->add(OID::NAMES[OID::OUTPUTSTATUS]); - message->add(OID::NAMES[OID::OUTPUTMEASUREMENTSENSEVOLTAGE]); - message->add(OID::NAMES[OID::OUTPUTMEASUREMENTCURRENT]); + String snmp_cmd = OID::NAMES[OID::OUTPUTSTATUS]; + snmp_cmd += channel; + message->add(snmp_cmd.c_str()); + + snmp_cmd = OID::NAMES[OID::OUTPUTMEASUREMENTSENSEVOLTAGE]; + snmp_cmd += channel; + message->add(snmp_cmd.c_str()); + + snmp_cmd = OID::NAMES[OID::OUTPUTMEASUREMENTCURRENT]; + snmp_cmd += channel; + message->add(snmp_cmd.c_str()); + return message; } - // Create an SNMP SETREQUEST message to switch on or off the MPOD -SNMP::Message* MPOD::output(const bool on) { +// Create an SNMP SETREQUEST message to switch on or off the MPOD +SNMP::Message* MPOD::output(const uint16_t channel, const bool on) { SNMP::Message* message = new SNMP::Message(SNMP::Version::V2C, "guru", SNMP::Type::SetRequest); // In SETREQUEST, use node type and set the value. // OUTPUT SWITCH, integer type, 0 is OFF and 1 is ON. - message->add(OID::NAMES[OID::OUTPUTSWITCH], new IntegerBER(on ? 1 : 0)); + String snmp_cmd = OID::NAMES[OID::OUTPUTSWITCH]; + snmp_cmd += channel; + message->add(snmp_cmd.c_str(), new IntegerBER(on ? 1 : 0)); return message; } - // Parse incoming message +// Parse incoming message bool MPOD::message(const SNMP::Message *message) { unsigned int found = 0; unsigned int index = 0; @@ -99,6 +94,12 @@ bool MPOD::message(const SNMP::Message *message) { found++; break; } + + // Get the channel ID from the last value in the varbind name + // No validation is done here as the varbind must be legal to get + // that far. + const char* channel = strrchr(varbind->getName(), '.') + 1; + _channel = atoi(channel); } // Return true if nodes found, that means this is a valid response from MPOD return found; @@ -136,6 +137,10 @@ float MPOD::getVoltageRiseRate() const { return _voltageRiseRate; } +uint16_t MPOD::getChannel() const { + return _channel; +} + // Use appropriate cast to get integer value unsigned int MPOD::getIntegerFromVarBind(const VarBind *varbind) { return static_cast<IntegerBER*>(varbind->getValue())->getValue(); @@ -148,6 +153,7 @@ float MPOD::getFloatFromVarBind(const VarBind *varbind) { String MPOD::toJSON() { String json = "\"status\":{\n"; + json += "\"channel\":" + String(getChannel()) + ","; json += "\"is_on\":" + String(isOn()) + ","; json += "\"is_up\":" + String(isUp()) + ","; json += "\"is_down\":" + String(isDown()) + ","; @@ -170,7 +176,8 @@ MPOD mpod; void onMessage(const SNMP::Message *message, const IPAddress remote, const uint16_t port) { if (mpod.message(message)) { Serial.println(); - Serial.print("MPOD status"); + Serial.print("MPOD status: "); + Serial.print(mpod.getChannel()); Serial.print(mpod.isOn() ? " on" : " off"); if (mpod.isUp()) { Serial.print(" up"); diff --git a/mpod.hpp b/mpod.hpp index 00dc724..a36d03f 100644 --- a/mpod.hpp +++ b/mpod.hpp @@ -27,20 +27,22 @@ class MPOD { }; static inline const char *NAMES[] = { - "1.3.6.1.4.1.19947.1.3.2.1.4.1", - "1.3.6.1.4.1.19947.1.3.2.1.5.1", - "1.3.6.1.4.1.19947.1.3.2.1.7.1", - "1.3.6.1.4.1.19947.1.3.2.1.9.1", - "1.3.6.1.4.1.19947.1.3.2.1.10.1", - "1.3.6.1.4.1.19947.1.3.2.1.12.1", - "1.3.6.1.4.1.19947.1.3.2.1.13.1", + // These are incomplete SNMP commands needing to be + // formatted with the channel id after the final dot + "1.3.6.1.4.1.19947.1.3.2.1.4.", + "1.3.6.1.4.1.19947.1.3.2.1.5.", + "1.3.6.1.4.1.19947.1.3.2.1.7.", + "1.3.6.1.4.1.19947.1.3.2.1.9.", + "1.3.6.1.4.1.19947.1.3.2.1.10.", + "1.3.6.1.4.1.19947.1.3.2.1.12.", + "1.3.6.1.4.1.19947.1.3.2.1.13.", }; // Returns index of OID equals to name // Returns UNKNOWN if none static unsigned int match(const char *name) { for (unsigned int index = 0; index < COUNT; ++index) { - if (strcmp(NAMES[index], name) == 0) { + if (strstr(name, NAMES[index])) { return index; } } @@ -50,9 +52,8 @@ class MPOD { // Create an SNMP SETREQUEST message to setup MPOD MPOD(); - SNMP::Message* setup(); - SNMP::Message* read(); - SNMP::Message* output(const bool); + SNMP::Message* read(const uint16_t); + SNMP::Message* output(const uint16_t, const bool); bool message(const SNMP::Message*); bool isOn() const; bool isUp() const; @@ -62,11 +63,13 @@ class MPOD { float getVoltage() const; float getCurrent() const; float getVoltageRiseRate() const; + uint16_t getChannel() const; String toJSON(); private: unsigned int getIntegerFromVarBind(const VarBind*); float getFloatFromVarBind(const VarBind*); + uint16_t _channel; bool _on; bool _up; bool _down; diff --git a/panel.hpp b/panel.hpp index ae6e2e0..3696a6d 100644 --- a/panel.hpp +++ b/panel.hpp @@ -38,6 +38,7 @@ String buildPanel(bool sib, bool plc, bool ups, unsigned long elapsed_secs) { page += "<h3>Last INT: "; page += elapsed_secs; + page += " secs. ago"; page += "</h3></div></body></html>"; return page; diff --git a/pins.cpp b/pins.cpp index d307577..0a0dc0c 100644 --- a/pins.cpp +++ b/pins.cpp @@ -68,6 +68,6 @@ void toggle_status_led() { if (millis() - toggle_start >= 1000) { toggle_start = millis(); on = !on; - MCP.write1(STATUS_LED_PIN, on); + MCP.write1(STATUS_LED_PIN, on); } } diff --git a/rest.cpp b/rest.cpp index 6ba0238..b3c5247 100644 --- a/rest.cpp +++ b/rest.cpp @@ -20,6 +20,7 @@ void initializeRoutes() { restServer.onNotFound(notFound); restServer.on("/send", sendSNMP); restServer.on("/", panel); + restServer.on("/poll", pollMPODChannel); restServer.begin(); Serial.println("REST Server Started"); @@ -128,6 +129,7 @@ enum { void sendSNMP() { uint8_t output = NONE; + uint16_t channel = 0; bool success = false; for (uint8_t i = 0; i < restServer.args(); i++) { @@ -137,32 +139,67 @@ void sendSNMP() { } else if (restServer.arg(i) == "off") { output = OFF; } - } + } else if(restServer.argName(i) == "ch") { + channel = restServer.arg(i).toInt(); + } } auto ipAddr = IPAddress(192,168,140,79); - if (output != NONE) { - SNMP::Message *snmp_msg = mpod.output(output); + if (output != NONE && channel != 0) { + SNMP::Message *snmp_msg = mpod.output(channel, output); snmp.send(snmp_msg, ipAddr, SNMP::Port::SNMP); delete snmp_msg; success = true; } + if (success) { + SNMP::Message *snmp_msg = mpod.read(channel); + snmp.send(snmp_msg, ipAddr, SNMP::Port::SNMP); + delete snmp_msg; + } + String http_msg = "{\n"; - http_msg += "\"target\":\"" + ipAddr.toString() + "\","; - http_msg += "\"arguments\":{\"output\":" + String(output) + "},\n"; + http_msg += "\"target\":\"" + ipAddr.toString() + "\",\n"; + http_msg += "\"arguments\":{"; + http_msg += "\"channel\":" + String(channel) + ","; + http_msg += "\"output\":" + String(output) + "},\n"; http_msg += "\"success\":" + String(success); + + if (success) { + http_msg += ",\n"; + http_msg += mpod.toJSON(); + } + http_msg += "\n}"; restServer.send(200, "text/json", http_msg); } void pollMPODChannel() { - auto ret = mpod.toJSON(); + uint16_t channel = 0; + for(uint8_t i = 0; i < restServer.args(); i++) { + if(restServer.argName(i) == "ch") { + channel = restServer.arg(i).toInt(); + } + } + + String ret; + if(!channel) { + ret = "\"reason\": \"Invalid channel\""; + } else { + auto ipAddr = IPAddress(192,168,140,79); + SNMP::Message *snmp_msg = mpod.read(channel); + snmp.send(snmp_msg, ipAddr, SNMP::Port::SNMP); + delete snmp_msg; + ret = mpod.toJSON(); // TODO: This has stale info at this stage! + } String http_msg = "{\n"; http_msg += ret; - http_msg += ",\"success\": 1"; + http_msg += ",\"success\": "; + http_msg += channel ? 1 : 0; + http_msg += ",\"channel\": "; + http_msg += channel; http_msg += "\n}"; restServer.send(200, "text/json", http_msg); diff --git a/rest.hpp b/rest.hpp index 4cc9039..c62ab9b 100644 --- a/rest.hpp +++ b/rest.hpp @@ -8,5 +8,6 @@ void initializeRoutes(); void identify(); // /idn void notFound(); // 404 void restart(); // /restart -void sendSNMP(); // /send?output=on -void panel(); //show a status page +void sendSNMP(); // /send?ch=107&output=on +void panel(); // / show a status page +void pollMPODChannel(); // /poll?ch=1 -- GitLab