Потоки(Thread) — Программы(class) — Переменные

Стартовая страница Форумы Разработка и интеграция Потоки(Thread) — Программы(class) — Переменные

  • В этой теме 123 ответа, 4 участника, последнее обновление 2 года назад сделано manjey73.
Просмотр 15 сообщений - с 76 по 90 (из 124 всего)
  • Автор
    Записи
  • #34152
    Romiros
    Участник

    А если добавить входную формулу Val();Stat(), Будут ли изменения?

    #34153
    manjey73
    Участник

    Нет изменений, переменная так же пляшет.
    Мне кажется я как-то неправильно сохраняю потоки и данные о них. Но толковых примеров не нахожу.

    Переменная пляшет между значением по умолчанию, которое берется из конфиг файла и последним введенным мной значением.

    • Ответ изменён 2 года назад пользователем manjey73.
    #34155
    JurasskPark
    Участник

    По описанию выглядит на то, что изменение переменной у вас создаёт новый поток с новым значением.
    И теперь два потока в один тег пишут по очереди
    1000
    2000
    1000
    2000

    #34156
    JurasskPark
    Участник

    В свойства потока добавьте генерируемый Guid id и в логах пишите с какого id какое value пишется в тег.

    #34157
    manjey73
    Участник

    Video

    screen

    Маленькое видео и скрин лога. Извиняйте, ютюбам не обучен. Видео на 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. Друг другу вроде не мешают.

    #34158
    manjey73
    Участник

    Да вроде нет новых потоков, в том то и дело. Да и по логам я вывожу их id видно, что они не меняются, как создалось три так и остается три.

    Это какой-то косяк именно с входными переменными потока. мои переменные cycle заданные по умолчанию при старте модуля и переменные Q = false при старте модуля.
    Они начинают моргать с какого-то…

    Хотя .SetValue в поток отрабатывает исправно, иначе бы счет не начинался…

    #34159
    manjey73
    Участник

    screen2

    Вот включил EN у всех трех потоков. Первая половина скрина видно как сработал Q первого потока.
    Вторая половина, добавился Q второго потока.
    Третий так же считает EQ со своим временем цикла.

    Но входы перемаргивают, и по логам в них запись то первого значения то нового.
    А код класса самих потоков один в один.

    Вот в этом и чудеса, почему он самостоятельно берет то старое то новое значение?
    Все каналы при этом Calculated/Output.
    Единственное, на выходных нет формул вообще. Формулы есть только на входных переменных.
    Если разобраться, то формула SetVal(CnlNum, Cmd) выходная никак не должна влиять.

    #34160
    manjey73
    Участник

    cycle и EN = false правильно будет, в сообщении #34158

    #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 Поток
    }
        }
    }
    
    
    #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);
    
    #34163
    manjey73
    Участник

    Примерно как-то так. Вытягиваю свой тестовый Heater и запускаю поток.

    Смущает то, что EQ и Q считаются и работают в зависимости от времени сна и включенного состояния после добавления переменной EN в класс программы.
    Потоки останавливаются при остановке сервера, запускаются при старте.

    Но кто и где постоянно переписывает значения между первоначальным и введенным загадка.

    #34164
    JurasskPark
    Участник

    Но кто и где постоянно переписывает значения между первоначальным и введенным загадка.

    Может у вас в коде используется одна переменная как private online, которая при инициализации класса создается, а вторая локальная с таким же именем?

    #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 админа
                                        }
                                    }
    
    #34166
    manjey73
    Участник

    CnlData curData = ServerContext.GetCurrentData(cnl.Value.CnlNum);

    Вот тут то все идентично — получили значение — записали значение в поток через поле и SetValue

    В логе это видно. Но с какого перепуга то значение по умолчанию, то то, которое я ввел через WEB?

    Не было бы шанса ввести через web так он бы и перетирал бы введенное мною значение на то, что там по умолчанию. А тут чудеса, да леший бродит. Как будто кто-то где-то кеширует данные и начинает сам менять значение то первое, то вновь введенное.

    #34167
    manjey73
    Участник

    Например если изменить только cycle без включения EN, то cycle начинает козлить и менять значение между первоначальным и введенным.
    При этом EN продолжает быть = 0 и не меняется.

    Стоит включить EN (= 1 On) как и он начинает козлить…

    Тут кажется уже вопрос к Михаилу, как система взаимодействует с модулем после отправки команды?

Просмотр 15 сообщений - с 76 по 90 (из 124 всего)
  • Для ответа в этой теме необходимо авторизоваться.