Стартовая страница › Форумы › Разработка и интеграция › Вопрос по кодингу модуля сервера.
- В этой теме 37 ответов, 4 участника, последнее обновление 6 лет, 7 месяцев назад сделано Mikhail.
-
АвторСообщения
-
04.08.2017 в 11:45 #6907OldManSpbУчастник
Приветствую всех!
Пытаюсь писать модуль сервера на основе ModTest. Все собирается, запускается, с этим все нормально. Мне надо обрабатывать события и отправлять каждое из них на другой сервер в своем формате. Индексов объекта, КП и др. недостаточно, необходимо получить их текстовое описание из соответствующих таблиц. Вот обработчик:public override void OnEventCreating(EventTableLight.Event ev) { // the method executes on event creating, event properties could be changed here // метод выполняется при создании события, свойства события можно изменить здесь WriteToLog("Process event creating by the module " + Name, Log.ActTypes.Action); CommSettings coms = new CommSettings(); string errMsg = "Error load settings"; coms.LoadFromFile(AppDirs.ConfigDir + "CommSettings.xml", out errMsg); //Так как не понимаю, в какой лог писать сделал следующее Log logg = new Log(Log.Formats.Simple); logg.FileName = "asd.log"; logg.Capacity = 1000; ServerComm scc = new ServerComm(coms, logg); DataCache dc = new DataCache(scc, logg); DataAccess da = new DataAccess(dc, logg); //Здесь в KPNum пытаюсь получить название КП WriteToLog("Data values ===================" + "\nObjNum: " + ev.ObjNum + " |Checked: " + ev.Checked + " |CnlNum: " + ev.CnlNum + " |Data: " + ev.Data + " |DateTime: " + ev.DateTime + " |Descr: " + ev.Descr + " |KPNum: " + da.GetKPName(ev.KPNum) + //Попытка получить название КП " |NewCnlStat: " + ev.NewCnlStat + " |NewCnlVal: " + ev.NewCnlVal + " |Number: " + ev.Number + //Почему-то всегда возвращает ноль " |ObjNum: " + ev.ObjNum + " |OldCnlStat: " + ev.OldCnlStat + " |OldCnlVal: " + ev.OldCnlVal + " |ParamID: " + ev.ParamID + " |UserID: " + ev.UserID, Log.ActTypes.Action);
Вот вывод лога:
2017-08-04 11:39:05 <GM><система><ACT> Data values =================== ObjNum: 2 |Checked: False |CnlNum: 1702 |Data: |DateTime: 04.08.2017 11:38:59 |Descr: |KPNum: |NewCnlStat: 13 |NewCnlVal: 65,1126556396484 |Number: 0 |ObjNum: 2 |OldCnlStat: 15 |OldCnlVal: 65,838134765625 |ParamID: 23 |UserID: 0
Что не так:
Во-первых, ID события всегда выдает 0.
Во-вторых не получается получить текстовые описания из таблиц.
Тыкните меня пожалуйста носом, что я делаю неправильно?04.08.2017 в 14:24 #6910OldManSpbУчастникНомер события из обработчика EventCreating дает 0 и из EventCreated тоже 0.
04.08.2017 в 21:12 #6914MikhailМодераторНомер события из обработчика EventCreating дает 0 и из EventCreated тоже 0.
Это исправлено в ветке develop и при следующем релизе войдёт в master.
Текст события генерируется довольно хитро. Опять же в ветке develop есть подвижки по этому вопросу. См. здесь.
Вам надо загрузить базу конфигурации из файлов dat, чтобы получить наименования объектов, КП и т.д. Актуальный путь к базе Вы можете найти в переменных, доступных в модуле.
04.08.2017 в 21:13 #6915MikhailМодераторВ новом Модуле автоуправления недавно решались похожие задачи.
05.08.2017 в 01:02 #6919OldManSpbУчастникМихаил, спасибо за ответ.
06.08.2017 в 19:08 #6924OldManSpbУчастникНасчет модуля автоуправления. Я бы добавил туда ПИ регулятор. И тогда можно было бы им делать вообще все что угодно. Извиняюсь за оффтоп.
07.08.2017 в 11:55 #6936MikhailМодераторДумаю, что ПИД-регулятор лучше сделать отдельным модулем, т.к. он специфичен. И ещё отличается тем, что регулятор подаёт команды постоянно, а модуль автоуправления всё-таки относительно редко — разный подход.
Но пока с коммерческой точки зрения модуль регулятора не очень востребован, может быть кто-то сделает и выложит или, наоборот, закажет и оплатит разработку.
07.08.2017 в 12:13 #6944manjey73УчастникРегулятор можно сделать формулой в канале, а когда в Модуле появится возможность посылать данные по изменению то и проблема снимется.
07.08.2017 в 12:38 #6950OldManSpbУчастникP — регулятор, да, можно формулой, или я не прав и можно и интегральный регулятор сделать формулой? Мне возможно понадобится в будущем, когда понадобится буду разбираться.
07.08.2017 в 13:10 #6955manjey73УчастникДа по идее все можно написать формулами. Если выход у регулятора один, то будет формула вида PID(Val(1), Val(2), Val(3)) — где Val(x) номера каналов отвечающие за настройки PID, интегральная составляющая, дифференциальная и так далее, все что необходимо для расчета.
Если выходов 2 и более, тогда придется разбивать на несколько частей формулу.Надо посмотреть код регуляторов на C для ПК или Ардуино, или например на ST для ПЛК ну и сделать ее на C# для использования в RapidScada.
Если найдете на языке LD для ПЛК то можно и так сделать, но много каналов может потребоваться для реализации.
07.08.2017 в 13:57 #6957OldManSpbУчастникХорошо, а ограничение выходного значения можно сделать? Или ограничение диапазона интегрирования в I компоненте?
07.08.2017 в 14:30 #6958manjey73УчастникТак все зависит от кода, если нужно ограничение по выходу, можно использовать код ScaleR на выходе или в самом коде PID.
Формула — это по сути обычный программный код функции, как напишите, так и будет работать.09.08.2017 в 10:39 #6969OldManSpbУчастник=Выкладываю класс для модуля сервера, который позволяет получать названия большинства полей события, а также получать номер события, который в текущей версии не передается в обработчики.
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using Scada.Data.Models; using Scada.Data.Tables; using Scada.Server.Modules; using Scada.Client; using System.Data; namespace Scada.Server.Modules { class DataTablesAccess { public DataTablesAccess(string PathToConfig) { string pathToconfig = PathToConfig;// "C:\\SCADA\\BaseDAT\\"; baseTables = new BaseTables(); baseAdapter = new BaseAdapter(); baseAdapter.FileName = pathToconfig + BaseTables.GetFileName(baseTables.KPTable); baseAdapter.Fill(baseTables.KPTable,true); baseAdapter.FileName = pathToconfig + BaseTables.GetFileName(baseTables.ObjTable); baseAdapter.Fill(baseTables.ObjTable, true); baseAdapter.FileName = pathToconfig + BaseTables.GetFileName(baseTables.InCnlTable); baseAdapter.Fill(baseTables.InCnlTable, true); baseAdapter.FileName = pathToconfig + BaseTables.GetFileName(baseTables.ParamTable); baseAdapter.Fill(baseTables.ParamTable, true); baseAdapter.FileName = pathToconfig + BaseTables.GetFileName(baseTables.EvTypeTable); baseAdapter.Fill(baseTables.EvTypeTable, true); baseAdapter.FileName = pathToconfig + BaseTables.GetFileName(baseTables.UserTable); baseAdapter.Fill(baseTables.UserTable, true); } public string GetKPNameByNubber (int KPNum) { DataRow[] dataRow; dataRow = baseTables.KPTable.Select("KPNum = " + KPNum); if (dataRow.Length > 0) return dataRow[0][1].ToString(); else return "No such KP"; } public string GetObjectNameByNumber(int ObjNum) { DataRow[] dataRow; dataRow = baseTables.ObjTable.Select("ObjNum = " + ObjNum); if (dataRow.Length > 0) return dataRow[0][1].ToString(); else return "No Such Object"; } public string GetChannelNameByNumber(int ChNum) { DataRow[] dataRow; dataRow = baseTables.InCnlTable.Select("CnlNum = " + ChNum); if (dataRow.Length > 0) return dataRow[0][2].ToString(); else return "No Such Channel"; } public string GetParametrName(int ParamNo) { DataRow[] dataRow; dataRow = baseTables.ParamTable.Select("ParamID = " + ParamNo); if (dataRow.Length > 0) return dataRow[0][1].ToString(); else return "No Such Param"; } public string GetEventTypeText(int ChStat) { DataRow[] dataRow; dataRow = baseTables.EvTypeTable.Select("CnlStatus = " + ChStat); if (dataRow.Length > 0) return dataRow[0][1].ToString(); else return "No Such Status"; } public string GetUserName(int userNo) { DataRow[] dataRow; dataRow = baseTables.UserTable.Select("UserID = " + userNo); if (dataRow.Length > 0) return dataRow[0][1].ToString(); else return "No Such User"; } public int GetLastEventNo() { string tableName = "C:\\SCADA\\ArchiveDAT\\Events\\" + "e" + DateTime.Now.ToString("yyMMdd") + ".dat"; EventTableLight eventTable = new EventTableLight(); EventAdapter eventAdapter = new EventAdapter(); eventAdapter.FileName = tableName; eventAdapter.Fill(eventTable); List<EventTableLight.Event> temp = new List<EventTableLight.Event>(); temp = eventTable.GetLastEvents(1); if (temp.Count > 0) return temp[0].Number; else return 0; } private BaseTables baseTables; private BaseAdapter baseAdapter; } }
09.08.2017 в 10:52 #6974MikhailМодераторСпасибо за код. Однако рекомендую выкладывать исходники на GitHub, а в форуме оставлять ссылку на код. Так будет проще найти этот код в будущем.
10.08.2017 в 10:57 #6984OldManSpbУчастникМой модуль иногда подглючивает после перезапуска сервера, всвязи с этим вопрос: При перезапуске сервера каждый раз вызывается конструктор модуля, либо только дергаются методы OnServerStart и OnServerStop?
-
АвторСообщения
- Вы должны авторизироваться для ответа в этой теме.