У нас на производстве уже несколько лет работает "самодельная" система контроля за простоями на производстве, реализованная на базе MS SQL, SQL Server Reporting Services и IIS. Вот пример отчета за некоторый период:

Принцип работы такой: агрегат каждую секунду (раньше это было через скрипт в SCADA системе, сейчас через PLC контроллер посредством UDP) передает в SQL базу свое состояние, и как только в таблице состояния появляется условие простоя (это обрабатывается триггером), то в другой таблице в графе "начало простоя" отбивается текущее время. При пропадании условия простоя в этой же таблице отбивается текущее время в графе "конец простоя", и вычисляется общая его продолжительность и отправляется в третью графу. На этом же сервере SQL крутится веб сервер IIS, который при запросе пользователя формирует и выдает ему таблицу простоев в браузере за заданный период. Я так понимаю ничего инновационного в таком подходе нет - MES системы, похоже, основаны на таком же принципе, разве что ПО более продвинутое и заточенное под конкретные задачи.

В данном случае мне такое решение показалось интересным, и одно из его достоинств это "бюджетность". Весь функционал был сделан своими силами инженерами АСУТП без привлечения высококлассных программистов, а я, столкнувшись по работе с этим, после небольшого экскурса в принцип работы, смог добавить еще парочку таблиц с простоями по аналогии с теми что были. Однако такое решение все же требует мало-мальский опыт в программировании и к тому же передача из UDP в SQL реализована через программу-прокладку, что тоже является костылями, но сейчас речь не об этом. Мне как пользователю самому приходится сталкиваться с этими отчетами простоев и поскольку все это решение заточено под использование продуктов Microsoft, то и для пользователе рекомендуется юзать Internet Explorer. То есть решение настолько древнее, что в современном браузере часть функционала уже недоступна, в частности выбор даты формирования отчета это не календарь, а обычное поле, куда нужно вводить даты вручную, что сильно раздражает. Собственно только этот момент и заставил меня ради эксперимента попробовать сделать что-то похожее, но чтобы шаблон отчета можно было редактировать как мне заблагорассудится и обязательно с нормальными полями выбора даты. Вот что получилось в результате у меня:

Пример сформированного отчета
Пример сформированного отчета

Очень классно, что для удобства использования (возможно и нет, это как посмотреть) программу можно сделать одним исполняемым файлом - это и веб-сервер, отдающий по запросу таблицу, и сама база данных в SQlite, и даже прием сигналов по UDP с последующим сохранением в базу данных тоже можно прикрутить. Запустил одну программу и все работает. На данный момент в программе реализован механизм получения данных из базы и отдача их по запросу по вебу. Код программы есть в моем репозитории ReportsFromDB. Сама программа заточена под таблицу с определенной структурой - это 3 столбца: Downtime_start - начало простоя, Downtime_stop - конец простоя, и Downtime_dur - продолжительность. В репозитории есть пример такой таблицы с ~55 тысячами записей. Выбор промежутка отчета реализован посредством html и js, что мне кажется тоже очень удобным, т.к. если нужно что-то менять в визуальном оформлении, то не нужно менять программу целиком, а только html файл. Таким же образом удалось добавить русифицированное представление даты и времени в таблице, не меняя и перекомпилируя программу.

Сама программа это, по сути, http Listener, который прослушивает порт 8880 и ожидает запрос, и в зависимости от его содержимого отправляет клиенту ту информацию которую он запросил. Чтобы сформировать таблицу за определенный период нужно подать на сервер запрос такой структуры: http://ip-addr:8880/;время_начала;время_окончания;clear_options Такой странный формат выбрал просто потому, что так проще сделать разделение RawUrl по точке с запятой в запросе, а так как на странице используется iframe, то пользователю не нужно запоминать формат запроса - он создается автоматически во фрейме, и в ответ приходит часть таблицы со 100 строками и несколькими такими страницами, если в запрашиваемый период больше 100 строк. Вот так это реализовано в коде:

try
{
	string[] req = request.RawUrl.Split(';');
	string reply = "";
	if (req[3] == "clear")
	{
		SQLrows.Clear();
	}
	reply = connectToDb(req[1], req[2]);
	if (reply == "ok" || reply == "full")
	{
		responseString = buildTable();
	}

	else
	{
		responseString = reply;
	}
}
catch
{
	try
	{
		responseString = System.IO.File.ReadAllText(System.IO.Path.Join(path, "/Resources/index.html"));
	}
	catch
	{
		responseString = "add index.html to generate page";
	}
}

В начале функционал был такой что пользователю приходила таблица целиком - вся огромная строка с кодом таблицы отправлялась в браузер и он конкретно так зависал, особенно если попытаться вывести все 55 тысяч строк разом. Именно поэтому был добавлен постраничный вид, с реализацией в виде списка, но опять же довольно криво, поскольку если 2 человека одновременно запросят разные даты, то таблица будет каждый раз пересоздаваться, т.к. список с результирующим массивом таблицы общий для всех. Если добавить в строку запроса еще несколько опций, то можно легко реализовать, например, выбор базы данных или таблицы, что очень удобно, если будет несколько таблиц и баз. Запрос без структуры приведет к тому что страница index.html находящаяся в папке Resources передастся клиенту, но если такой страницы не существует, об этом сообщит браузер. Index.html как и говорил облегчает обращение к базе, за счет использования фрейма, который меняется через javascript.

Разработка велась на Windows и по идее .NET должен работать так же на Linux, поэтому установив .NET SDK на Убунту, я это проверил сам. Однако столкнулся с интересной особенностью - формирование и перелистывание отчета на Убунту срабатывает со второго раза, а в панели разработчика в браузере при перелистывании появляется такая картина:

Не совсем понятно что это такое и из-за чего это может быть, возможно кто-нибудь сможет подсказать в комментарии. А вообще, этот эксперимент я считаю для себя результативным: по крайней мере функцию выбора даты по календарю я все-таки сделал, и даже русификацию даты добавил. А для производственного использования это решение не предполагалось изначально, т.к. нынешняя система на IIS и MSSQL хоть и криво, но работает, по крайней мере пользователи не жалуются (возможно они и не знают что можно что-то улучшить, если хорошо попросить). Я же написал эту заметку для того, чтобы получить конструктивную критику и советы, как можно сделать что-то лучше или, возможно, вообще есть готовые решения такого же плана. Также это и возможность поделиться таким вот способом решения проблемы по созданию отчетов по простоям. Получилась своеобразная MES система на минималках по регистрации простоев, которая пока так и останется экспериментом, а мы так и будем юзать MS SQL и IIS :)

Комментарии (11)


  1. tsklab
    00.00.0000 00:00
    +1

    А что мешает перейти на SSRS 16 (это отдельная от MS SQL служба), которой не нужен IIS и поддерживаются современные браузеры (больше того IE уже не поддерживается).

    Кстати (просто бросается в глаза) у вас нет группировки по агрегатом, нет итогов. Что делается в SSRS очень просто (уже в самом отчёте, а не во внешнем источнике данных).


    1. Weron2 Автор
      00.00.0000 00:00
      +1

      Да, наверное это был бы самый правильный способ.


  1. sergs79
    00.00.0000 00:00
    +1

    ну или все это обернуть в Loginom.


  1. dcs_pls
    00.00.0000 00:00

    Коллега, не сталкивался никогда с задачей с точностью до десятых секунды определить простой или наработку. Обычно необходима месячная и общая наработка и там такая точность не нужна. И какая АСУ ТП у вас установлена?


    1. Weron2 Автор
      00.00.0000 00:00
      +1

      Да, нам такая точность тоже не нужна, просто в базе отбиваются такие цифры с точностью до миллисекунды)

      АСУТП - не совсем понимаю что имелось ввиду?


      1. dcs_pls
        00.00.0000 00:00

        Если предприятие большое и есть PLC контроллеры наверняка есть и верхний уровень сервера (архивы которые часто пишутся в SQL базу, расчетные задачи и т.п.) и конечно операторские станции. Все это хозяйство имеет производителя и название. Я про название и производителя.

        Расчет наработки (моточасы) я перенес в контроллеры там же считается оставшееся время до ремонта-ревизии и сигнализацию можно прикрутить.


        1. Weron2 Автор
          00.00.0000 00:00
          +1

          Главные контроллеры линий это Mitsubushi Q50UDEH, есть и Сименсы 300, 400, 1200 на некоторых узлах. СКАДА система Intouch, есть и операторские панели от Сименса и Митсубиси.

          У нас тоже расчет моточасов в контроллере сделан)


  1. WondeRu
    00.00.0000 00:00
    +3

    мне сам подход не близок, но в глаза бросилась обработка исключений: вы не обрабатываете конкретные типы исключений и никогда не узнаете почему код не сработал, либо нет доступа до сервера DB, либо пароль неправильный, либо еще чего-нибудь. Второй catch тоже может не сработать по разным причинам. Почитайте про исключения в c# и логгирование.


  1. xztau
    00.00.0000 00:00

    через PLC контроллер посредством UDP) передает в SQL базу свое состояние,

    Как? Omron NJ?

    ОБН

    UDP в SQL реализована через программу-прокладку

    Ааааааааа. OPC сервер. Так это нормально - так и делается.


    1. Weron2 Автор
      00.00.0000 00:00

      Есть блоки передачи данных и у Сименса и у Митсубиси для TCP, UDP соединений - вот они и передают те сигналы которые им скажешь

      Ааааааааа. OPC сервер. Так это нормально - так и делается

      Не совсем) Написали тоже простенькую программу на C# которая запущена на сервере и слушает UDP порт. Все что в него приходит она отправляет в базу данных


  1. Dynasaur
    00.00.0000 00:00
    +1

    Я вижу, у вас только информация о простое самой единицы оборудования, без привязки что на ней производилось и по какому заказу (или по какому ремонтному заказу), правильно? Если сумеете подтянуть производственный заказ (ваша терминология может отличаться, но, думаю, понятно), ремонтный заказ, заказ на переналадку оборудования, на котором образовался простой, то можно интегрировать эту информацию с ERP и ценность её кратно возрастёт