Стартовая страница › Форумы › Разработка и интеграция › Потоки(Thread) — Программы(class) — Переменные
- В этой теме 123 ответа, 4 участника, последнее обновление 2 года назад сделано
manjey73.
-
АвторЗаписи
-
22.08.2024 в 22:22 #34152
Romiros
УчастникА если добавить входную формулу Val();Stat(), Будут ли изменения?
22.08.2024 в 23:05 #34153
manjey73УчастникНет изменений, переменная так же пляшет.
Мне кажется я как-то неправильно сохраняю потоки и данные о них. Но толковых примеров не нахожу.Переменная пляшет между значением по умолчанию, которое берется из конфиг файла и последним введенным мной значением.
-
Ответ изменён 2 года назад пользователем
manjey73.
22.08.2024 в 23:57 #34155
JurasskParkУчастникПо описанию выглядит на то, что изменение переменной у вас создаёт новый поток с новым значением.
И теперь два потока в один тег пишут по очереди
1000
2000
1000
200022.08.2024 в 23:59 #34156
JurasskParkУчастникВ свойства потока добавьте генерируемый Guid id и в логах пишите с какого id какое value пишется в тег.
23.08.2024 в 08:45 #34157
manjey73УчастникМаленькое видео и скрин лога. Извиняйте, ютюбам не обучен. Видео на 10 Мб всего.
В табличном представлении переменные трех одинаковых потоков.
cycle по умолчанию для потоков берется из конфиг файла при старте модуля и запуске потоков. Я их меняю и они по идее применяются, так как переменными EN я в принципе разрешаю работать коду, при EN = 0 цикл while потока крутится в холостую.
На скрине лога видно красным значения по умолчанию, синим, те, которые я ввел.
Тоже для EN переменных.
3-й поток я EN на видео не включал вообще, и там выход EQ по нулям.
Ну и cycle это мс для Thread.Sleep по сути.
Выход Q по достижении EQ > 100 станет true. то есть все три потока работают вроде бы корректно, но вот эти изменения между первым значением и введенным как для cycle так и для EN меня вводят в ступор. То есть если бы код всегда принимал первое значение, он не давал бы мне изменять его и снова бы переписывал на значение по умолчанию, а тут поток сам берет и меняет постоянно введенное значение между первым и последним введенным. Ну для EN непонятно, так как там всего 2 значения в принципеpublic class Heater() { public bool EN; // входная переменная разрешающая работу программы public bool Q; // выходная переменная public uint EQ; // цифровой выход результата программы public volatile ushort cycle; // Задание цикла выполнения программы в мс public volatile bool terminated = false; // Внутренние переменные для работы программы uint start = 0; // Стартовое значение программы (флаг) public void Run() { while (!terminated) // Поток необходимо прерывать только по завершении, паузу необходимо делать через EN { if (EN) { // Инициализация выходных переменных если требуется. Q = false; // тело программы start++; if (start > 100) { Q = true; } // тело программы EQ = start; Thread.Sleep(cycle); } } } }Программа простейшая в виде класса, который загружается как экземпляр в потоке через Reflection. Друг другу вроде не мешают.
23.08.2024 в 08:52 #34158
manjey73УчастникДа вроде нет новых потоков, в том то и дело. Да и по логам я вывожу их id видно, что они не меняются, как создалось три так и остается три.
Это какой-то косяк именно с входными переменными потока. мои переменные cycle заданные по умолчанию при старте модуля и переменные Q = false при старте модуля.
Они начинают моргать с какого-то…Хотя .SetValue в поток отрабатывает исправно, иначе бы счет не начинался…
23.08.2024 в 08:58 #34159
manjey73УчастникВот включил EN у всех трех потоков. Первая половина скрина видно как сработал Q первого потока.
Вторая половина, добавился Q второго потока.
Третий так же считает EQ со своим временем цикла.Но входы перемаргивают, и по логам в них запись то первого значения то нового.
А код класса самих потоков один в один.Вот в этом и чудеса, почему он самостоятельно берет то старое то новое значение?
Все каналы при этом Calculated/Output.
Единственное, на выходных нет формул вообще. Формулы есть только на входных переменных.
Если разобраться, то формула SetVal(CnlNum, Cmd) выходная никак не должна влиять.23.08.2024 в 08:59 #34160
manjey73Участникcycle и EN = false правильно будет, в сообщении #34158
23.08.2024 в 09:14 #34161
JurasskParkУчастникНу это всё понятно, но вы самое главное не показали. Код как вы запускаете потоки.
У меня в старых проектах было так. Но сразу оговорюсь, естественно, там class не public 🙂
Часть кода удалил, чтобы не мешало.private void StartThreads() { //Опрос устройств try { if (HARDWARE_GROUP_MODBUS.Channels != null) { //Приходимся по списку каналов for (int i = 0; i < HARDWARE_GROUP_MODBUS.Channels.Count; i++) { CHANNEL thread_channel = HARDWARE_GROUP_MODBUS.Channels[i]; //Делаем признак, что каналы можно запускать thread_channel.ChannelThreadEnabled = true; //А вот если пользователь выставил, что этот канал не надо запускать, то его пропускаем и не трогаем if (thread_channel.ChannelEnabled == true) { int port = 0; if (thread_channel.GatewayTypeProtocol == 0) { //Если шлюз не указан т.е. выключен, то генерируем порт port = GENERATE_TCP_SERVER_PORT.PORT_NEW(); } else { bool checked_port = GENERATE_TCP_SERVER_PORT.CheckAvailableServerPort(Convert.ToInt32(thread_channel.GatewayPort)); if (checked_port == false) { MessageBox.Show("Указанный порт " + thread_channel.GatewayPort + " занят! Попробуйте другой!", "Ошибка", MessageBoxButtons.OK, MessageBoxIcon.Error); return; } port = thread_channel.GatewayPort; } thread_channel = HARDWARE_GROUP_MODBUS.Channels[i]; //Получение RxTx ThreadModbusDevicesPoll.EventHandlerEventDevicePollTxRx = new ThreadModbusDevicesPoll.EventDevicePollTxRx(DevicePollTxRxGet); //Получение лога ThreadModbusDevicesPoll.OnDebug = new ThreadModbusDevicesPoll.DebugData(DevicePollLogGet); //Получение информации с TCP сервера ThreadModbusDevicesPoll.OnDebugTCPServer = new ThreadModbusDevicesPoll.DebugDataTCPServer(DevicePollTCPServerLogGet); ThreadModbusDevicesPoll ModbusDevicesPoll = new ThreadModbusDevicesPoll(thread_channel); new Thread(new ParameterizedThreadStart(ModbusDevicesPoll.Work)) { IsBackground = true }.Start((object)ModbusDevicesPoll); } } } } catch { } } private void StopThreads() { if (HARDWARE_GROUP_MODBUS.Channels != null) { //Приходимся по списку каналов for (int i = 0; i < HARDWARE_GROUP_MODBUS.Channels.Count; i++) { CHANNEL thread_channel = HARDWARE_GROUP_MODBUS.Channels[i]; //Останавливаем опрос канал по признаку Потока thread_channel.ChannelThreadEnabled = false; } } } internal class ThreadModbusDevicesPoll { //Канал private CHANNEL channel; //Асинхронный TCP сервер private ListAsyncTCPServer listAsyncTCPServer = new ListAsyncTCPServer(); AsyncTCPServer atcpserver; private int gatewaytypeprotocol = 0; private int gatewayport = 60000; private int gatewayсonnectedclientsmax = 10; private Guid gatewaychannelID; public ThreadModbusDevicesPoll(CHANNEL Channel) { this.channel = Channel; } public void Work(object obj) { #region Поток //Проверка условия должен ли поток работать (в цикле) while (channel.ChannelThreadEnabled == true) { #region Опрос устройств #endregion Опрос устройств } #endregion Поток } } }23.08.2024 в 09:26 #34162
manjey73Участникstring myProg = program.program.Task; // Тут я указываю имя класса, который надо считать, в данном случае Heater Type typelist1 = Assembly.GetExecutingAssembly().GetExportedTypes().Where(x => x.Name == myProg).FirstOrDefault(); // Получение списка только интересующего класса Type type = Assembly.GetExecutingAssembly().GetType(typelist1.FullName); // "........Heater" object instance = Activator.CreateInstance(type); FieldInfo[] fi = type.GetFields(); // Получение public полей EN, cycle, Q, EQ и т.д. // Тут поиск переменной по имени связывания цикла для управления из программы(web) ??? FieldInfo name = type.GetField("cycle"); // чтение из полей параметра cycle name?.SetValue(instance, cycle); // запись параметра при старте moduleLog.WriteLine($"cycle = {cycle}"); // Создание и запуск потоков программ Thread thr = new Thread(delegate () { object obj = instance; object[] parameters = []; MethodInfo mi = type.GetMethod("Run"); // чтение метода Run из класса Heater mi.Invoke(obj, parameters); // и его вызов }); thr.Name = $"di{program.deviceIndex}pi{program.programIndex}"; // Задание имени потока по deviceIndex и programIndex thr.IsBackground = true; thr.Priority = (ThreadPriority)priority; // Задание приоритета выполнения программы из шаблона thr.Start(); // Добавление в словарь потоков ModThread modThread = new ModThread { thread = thr, nameThread = thr.Name, fields = fi, obj = instance, type = type, allProgramm = program, threadCnl = dictCnl, // Добавления словаря привязанных каналов входных и выходных переменных }; PrgThread.Add(thr.Name, modThread);23.08.2024 в 09:28 #34163
manjey73УчастникПримерно как-то так. Вытягиваю свой тестовый Heater и запускаю поток.
Смущает то, что EQ и Q считаются и работают в зависимости от времени сна и включенного состояния после добавления переменной EN в класс программы.
Потоки останавливаются при остановке сервера, запускаются при старте.Но кто и где постоянно переписывает значения между первоначальным и введенным загадка.
23.08.2024 в 09:37 #34164
JurasskParkУчастникНо кто и где постоянно переписывает значения между первоначальным и введенным загадка.
Может у вас в коде используется одна переменная как private online, которая при инициализации класса создается, а вторая локальная с таким же именем?
23.08.2024 в 09:41 #34165
manjey73УчастникДа нет вроде, ничего такого мудреного нет.
Суть. Все каналы полностью одинаковы — Calculated/Output
При этом где нет формул — строго типа выходные. Нет каких либо перемаргиваний.
Например EQ не моргает между значением и первоначальным 0.
Q не моргает после включения и нулем(false)Способ записи вроде одинаковый по именам переменных в канале
if (typeIdCnl == 4) // Каналы Calculated/Output { //moduleLog.WriteLine($"Имя переменной {cnl.Value.Name} FieldName {field.Name} Значение {ObjValue.ToString()} FieldType {field.FieldType}"); //ServerContext.WriteCurrentData(cnl.Value.CnlNum, new CnlData(valDouble, 1)); // Test CnlData curData = ServerContext.GetCurrentData(cnl.Value.CnlNum); if (curData.IsDefined) { if (field.FieldType.ToString() == "System.UInt16") { field?.SetValue(thr.Value.obj, Convert.ToUInt16(curData.Val)); moduleLog.WriteLine($"System.UInt16 запись {valDouble} fieldName {field.Name}"); } if (field.FieldType.ToString() == "System.Boolean") { field?.SetValue(thr.Value.obj, Convert.ToBoolean(curData.Val)); moduleLog.WriteLine($"System.Boolean запись {valDouble} fieldName {field.Name}"); } } if (cnl.Value.DataTypeID == 0 || cnl.Value.DataTypeID == null) { ServerContext.WriteCurrentData(cnl.Value.CnlNum, new CnlData(valDouble, 1)); // Test //moduleLog.WriteLine($"Double"); } else if (cnl.Value.DataTypeID == 1) // тип данных канала Int64 { if (field.FieldType.ToString() == "System.UInt16") { valDouble = Convert.ToUInt16(ObjValue); moduleLog.WriteLine($"System.UInt16 {Convert.ToDouble(ObjValue)} {valDouble}"); } ServerContext.WriteCurrentData(cnl.Value.CnlNum, new CnlData(valDouble, 1)); // Test //moduleLog.WriteLine($"Int64"); } } if (typeIdCnl == 2 || typeIdCnl == 4) // Каналы с выходом Input/Output и Calculated/Output { if (cnl.Value.DataTypeID == 0 || cnl.Value.DataTypeID == null) { ServerContext.SendCommand(new TeleCommand(cnl.Value.CnlNum, valDouble, 11)); // 11 здесь ID админа } else if (cnl.Value.DataTypeID == 1) // тип данных канала Int64 { long val = Convert.ToInt64(ObjValue); ServerContext.SendCommand(new TeleCommand(cnl.Value.CnlNum, Convert.ToDouble(val), 11)); // 11 здесь ID админа } }23.08.2024 в 09:45 #34166
manjey73УчастникCnlData curData = ServerContext.GetCurrentData(cnl.Value.CnlNum);
Вот тут то все идентично — получили значение — записали значение в поток через поле и SetValue
В логе это видно. Но с какого перепуга то значение по умолчанию, то то, которое я ввел через WEB?
Не было бы шанса ввести через web так он бы и перетирал бы введенное мною значение на то, что там по умолчанию. А тут чудеса, да леший бродит. Как будто кто-то где-то кеширует данные и начинает сам менять значение то первое, то вновь введенное.
23.08.2024 в 10:28 #34167
manjey73УчастникНапример если изменить только cycle без включения EN, то cycle начинает козлить и менять значение между первоначальным и введенным.
При этом EN продолжает быть = 0 и не меняется.Стоит включить EN (= 1 On) как и он начинает козлить…
Тут кажется уже вопрос к Михаилу, как система взаимодействует с модулем после отправки команды?
-
Ответ изменён 2 года назад пользователем
-
АвторЗаписи
- Для ответа в этой теме необходимо авторизоваться.