2015-05-15 7 views
10

Я разрабатываю клиентский OPC-клиент C#, я начал писать в консольном приложении быстро, все работает отлично, как я хочу.C# OPC Приложения Идентичный код, но работают по-другому

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

Приложение для создания окон просто прекращает работу, прекращает чтение данных с сервера OPC примерно через минуту. Где, когда консольное приложение продолжает читать и читать.

Я не могу найти ничего очевидного в режиме отладки.

Я абсолютно сжимаю соломинку здесь и, надеюсь, кто-то может пролить свет.

Каждое приложение использует DLL-файлы, предоставленные OPCFoundation.

Вот консольное приложение

static void Main(string[] args) 
     { 

      Opc.URL url = new Opc.URL("opcda://localhost/RSLinx OPC Server"); 
      Opc.Da.Server server = null; 
      OpcCom.Factory fact = new OpcCom.Factory(); 
      server = new Opc.Da.Server(fact, null); 
      server.Connect(url, new Opc.ConnectData(new System.Net.NetworkCredential())); 
      // Create a group 
      Opc.Da.Subscription group; 
      Opc.Da.SubscriptionState groupState = new Opc.Da.SubscriptionState(); 
      groupState.Name = "Group"; 
      groupState.Active = true; 
      group = (Opc.Da.Subscription)server.CreateSubscription(groupState); 
      // add items to the group. 
      Opc.Da.Item[] items = new Opc.Da.Item[6]; 
      items[0] = new Opc.Da.Item(); 
      items[0].ItemName = "[UX1]F20:9"; 
      items[1] = new Opc.Da.Item(); 
      items[1].ItemName = "[UX1]F22:30"; 
      items[2] = new Opc.Da.Item(); 
      items[2].ItemName = "[UX1]F22:6"; 
      items[3] = new Opc.Da.Item(); 
      items[3].ItemName = "[UX1]F18:8"; 
      items[4] = new Opc.Da.Item(); 
      items[4].ItemName = "[UX1]F22:32"; 
      items[5] = new Opc.Da.Item(); 
      items[5].ItemName = "[UX1]F22:5"; 
      items = group.AddItems(items); 

       group.DataChanged += new Opc.Da.DataChangedEventHandler(OnTransactionCompleted); 

     } 





     static void OnTransactionCompleted(object group, object hReq, Opc.Da.ItemValueResult[] items) 
     { 

      Console.WriteLine("------------------->"); 
      Console.WriteLine("DataChanged ..."); 
      for (int i = 0; i < items.GetLength(0); i++) 
      { 

        Console.WriteLine("Item DataChange - ItemId: {0}", items[i].ItemName); 
        Console.WriteLine(" Value: {0,-20}", items[i].Value); 
        Console.WriteLine(" TimeStamp: {0:00}:{1:00}:{2:00}.{3:000}", 
        items[i].Timestamp.Hour, 
        items[i].Timestamp.Minute, 
        items[i].Timestamp.Second, 
        items[i].Timestamp.Millisecond); 

      } 
      Console.WriteLine("-------------------<"); 
     } 

Вот приложение WinForm

public Form1() 

    { 
     InitializeComponent(); 
     _Form1 = this; 
    } 

    public static Form1 _Form1; 

    public void update(string message) 

    { 
     this.richTextBox1.Text = message; 
    } 

    private void Form1_Load(object sender, EventArgs e) 

    { 

     readplc(); 

    } 


static void readplc() 
     { 
       Opc.URL url = new Opc.URL("opcda://localhost/RSLinx OPC Server"); 
      Opc.Da.Server server = null; 
      OpcCom.Factory fact = new OpcCom.Factory(); 
      server = new Opc.Da.Server(fact, null); 
      server.Connect(url, new Opc.ConnectData(new System.Net.NetworkCredential())); 
      // Create a group 
      Opc.Da.Subscription group; 
      Opc.Da.SubscriptionState groupState = new Opc.Da.SubscriptionState(); 
      groupState.Name = "Group"; 
      groupState.Active = true; 
      group = (Opc.Da.Subscription)server.CreateSubscription(groupState); 
      // add items to the group. 
      Opc.Da.Item[] items = new Opc.Da.Item[6]; 
      items[0] = new Opc.Da.Item(); 
      items[0].ItemName = "[UX1]F20:9"; 
      items[1] = new Opc.Da.Item(); 
      items[1].ItemName = "[UX1]F22:30"; 
      items[2] = new Opc.Da.Item(); 
      items[2].ItemName = "[UX1]F22:6"; 
      items[3] = new Opc.Da.Item(); 
      items[3].ItemName = "[UX1]F18:8"; 
      items[4] = new Opc.Da.Item(); 
      items[4].ItemName = "[UX1]F22:32"; 
      items[5] = new Opc.Da.Item(); 
      items[5].ItemName = "[UX1]F22:5"; 
      items = group.AddItems(items); 



       group.DataChanged += new Opc.Da.DataChangedEventHandler(OnTransactionCompleted); 


     } 




     static void OnTransactionCompleted(object group, object hReq, Opc.Da.ItemValueResult[] items) 
     { 

      for (int i = 0; i < items.GetLength(0); i++) 
      { 

       UIUpdater TEXT = new UIUpdater(); 
        TEXT.UpdateText(items.GetLength(0).ToString() + " t " + i.ToString() + "Item DataChange - ItemId:" + items[i].ItemName + 
         "Value: " + items[i].Value + " TimeStamp: " + items[i].Timestamp.Hour + ":" + 
         items[i].Timestamp.Minute + ":" + items[i].Timestamp.Second + ":" + items[i].Timestamp.Millisecond); 

      } 

     } 

UIUpdate Класс

class UIUpdater 

    { 

     public void UpdateText(string DATA) 

     { 
      Form1._Form1.update(DATA); 
     } 

     public class UpdateUI 

     { 



      public int updatedRows { get; set; } 

      public string Custom1 { get; set; } 

      public string Custom2 { get; set; } 

      public string Custom3 { get; set; } 

      public string exception { get; set; } 

      public plcTextStatus PLCStatus { get; set; } 


     } 

Любые вопросы, пожалуйста, спросите!

+0

Что такое 'UIUpdater'? –

+0

Просто класс для обновления UserInterface Thread – SK2017

+0

Можете ли вы опубликовать код для него? Это похоже на проблему с перекрестной резьбой. Я разработал множество приложений, использующих OPC самостоятельно, RSLinx довольно устойчив, похоже, что вы подключаетесь к Micrologix или SLC, или что-то вроде эпохи PLC5 ... –

ответ

5

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

Он работает некоторое время, потому что он относительно терпим к ошибкам, однако вы, вероятно, достигли точки, в которой вы зашли в тупик или выбрасывали исключение, которое не попадает (или не сообщается).

Исправление достаточно простое.

В этом методе:

public void update(string message) 
{ 
    this.richTextBox1.Text = message; 
} 

изменить его на:

public void update(string message) 
{ 
    richTextBox1.Invoke(
     (MethodInvoker) delegate 
     { 
      richTextBox1.Text = message; 
     }); 
} 

Что это делает говорит richTextBox1 для «вызова» или запустить следующий делегат (функция), на которой он принадлежит нити (ака, поток пользовательского интерфейса).

Вы действительно должны стараться избегать использования static методов и ссылок в этом коде. Я не вижу причин, по которым код, который у вас есть, не должен быть экземпляром вместо статических.

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

Редактировать

Другая проблема, которую вы имеете, используя локальные ссылки (на сервер OPC, и подписки, например) и показывает утечку памяти и объект зомби. Случается, что метод readplc выходит за пределы области видимости, и вы создали ссылки на объекты внутри, которые хранятся в памяти. Поскольку у вас нет возможности отказаться от подписки на мероприятие, мероприятие продолжает стрелять. Эти переменные должны быть объявлены за пределами метода readplc, чтобы вы могли надлежащим образом отменить подписку на событие и завершить работу OPC-сервера. В противном случае вы покидаете подписки на зомби (посмотрите страницу диагностики RSLinx OPC Diagnostics, вы увидите все ваши подписки).

+0

Просто изменил код на Invoke, все еще не изменил ситуацию, он по-прежнему разбился после столь долгого – SK2017

0

Поместите ваш сервер снаружи метода readplc() в качестве объекта уровня формы. Пока ваша форма будет создана (не закрыта) - ваш объект сервера будет активным, и так должно быть событие вашей подписки.

Сервер, скорее всего, собирается сборщиком мусора.

Opc.Da.Server server = null; 

static void readplc() 
     { 
       Opc.URL url = new Opc.URL("opcda://localhost/RSLinx OPC Server"); 
      Opc.Da.Server server = null; 
      OpcCom.Factory fact = new OpcCom.Factory(); 
      **this.server = new Opc.Da.Server(fact, null);** 
    .... 
    } 

 Смежные вопросы

  • Нет связанных вопросов^_^