Горячая линия Embedded System Rus:8-800-775-06-34 (звонок по России бесплатный)

LM5_N
LM-Wall_N
DALI_N
Vita_N

Мониторинг шины KNX

Использование LM для автоматического мониторинга KNX-TP шины и генерации отчетов.

С помощью библиотеки knxlib мы можем опрашивать KNX устройства для получения информации о их наличии и текущем напряжении на шине. О возможности узнать напряжение в ETS знают, но пользуются редко, что можно компенсировать с помощью LM.

Шаг 1 – Сбор информации

Для сбора информации об устройствах мы будем пользоваться функциями ping и readbusvoltage, которые позволят нам узнать о наличии или работоспособности устройства в шине и напряжении соответственно. Для задачи диапазона опрашиваемых адресов используется двухмерный массив addr_table, в котором находятся массивы с тремя значениями: ‘линия KNX’, ‘начальное значение’, ‘конечное значение’. На основе данных, находящихся в массиве addr_table, происходит опрос устройств по заданным адресам и генерируется массив с результатами опроса вида: адрес, результат ping, напряжение, время ОС в секундах, дата ОС в формате год-месяц-день_часы-минуты. Таблица результатов хранится в storage. Скрипт может быть и событийным, и по расписанию, всё зависит от ваших потребностей. Но учтите, что при большом количестве опрашиваемых устройств, отчёт также будет расти с большой скоростью, поэтому вы можете создать несколько различных копий этого скрипта с разной частотой запуска и контролируемым диапазоном адресов.

addr_table = {
-- Массив для задачи диапазона вида { 'линия', start, stop }
{ '1.1', 1, 20 },
{ '1.1', 50, 60 },
{ '1.2', 10, 15 },
}
res_table = storage.get('res_table', {})
for index, value in ipairs(addr_table) do
for i = value[2], value[3], 1 do
addr = value[1] .. '.' .. i
res_ping = knxlib.ping(addr)
os.sleep(0.05)
if res_ping then
res_voltage = knxlib.readbusvoltage(addr)
end
table.insert(res_table, { addr, res_ping, res_voltage, os.time(), os.date('%Y-%m-%d_%H-%M') })
end
end
storage.set('res_table', res_table)

Шаг 2 – Генерация csv-таблицы с результатами опроса.

Для того, чтобы не нагружать лишний раз флеш-память LM, добавьте в скрипт запуска системы (init script) следующую строку, чтобы создать папку в оперативной памяти для хранения файла отчета:

os.execute('mkdir -p /tmp/ftp; chown ftp:ftp /tmp/ftp; mkdir -p /home/ftp/tmp; \
            chown ftp:ftp /home/ftp/tmp; mount --bind /tmp/ftp /home/ftp/tmp')

Для создания csv-файла используйте следующий код. Переменная buffer является первой строкой таблицы с заголовками, в случае необходимости вы можете его изменить. Переменная my_res отвечает за каждую строку отчета, поэтому при изменении buffer не забудьте исправить и её. Скрипт создаёт файл во временной папке, к которой можно получить доступ по ftp, но при перезагрузке LM файл будет удалён. Файл имеет имя вида «%Дата создание%.csv». Также при создании файла обнуляется массив результатов, хранящийся в storage.

os.sleep(15)
res_table = storage.get('res_table', {})
-- Для изменения вида таблицы внести изменения в заголовки buffer с разделителем ";" и в my_res
buffer = {'Физический адрес;Пинг;Напряжение;Время;Дата'}
for i = 1, #res_table do
obj = res_table[i]
adr = obj[1]
res_ping = obj[2]
res_voltage = obj[3]
time = obj[4]
date = obj[5]
-- Перевод boolean значения в строку
if res_ping then res_ping = 'true' else res_ping = 'false' end
-- Проверка на наличие значения напряжения
if not res_voltage then res_voltage = ' ' end
-- Генерация csv-строки
my_res = adr..';'..res_ping..';'..res_voltage..';'..time..';'..date
table.insert(buffer, my_res)
end
-- Генерация csv-файла
res_csv = table.concat(buffer , '\n')
filepath = '/data/ftp/tmp/' .. os.date('%Y-%m-%d_%H-%M') ..'.csv'
log('Write file:', io.writefile(filepath, res_csv))
storage.set('res_table', {})

Шаг 3 – Отправка файла.

Шаг 3.1 – Отправка файла по e-mail.

Для отправки сгенерированного csv-файла используйте следующий скрипт. Он является немного измененной версией отправки обычных писем, поэтому внесите изменения в параметры функции, такие как user, password, from и to.

--send by email
table_of_files = io.ls('/tmp/ftp/')
--Получение имени файла и пути к нему
if #table_of_files > 0 then
file_name = table_of_files[1]
filepath = '/tmp/ftp/' .. file_name
--Gmail (smtp) username
user = 'example@gmail.com'
--Gmail (smtp) password
password = 'example'
--Sender for e-mail
from = 'example@gmail.com'
--Recipient for e-mail
to = 'example@yandex.ru'
--Subject for e-mail
subjectpart1 = 'Отчет от ' .. file_name
subjectpart2 = 'автоматически отправлен LogicMachine'
image_description = 'Отчет LogicMachine'
--Текст в конце сообщения (отображается, если почтовый клиент не может отобразить вложение)
epilogue = 'Конец сообщения'
-- Функция отправки
-- При успешной отправке удаляет отправленный файл, при неудаче - лог ошибки.
-- Ниже черты параметры не стоит менять.
-----------------------------------------------------------------------------------------------
--Create table to include mail settings
local settings = {
from = '<' .. from .. '>',
rcpt = '<' .. to .. '>',
user = user,
password = password,
server = 'smtp.gmail.com',
port = 465,
secure = 'sslv23',
}
--Create subject
subject = subjectpart1 .. subjectpart2
--Load required modules to send email with attachment
local smtp = require("socket.smtp")
local mime = require("mime")
local ltn12 = require("ltn12")
--Create e-mail header
settings.source = smtp.message{
headers = {
from = from,
to = to,
subject = subject
},
--Load attachment inside body
body = {
preamble = "",
[1] = {
headers = {
["content-type"] = 'text/csv',
["content-disposition"] = 'attachment; filename="'..file_name..'"',
["content-description"] = '.. dst ..',
["content-transfer-encoding"] = "BASE64",
},
body = ltn12.source.chain(
ltn12.source.file(io.open(filepath, "rb")),
ltn12.filter.chain(
mime.encode("base64"),
mime.wrap()
)
)
},
epilogue = epilogue
}
}
--Send the email
r, e = smtp.send(settings)
--Create alert when sending gives an error with error message
if (e) then
log (e)
log (r)
alert("Could not send email: ", e, "\n")
else
log('Deleting file: ', os.remove(filepath))
end
end

Шаг 3.2 – Отправка файла на внешний ftp-сервер

Следующий скрипт также является стандартным для отправки файлов на внешний ftp-сервер, поэтому внесите необходимые изменения в переменную ftpfile, чтобы задать параметры вашего ftp-сервера.

-- send by ftp
table_of_files = io.ls('/tmp/ftp/')
--Получение имени файла и пути к нему
if #table_of_files > 0 then
file_name = table_of_files[1]
filepath = '/tmp/ftp/' .. file_name
require('socket.ftp')
-- ftp file
ftpfile = string.format('ftp://login:passw@192.168.0.1/csv/%s', file_name)
-- upload to ftp only when there's data in buffer
result, err = socket.ftp.put(ftpfile, io.readfile(filepath))
-- error while uploading
if err then
log('FTP upload error: ' .. tostring(err))
else
log('Deleting file: ', os.remove(filepath))
end
end