Когда-то давно, в 2012–2014 годах, мне и коллегам понадобилось собирать различные данные с большого числа различных коммутаторов и прочего сетевого оборудования.
В то время у нас были в основном коммутаторы Cisco немного Moxa и немного HP.
Мониторилось все это при помощи PRTG и Nagios, а для сбора данных мы использовали ПО switchmap для Cisco и собственные скрипты на PHP для Moxa. ПО в целом выполняло свою функцию, информация собиралась и помогала в работе.
Однако с течением времени количество коммутаторов и маршрутизаторов увеличивалось, появилось оборудование других производителей, которое switchmap опросить уже не мог, так же возникали различные проблемы и с Moxa, плюс ко всему SNMP версии 2C требовалось по возможности больше не использовать, перейдя на версию 3 (актуальная по сей день).
Так же хотелось сохранять данные в базе данных и/или в файлах формата XML/JSON.
И конечно хотелось параллельного опроса множества коммутаторов.
Имея опыт в написании ПО на Go с использованием горутин, я предположил, что Go идеальный кандидат на язык и рантайм для подобного ПО и стал искать подходящую библиотеку для опроса оборудования по протоколу SNMP.
Остановился я на библиотеке WebNMS от Zoho, она коммерческая, но была и бесплатная версия (если что, нвйти по ней информацию чегодня практический невозможно).
На ее базе я довольно быстро смог создать вполне неплохо работающее ПО, которое позволяло собирать данные по SNMP v3 со множества различных коммутаторов параллельно.
Однако довольно быстро обнаружились проблемы: с некоторого оборудования данные получить не удавалось – я получал ошибку парсинга ASN.1 (тут поясню для тех, кто глубоко не знает SNMP. Данные в SNMP кодируются при помощи ASN.1 BER, и именно то, что это BER приводит к проблемам, которые я опишу ниже).
Так же периодически получал различные друге ошибки, которые требовалось исправлять в коде библиотеки.
И можно было бы перейти на коммерческую версию и полагать что Zoho исправит ошибки в рамках поддержки, но Zoho вообще прекратили ее поддерживать, а теперь про нее ничего не может найти даже гугл.
Закопавшись в исходниках библиотеки я стал понимать, что для того, чтоб что-то там исправить, помимо понимания структуры библиотеки, необходимо еще и глубокие знания SNMP и ASN.1. Моих знаний на тот момент было недостаточно и пришлось их форсированно пополнить.
В итоге я принял решение написать свою библиотеку SNMP v3, но начал, разумеется, с версии 2C.
Затем я потратил много времени на погружение в аутентификацию и шифрование (на тот момент у меня были не нулевые знания в этой области, но их было недостаточно) и следующим этапом было добавление поддержки 3 версии SNMP в библиотеку. Я добавил только шифрование AES-128 и аутентификацию SHA и на этом ограничился.
Итогом этого труда стало то, что я с коллегами смог опрашивать все наше оборудование, а там, где возникали проблемы мы их оперативно исправляли.
Очень скоро оказалось что некоторое оборудование поддерживает только DES и MD5, пришлось добавить и их.
В 2025 году мне понадобилось добавить поддержку шифрования AES-256, и я решил так же немного сделать рефакторинг кода, а за одно довести его до состояния, когда им будет не стыдно делиться и в итоге опубликовал его на Github под лицензией MIT.
А в этой статье я попробую раскрыть детали проекта и возможно кому-то моя библиотека пригодится.
Итак приступим.
Любая библиотека SNMP должна уметь кодировать и декодировать данные в формате ASN.1 BER. Нам придется немного погрузиться в этот стандарт.
Минимально что нужно знать – ASN.1 это бинарное кодирование данных, в виде последовательности тип, длина, значение (Type Length Value или TLV), существует множество вариантов такого кодирования, но нас интересует BER – базовое и DER строгое, и то что парсер ASN.1 в Go это парсер именно DER (и именно DER применяется в криптографии).
Но в SNMP применяется BER и напрямую, стандартный парсер Go ASN.1 применить можно, но очень скоро мы столкнемся с оборудованием, декодировать данные которого не сможем.
Тут замечу что в обратную сторону это работает - то есть закодировав запрос DER кодированием, оборудование его поймет.
Очень важное и ключевое отличие между BER и DER это как кодируется длина.
В DER есть правило, ели длина меньше 127 то кодируем одним байтом, в BER же это правило можно не соблюдать и кодировать длину, например в 5 байт двумя байтами, зачем спросите вы? Ну например для того, чтоб структура данных была фиксированной длины, или еще по какой-то причине, это вопросы к производителям оборудования, например к Moxa.
Из этого следует что придется либо использовать сторонний парсер ASN.1, либо писать свой.
Я решил просто модифицировать стандартный парсер, ослабив проверки и введя поддержку еще одного формата длины – Indefinite, это когда в конце данных есть маркер окончания, длину в такой форме в SNMP я никогда не видел, но решил ее поддержку тоже добавить.
Так появился форк стандартного парсера ASN.1, с которым можно ознакомиться по ссылке.
Итак, кодер/декодер есть, теперь нужно реализовать:
Для версии 2c
формирование запроса
Парсинг ответа
Посылку/прием запроса/ответа, повторную посылку в случае неполучения ответа, таймауты
Функции Get, Set, GetNext
Поддержку Bulk запросов/ответов
Высокоуровневый Walk
Служебные функции, такие как обнаружение выхода из ветки для завершения Walk, инициализацию и прочее
Для версии 3 в дополнение к тому, что есть у 2c
Шифрование/Дешифрование
Аутентификация
Процедура Discovery
Обработка Report сообщений
Работа со специфичными для 3 версии функциями – например в версии 3 можно сообщить передатчику сколь данных максимально мы можем принять, и тогда буфер, в который будет прочитана датаграмма, можно ограничить этим объемом сэкономив память.
Так же нужно реализовать прием trap/inform как для версии 2c, так и для 3 версии и еще многое другое.
Если вас утомил текст то вы можете ознакомится с проектом по ссылке и далее не читать, а если же нет, то далее я коротко опишу чем проект интересен, а следующим этапом погрузимся в технические детали, но это будет уже вторая статья (если конечно вы мне не напишите в коментариях - нет, не интересно и не нужно продолжение). А пока опишу чем проект интересен и какие есть альтернативы.
И так сильные (на мой взгляд) стороны библиотеки:
Использование слегка модифицированного парсера ASN.1 из стандартной библиотеки.
Почему это преимущество? Потому что это родной Go парсер, он хорошо оттестирован и широко используется, поскольку модификации минимальные и хорошо описаны, их легко можно внести в более новые версии стандартного парсера. Так же используется традиционный Go способ кодировки/декодировки – marshal/unmarshal.
Синхронизация Boots/Time выполняется при выполнении процедуры Discovery и при получении Report сообщения с ошибкой notInTimeWindows. Почему это как мне кажется хорошо? Давайте немного подробнее разберемся как это возможно реализовать:
И так для того, чтоб ваш запрос к устройству был валидный, вам помимо знания Username, протоколов и ключей аутентификации и шифрования а так же Engine ID, надо еще указать два параметра – Boots (количество перезагрузок устройства) и Time (время в секундах, с момента включения устройства).
Boots должен точно совпадать а Time может отличаться не более чем на ±150 секунд
Если этот параметр однократно инициализировать при Discovery, то спустя 150 секунд мы получим Report с ошибкой notInTimeWindows.
Если же этот параметр инкрементировать при каждом запросе, то если сеть быстрая, а запросов много, то может так случиться, что счетчик, наоборот, убежит вперед и тоже станет не валидным.
Можно, конечно, использовать реальное время, а можно просто при получении Report notInTimeWindows, произвести синхронизацию и повторить запрос.
Обработка Report с ошибкой usmStatsUnknownEngineIDs (выполнение повторного Discovery).
Ошибки разделены на фатальные и не фатальные, так же содержат детальную информацию. Это требует отдельного описания, оно будет ниже.
Компактный код прост для понимания.
Теперь остановимся подробнее на процессе Discovery и обработке Report сообщений, разумеется, это все относится только к версии 3 протокола.
Что же такое Discovery и зачем она нужна? Для того чтоб отправить запрос, например Get, понадобится указать Username, Engine ID, Boots, Time, Auth parameter, Priv parameter. Из всего этого на начальном этапе известен лишь Username, потому что для вычисления Auth/Priv параметров, и создания корректного запроса необходимо знать Engine ID, Boots и Time.
Чтоб получить эти данные пошлем Get запрос с пустыми, всеми этими полями:

В ответ должен придет Report с ошибкой usmStatsUnknownEngineIDs (OID 1.3.6.1.6.3.15.1.1.4.0):

Из этого сообщения мы можем извлечь сразу и Engine ID и Boots/Time.
Данная процедура выполняется до начала обмена данными, однако же, если у вас какой то очень протяженный по времени опрос, и в какой то момент Engine ID на устройстве сменился, нужно обработать эту ситуацию и произвести определение Engine ID заново, а значит и сгенерировать новый ключевой материал, так как он зависит от Engine ID.
К обработке report сообщений мы еще вернемся, а пока перейдем к обработке ошибок при, например отсутствии запрашиваемого OID на оборудовании.
Проблема актуальна только для GET в котором запрашиваются данные сразу по нескольким OID’ам, суть проблемы:
Рассмотрим случай, в котором мы хотим получить данные по нескольким OID’ам:
"1.3.6.1.2.1.1.5.0", // sysName "1.3.6.1.2.1.1.6.0", // sysLocation "1.3.6.1.2.1.1.99.0", // Ошибочный OID (вернет noSuchObject) "1.3.6.1.2.1.1.100.0", // Ошибочный OID (вернет noSuchObject)
Вот так выглядит Get запрос:

А так, ответ на него:

То есть формально, SNMP ошибки нет, однако запрашиваемые данные содержат значения noSuchObject. И обычно, это должен обработать пользователь библиотеки.
В PowerSNMPv3 происходит все иначе, Функция SNMP_GetMulti вернет только те данные, которые существуют, и вернет ошибку, ошибка структурирована и есть специальная функция ParseError, которая возвращает информацию, фатальная была ошибка или нет, список ошибочных OID и причину ошибки, и еще некоторую полезную информацию.
Что касается операции Set то тут ситуация абсолютно другая. Set операция атомарная, и если не удалось установить один из OID’ов то вся операция провалена и ничего не будет модифицировано.
Соответственно ошибка будет всегда фатальная.
Так же если мы выполним GET с несколькими OID’ами и все будут с ошибкой, ошибка тоже будет фатальная. Давайте посмотрим на SET:

И ответ на него:

Тут уже поле error-status указывает на тип ошибки, а error-index на тот OID (начиная с 1) по которому и произошла ошибка, если в списке будут другие ошибочные OID мы ничего о них не узнаем, индекс только один.
Однако в данных, которые прислал коммутатор, выглядит все так как будто все применилось.
И некоторые библиотеки отдают эти данные пользователю. PowerSNMPv3 же в этом случае, вернет пустые данные.
В этой статье, чтобы она не распухала, я ограничусь очень примитивным демонстрационным кодом, а подробный разбор сделаю в продолжении если читателю это вообще будет интересно.
Итак, напишем небольшой тест того, что выше описано.
package main import ( "flag" "fmt" PowerSNMP "github.com/OlegPowerC/powersnmpv3" ) func main() { //Параметры командной строки Host := flag.String("h", "", "Switch or routers IP") SNMPuser := flag.String("u", "", "SNMP v3 USER") SNMPauthProtocol := flag.String("a", "", "SNMP auth protocol") SNMPauthPassword := flag.String("A", "", "SNMP auth password") SNMPprivProtocol := flag.String("x", "", "SNMP priv protocol") SNMPprivPassword := flag.String("X", "", "SNMP priv password") flag.Parse() //Осздаем описание коммутатора var dev PowerSNMP.NetworkDevice dev.IPaddress = *Host dev.SNMPparameters.SNMPversion = 3 dev.Port = 161 dev.SNMPparameters.Username = *SNMPuser dev.SNMPparameters.AuthProtocol = *SNMPauthProtocol dev.SNMPparameters.AuthKey = *SNMPauthPassword dev.SNMPparameters.PrivProtocol = *SNMPprivProtocol dev.SNMPparameters.PrivKey = *SNMPprivPassword //Инициализируем SNMP Ssess, InitErr := PowerSNMP.SNMP_Init(dev) if InitErr != nil { fmt.Println(InitErr) return } fmt.Println("=== выполним GET для единичного OID ===") swrongoid := "1.3.6.1.2.1.1.99.0" swrongoidiarr, parseerr := PowerSNMP.ParseOID(swrongoid) if parseerr != nil { fmt.Println(parseerr) return } GetsingleRes, GetsingleErr := Ssess.SNMP_Get(swrongoidiarr) if GetsingleErr != nil { snmpErr, commonErr := PowerSNMP.ParseError(GetsingleErr) if commonErr != nil { // Ошибка связанная с недоступностью хоста, шифрованием, еще чем то подобным fmt.Println("ошибка Network/system:", GetsingleErr) return } if snmpErr.IsFatal { // Фатальная SNMP ошибка fmt.Println("Фатальная ошибка SNMP, результат недоступен") for _, descr := range snmpErr.Oids { fmt.Println("Описание ошибки:", descr.ErrorDescription, descr) } } else { // Ошибка не фатальная fmt.Printf("Частичная ошибка, %d ошибочные OID'ы:", len(snmpErr.Oids)) for _, oidErr := range snmpErr.Oids { fmt.Printf(" | %s", oidErr.ErrorDescription) } } fmt.Println("") } else { fmt.Println("--- Результат одиночного GET ---") for _, wl := range GetsingleRes { fmt.Println(PowerSNMP.Convert_OID_IntArrayToString_RAW(wl.RSnmpOID), "=", PowerSNMP.Convert_Variable_To_String(wl.RSnmpVar), ":", PowerSNMP.Convert_ClassTag_to_String(wl.RSnmpVar)) } } fmt.Println("=== выполним GET с несколькими OID'ами ===") OidsStrings := []string{"1.3.6.1.2.1.1.6.0", "1.3.6.1.2.1.1.99.0", "1.3.6.1.2.1.1.5.0", "1.3.6.1.2.1.1.100.0"} OidsConverted := []PowerSNMP.SNMP_Packet_V2_Decoded_VarBind{} for _, OidSting := range OidsStrings { Ioid, IoidErr := PowerSNMP.Convert_OID_StringToIntArray_RAW(OidSting) if IoidErr != nil { fmt.Println(IoidErr) return } OidsConverted = append(OidsConverted, PowerSNMP.SNMP_Packet_V2_Decoded_VarBind{Ioid, PowerSNMP.SNMPvbNullValue}) } GetRes2, verr2 := Ssess.SNMP_GetMulti(OidsConverted) fmt.Println("--- Проверим ошибки ---") if verr2 != nil { snmpErr, commonErr := PowerSNMP.ParseError(verr2) if commonErr != nil { // Ошибка связанная с недоступностью хоста, шифрованием, еще чем то подобным fmt.Println("ошибки Network/system:", commonErr) } if snmpErr.IsFatal { // Фатальная SNMP ошибка fmt.Println("Фатальная ошибка SNMP, результат недоступен") for _, descr := range snmpErr.Oids { fmt.Println("Описание ошибки:", descr.ErrorDescription, descr) } } // Ошибка не фатальная, результат каой то доступен fmt.Printf("Частичная ошибка, %d ошибочные OID'ы:", len(snmpErr.Oids)) for _, oidErr := range snmpErr.Oids { fmt.Printf(" | %s", oidErr.ErrorDescription) } fmt.Println("") } else { fmt.Println("- Нет ошибок -") } fmt.Println("--- Результаты ---") for _, wl := range GetRes2 { fmt.Println(PowerSNMP.Convert_OID_IntArrayToString_RAW(wl.RSnmpOID), "=", PowerSNMP.Convert_Variable_To_String(wl.RSnmpVar), ":", PowerSNMP.Convert_ClassTag_to_String(wl.RSnmpVar)) } fmt.Println("=== операция SET нескольких OID ===") VarData := []PowerSNMP.SNMPVar{PowerSNMP.SetSNMPVar_OctetString("Test 6.0"), PowerSNMP.SetSNMPVar_OctetString("Test 99.0"), PowerSNMP.SetSNMPVar_OctetString("Test 5.0")} SetStringOids := []string{"1.3.6.1.2.1.1.6.0", "1.3.6.1.2.1.1.99.0", "1.3.6.1.2.1.1.5.0"} SetDataVB := []PowerSNMP.SNMP_Packet_V2_Decoded_VarBind{} if len(VarData) == len(SetStringOids) { for VdataInd, StoidS := range SetStringOids { IoidS, IoidErrS := PowerSNMP.Convert_OID_StringToIntArray_RAW(StoidS) if IoidErrS != nil { fmt.Println(IoidErrS) return } SetDataVB = append(SetDataVB, PowerSNMP.SNMP_Packet_V2_Decoded_VarBind{IoidS, VarData[VdataInd]}) } } else { fmt.Println("Не равно количество oid и данных") return } sdata, verres3 := Ssess.SNMP_SetMulti(SetDataVB) fmt.Println("--- Проверим ошибки ---") if verres3 != nil { snmpErr, commonErr := PowerSNMP.ParseError(verres3) if commonErr != nil { // Ошибка связанная с недоступностью хоста, шифрованием, еще чем то подобным fmt.Println("ошибки Network/system:", commonErr) } if snmpErr.IsFatal { // Фатальная ошибка fmt.Println("Фатальная ошибка SNMP, операция провалена") for _, descr := range snmpErr.Oids { fmt.Println("Описание ошибки:", descr.ErrorDescription) } } else { // Частичная ошибка, но ее не может быть при операйии SET fmt.Printf("Частичная ошибка, %d ошибочные OID'ы:", len(snmpErr.Oids)) for _, oidErr := range snmpErr.Oids { fmt.Printf(" | %s", oidErr.ErrorDescription) } } fmt.Println("") } else { fmt.Println("- Нет ошибки -") } fmt.Println("--- Результат, который вернул SET ---") for _, wl := range sdata { fmt.Println(PowerSNMP.Convert_OID_IntArrayToString_RAW(wl.RSnmpOID), "=", PowerSNMP.Convert_Variable_To_String(wl.RSnmpVar), ":", PowerSNMP.Convert_ClassTag_to_String(wl.RSnmpVar)) } }
И выполним, указав тестовый коммутатор и все нужные параметры, результат должен быть примерно такой:
=== выполним GET для единичного OID === Фатальная ошибка SNMP, результат недоступен Описание ошибки: 1.3.6.1.2.1.1.99.0 (status=128): NoSuchObject {[1 3 6 1 2 1 1 99 0] 128 1.3.6.1.2.1.1.99.0 (status=128): NoSuchObject} === выполним GET с несколькими OID'ами === --- Проверим ошибки --- Частичная ошибка, 2 ошибочные OID'ы: | 1.3.6.1.2.1.1.99.0 (status=128): NoSuchObject | 1.3.6.1.2.1.1.100.0 (status=128): NoSuchObject --- Результаты --- 1.3.6.1.2.1.1.6.0 = Test location : Universal OCTET STRING 1.3.6.1.2.1.1.5.0 = powercsw01.powerc : Universal OCTET STRING === операция SET нескольких OID === --- Проверим ошибки --- Фатальная ошибка SNMP, операция провалена Описание ошибки: 1.3.6.1.2.1.1.99.0 (status=11): CannotCreateVariable --- Результат, который вернул SET ---
Полезно выполнить в режиме отладки, поставить точки останова а так же запустить Wireshark.
В Wireshark можно указать параметры SNMP и он будет дешифровать данные и отображать расшифрованные.
Так же в библиотеку включен интеграционный тест.
Думаю на этом первую чась я закончу и милости просим в коментарии для обсуждения.
Комментарии (22)

dyadyaSerezha
07.05.2026 19:48Прочитал только водную часть, так как не сетевик. Два комментария.
Спасибо, что есть объяснение того, почему и зачем вообще была создана библиотека. Но.
Чисто по логике, это же одна из стандартнейших задач любой большой фирмы. Неужели нет хотя бы десятка разных open source и десятка коммерческих библиотек или уже готовых продуктов по теме? Прямо странно.
А так, молодец.

aborouhin
07.05.2026 19:48Есть gosnmp, и на других площадках (раз, два), но почему-то не тут, автор даже подробно описал отличия своей библиотеки от gosnmp. Не проще ли было сделать PR в gosnmp для решения этих нюансов - другой вопрос...

OlegPowerC Автор
07.05.2026 19:48На мой взгляд у gosnmp слишком "другая" архитектура.
Тогда уж snmpgo - куда более правильная.
И опять же, создание своего велосипеда дает такой опыт который по другому получить крайне сложно. Академический интерес не всегда плохо.

aborouhin
07.05.2026 19:48Тогда уж snmpgo - куда более правильная.
Но 132 звезды и, главное, последний коммит 9 лет назад :( Обычно выбираешь что-то хотя бы относительно популярное и точно активно поддерживаемое. А тут явно заброшенный проект, разве что форкнуть и поддерживать дальше самому - но тогда уж и правда проще своё с нуля, понимаю.

OlegPowerC Автор
07.05.2026 19:48Но тем не менее, оно реально хорошо сделано. Если пробежаться по коду то там все достойно. Но честно скажу - ее я особо не тестировал глубоко. Можно будет сделать сравнительные тесты.

victor_1212
07.05.2026 19:48мне тоже интересно, хотя бы потому, что в свое время rmon написал с нуля включая snmp и ip stack, примерно лет 30+ назад, на С конечно, proprietary mibs тоже достаточно для сетевого оборудования когда это было нужно, в далеком прошлом, собственно rmon2 примерно за пол года :)

OlegPowerC Автор
07.05.2026 19:48Ну что тут сказать - только выскажу свое почтение.
Я тоже лет 15 назад писал TCP/IP стек под Renesas RX да потом забросил.
Сейчас иногда вспоминаю и пытаюсь доделать.
Делал его чуть продвинутее чем uIP и чуть компактнее чем lwIP
Сейчас конечно скажут зачем когда есть CyclonTCP и lwIP в целом неплох.....

victor_1212
07.05.2026 19:48спасибо, но на самом деле это немного, типа 1-2 строки в резюме, последнее место работы Ciena

dyadyaSerezha
07.05.2026 19:48При чем тут архитектура и академический? Этого в статье как раз и нет, поторопился я с похвалой, выходит)
В статье написано, что библиотека была сделана с чисто прагматической целью.

OlegPowerC Автор
07.05.2026 19:48Так я все написал, причины создания были:
WebNMS имела проблемы а на нее уже было много всего завязано.
gosnmp не понравился по разным пичинам
Неплохое знание ASN.1 и прочих вещей позволило в течение нескольких месяцев создать свою библиотеку
Своя понятнее
gosnmp не нравится парсер ASN.1, не нравится приемник трапов, не нравится работа со строками, не нравится работа с репортами, не нравится работа с буферами, не нравится работа с ошибками и еще много много чего
Есть у меня тесты и того и того с Элтексом которые gosnmp проваливает
Есть тесты с другими девайсами.
Да цель - свой продукт и он есть и не один.
Вот тут я немного рассказал об этом
https://youtu.be/bI66ejMpPnU?list=PLh8giz78WRMzJJIE6DQ2mQQN-b72FtZv8
Возможно кто-то из читателей там был даже (точнее знаю, точно был :-) ).

OlegPowerC Автор
07.05.2026 19:48Добрый вечер.
Большинство использует для высокопроизводительных систем Net-SNMP, SNMP++
Java 'вскую JNMP4J
Python - тут есть из чего выбрать: нативнцй питон, обертка над net-snmp
На Go есть gosnmp и snmpgo
Теперь про коммерческие - есть несколь джаваских и C#
Я свою делал потому что столкнулся с проблемами с некоторыми коммутаторами, но прежде всего был академический интерес.
А - забыл, есть еще что-то на расте, и вот это интересно
Go чем хорош? конечно же множим горутины :-) ну и без зависимостей

OlegPowerC Автор
07.05.2026 19:48Нейронка использовалась только для генерации GoDoc и английского ридми. Еще как код ревью .
Клод .
Код писался без ИИ .
Могу за каждую строчку ответить

OlegPowerC Автор
07.05.2026 19:48Пока в виде комментария добавлю скриншоты из Wireshark как GoSNMP и PowerSNMPv3 работает с буфферами и параметром maxMsgSize:

GoSNMP 
PowerSNMPv3 
GetBulk PowerSNMPv3 
И ответ А теперь зададим MaxMsgSize как 1200

Запрос 
Ответ Разумеется придется разное количество запросов делать - так как в результатах будет разное количество данных.
gerbert_MX
вы не везде нейрослоповые комментарии убрали/удалили в репозитории
часть кода (большая часть cmd) выглядит как будто его даже не вычитывали после нейронки
возможно для cmd-части это нормально, чисто как пример нагенерили, но это уже нехороший звоночек, учитывая что нигде в реадмишках не сказано что юзалась нейросеть.
На будушее лучше сразу указывать если юзалась нейросеть, где и для чего конкретно. Повышает доверие. А то заметив нейрослоп в одном месте, сразу начинаешь считать нейрослоповым все, хотя судя по коду оно явно руками писалось, как минимум часть
OlegPowerC Автор
Нейронка использовалась только для генерации GoDoc и английского ридми. Еще как код ревью .
Клод .
Код писался без ИИ .
Могу за каждую строчку ответить
gerbert_MX
возможно комментарии и ввели в заблуждение
только если по самому модулю видно что просто комменты нагенерено (надо быть очень упорным что бы так расписать каждый метод), то вот именно в cmd не просто методы, но еще и "пошаговые" комментарии, причем пошаговость в плане написания а не выполнения.
Вот например https://github.com/OlegPowerC/powersnmpv3/blob/c4828e37b4609ab587e17ed79942741f34aaa673/cmd/c98800aps/c9800aps.go#L56
Такое нейонка не пишет на просьбу "прокомментируй код", такое нейронка пишет когда сама создает код с подробными комментариями.
OlegPowerC Автор
Не верите? а это я писал :-) решил прям расписать.
И статью пробовал показать ИИ - ну GPT сказал надо все исправить и переделать.
Я разумеется не стал. Мне кажется ценность "человеческого" материала важнее чем опечатки. Ну и любой ИИ скажет - слабая подача, нет сравнения и прочее.
Но я специально не намерен писать что эта библиотека превосходит другие, я попытался скорее описать чем она интересна .
PS:
Кстати snmpgo не заслуженно непопулярна - тоже рекомендую, писал какой то японец.
dyadyaSerezha
Кстати, не пропущена ли заглавная буква в Prtgbody?
И почему функция “выход из программы по ошибке”, то есть, главное действие “выход”, судя по коду и описанию, называется ErrorInProgram?
С одной стороны, вроде придираюсь, а с другой стороны, если без текста самой этой функции, то совершенно непонятно, что она делает при вызовах.
OlegPowerC Автор
Ну эта функция нужна чтоб PRTG при отображении алагма, писал что пошло не так. А название.... как то давно делал не предал значения.
Суть сенсора в том, что XML (но можно и JSON для более менее свежих версий) может содержать данные каналов, а может содержать секцию:
<error>1</error> и <text> тут сообщение об ошибке </text> и все.
И вот любые ошибки не на уровне каналов я вывожу сюда.
А буква... А где именно? в исходнике примера для С9800? вроде не должна. Давайте проверю
dyadyaSerezha
Вы зря написали так много слов. Главная суть функции - она глобально завершается работу программы. При этом ещё и пишет причину. Значит это или ExitOnError, или ProcessErrorAndExit, но никак не совершенно аморфное ErrorInProgram.
Prtgbody - > PrtgBody
OlegPowerC Автор
А вы про это, ну это никак не влияет на работу, а код вот он - опен сурс, кто желает может как угодно переделать.