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