SNMP Manager — несколько IP-адресов — проблема

У меня есть несколько SNMP-устройств в одной сети, и я пытаюсь запросить через номера OID каждое SNMP-устройство и назначить ответ в массив. Когда я запрашиваю один IP-адрес, я получаю правильный ответ, как и ожидалось.

Проблема: Если я запускаю один и тот же код в цикле for (несколько IP-адресов 192.168.1.150, 192.168.1.151 и т. д.), я получаю повторяющуюся информацию. Возможно, я использую неправильный метод обратного вызова, или диспетчер SNMP не может динамически изменить IP-адрес. Я изучил множество тем и испробовал множество способов, но не могу пройти мимо этого пункта.

Если кто-нибудь может мне помочь, это очень поможет!!


Пример вывода: Название модели и серийный номер должны быть разными для каждого IP-адреса

----------------------
192.168.8.150
KONICA MINOLTA bizhub C658
A79J021002278
----------------------
192.168.8.151
KONICA MINOLTA bizhub C658
A79J021002278
----------------------
192.168.8.152
KONICA MINOLTA bizhub C658
A79J021002278
----------------------
192.168.8.153
KONICA MINOLTA bizhub C658
A79J021002278
----------------------
192.168.8.154
KONICA MINOLTA bizhub C658
A79J021002278

Вот код

#if defined(ESP8266)
#include <ESP8266WiFi.h> // Основная библиотека WiFi ESP8266
#else
#include <WiFi.h>        // Основная библиотека Wi-Fi ESP32
#endif

#include <string.h>
#include <WiFiUdp.h>
#include <Arduino_SNMP_Manager.h>

//******************************************
//* Информация о вашем WiFi *
//******************************************
const char *ssid = "Workmin_Sales";
const char *password = "workmin92429242";
//******************************************

//******************************************
//* Информация об устройстве SNMP *
//******************************************
const char *community = "public";
const int snmpVersion = 1; // Версия SNMP 1 = 0, Версия SNMP 2 = 1
// OID
const char *oidModelName = ".1.3.6.1.2.1.25.3.2.1.3.1";               // OctetString SysName
const char *oidSerialNumber = ".1.3.6.1.2.1.43.5.1.1.17.1";              // OctetString SysName
//******************************************

//******************************************
//* Настройки *
//******************************************
int pollInterval = 20000; // задержка в миллисекундах
//******************************************

//******************************************
//* Инициализация *
//******************************************
// Переменные
char *modelNameArray[5];
char *serialNumberArray[5];

char modelName[50];
char *modelNameResponse = modelName;
char serialNumber[50];
char *serialNumberResponse = serialNumber;

// Пустой указатель обратного вызова для каждого OID
ValueCallback *callbackSerialNumber;
ValueCallback *callbackModelName;
//******************************************

unsigned long pollStart = 0;
// SNMP-объекты
WiFiUDP udp;                                           // Объект UDP, используемый для отправки и получения пакетов
SNMPManager snmp = SNMPManager(community);             // Запускаем SMMPManager для прослушивания ответов на get-запросы
SNMPGet snmpRequest = SNMPGet(community, snmpVersion); // Запускает экземпляр SMMPGet для отправки запросов


//******************************************
//* Объявления функций *
//******************************************
void getSNMP();
//******************************************

void setup()
{
  Serial.begin(115200);
  WiFi.begin(ssid, password);
  Serial.println("");
  // Ждем соединения
  while (WiFi.status() != WL_CONNECTED)
  {
    delay(500);
    Serial.print(".");
  }
  Serial.println("");
  Serial.print("Connected to SSID: ");
  Serial.println(ssid);
  Serial.print("IP address: ");
  Serial.println(WiFi.localIP());

  snmp.setUDP(&udp); // даем snmp указатель на объект UDP
  snmp.begin();      // запускаем SNMP-менеджер
}

void loop()
{
  // поместите сюда ваш основной код для многократного запуска:
  snmp.loop();
  if (millis() - pollStart >= pollInterval)
  {
    pollStart += pollInterval; // это предотвращает дрейф в задержках
    
    getSNMP();
    // doSNMPCalculations(); // Делаем что-то с собранными данными
  }
}

void getSNMP()
{
  for (int i = 0; i < 5; i++) {
    // Создаем SNMP-запрос на получение, добавляем каждый OID в запрос
    IPAddress router(192, 168, 8, i + 150);

    // Получаем обратные вызовы от создания обработчика для каждого из OID
    callbackSerialNumber = snmp.addStringHandler(router, oidSerialNumber, &serialNumberResponse);
    callbackModelName = snmp.addStringHandler(router, oidModelName, &modelNameResponse);

    snmpRequest.addOIDPointer(callbackSerialNumber);
    snmpRequest.addOIDPointer(callbackModelName);


    snmpRequest.setIP(WiFi.localIP()); // IP прослушивающего MCU
    // snmpRequest.setPort(501); // По умолчанию используется UDP-порт 161 для SNMP. Но при необходимости можно переопределить.
    snmpRequest.setUDP(&udp);
    snmpRequest.setRequestID(rand() % 5555);
    snmpRequest.sendTo(router);
    snmpRequest.clearOIDList();
    delay(1000);

    modelNameArray[i] = modelNameResponse;
    serialNumberArray[i] = serialNumberResponse;
    Serial.print("192.168.8.");
    Serial.println(i + 150);
    Serial.println(modelNameArray[i]);
    Serial.println(serialNumberArray[i]);
    Serial.println("----------------------");
  }
}

, 👍1


2 ответа


Лучший ответ:

1

Думаю, я вижу несколько проблем в приведенном выше примере.

  1. snmpRequest.sendTo(router); отправит SNMP-пакет. Но вам нужно вызвать snmp.loop(), чтобы прочитать входящие ответы на ваш запрос. Поэтому вам нужно реструктурировать код, чтобы вы могли вызывать loop() потенциально много раз, чтобы ждать входящих ответов, прежде чем читать и сохранять значения.
  2. serialNumberResponse — указатель на массив символов serialNumber (аналогично для модели). Если обратный вызов возвращает серийный номер 123ABC в первый раз, а следующее устройство называется 1XD, тогда массив будет иметь значение 1XDABC. Поэтому, если вы планируете повторно использовать переменную, вам нужно очищать ее между использованиями, например: memset(serialNumber, 0, sizeof(serialNumber));
  3. Поскольку вы не всегда можете получить успешный ответ или ответ в отведенное время, значение может оставаться пустым. Если вы хотите сохранить значения в другой переменной, вы можете убедиться, что они не пусто, так как это перезапишет значение, которое вы могли получить при предыдущем успешном вызове. Если вы имели дело с числами, значение также может быть значением предыдущего вызова. Как указано в ответе выше, вы имеете дело с указателями, поэтому, если вы хотите сохранить значение, вам нужно скопировать значение, иначе вы просто укажете на тот же указатель.

Я надеюсь, что это поможет указать вам правильное направление. Я добавил в библиотеку пример, показывающий, как можно запрашивать несколько устройств и сохранять результаты в массиве, см.: https://github.com/shortbloke/Arduino_SNMP_Manager/tree/master/examples/ESP_Multiple_SNMP_Device_Polling. Он сохраняет ответы непосредственно в массив. Это может привести к очистке значений в случае тайм-аута, я еще не проверял это полностью.

Мартин

,

Большое спасибо, Мартин. Я попробовал пример, и он работает без проблем! Когда у меня будут деньги, я куплю тебе пива :), @Christo Deyzel


1

Вы не сохраняете данные в своем массиве, вы сохраняете указатель на хранилище, где обратный вызов хранит свои данные. Таким образом, каждая запись получает один и тот же указатель на одно и то же хранилище, содержимое которого изменяется. Таким образом, все элементы массива видят одно и то же (последнее) содержимое.

Мне потребовалось много лет, чтобы как следует разобраться с указателями C.

Вместо того, чтобы просто присвоить (=) значение, нужно скопировать содержимое.

Поскольку у вас есть только массив указателей, это можно сделать с помощью strdup(), который выделяет новый блок ОЗУ и копирует в него содержимое. Обязательно используйте free() для этого выделенного блока, когда закончите с ним.

  • стрдуп
,