Инициализация
PROGRAM MAIN VAR PLCVar : INT; END_VAR
private int hVar; private TcAdsClient adsClient; private short plcVar;
TcAdsClient adsClient = new TcAdsClient();
Подключаемся к ПЛК-рантайму №1.
Для TwinCAT2 порт = 801, для TwinCAT3 порт = 851.
adsClient.Connect("172.16.3.217.1.1", 801); // TwinCAT 2 adsClient.Connect("172.16.3.217.1.1", 851); // TwinCAT 3
Подключение
Как таковой связи еще нет, мы всего-лишь подключились к локальному роутеру. Поэтому, перед созданием дескрипторов переменных, необходимо как-то поймать момент, когда переменные ПЛК уже доступны. Произойдет это после запуска ПЛК-задачи контроллера:
StateInfo info = new StateInfo(AdsState.Invalid, 0); while (info.AdsState != AdsState.Run) { try { info = adsClient.ReadState(); } catch { } Thread.Sleep(100); // ждем 100мс перед следующей проверкой } // OnConnected(); // отправляем в родительский поток сообщение о подключении
Такой кусок кода нужно запустить в отдельном потоке и дождаться сообщения о благополучном запуске задачи. В C#.NET 4.5 удобно использовать конструкции async/await.
Синхронное чтение
Создаем дескриптор для переменной из контроллера
try
{ hVar = adsClient.CreateVariableHandle("MAIN.PLCVar"); } catch(Exception ex) { MessageBox.Show(ex.Message); }
Немного исправим способ из справочной системы.
plcVar = (short) adsClient.ReadAny(hVar, typeof(short)); // Было plcVar = (short) adsClient.ReadAny(hVar, plcVar.GetType()); // Стало * plcVar = (short)(adsClient.ReadAny(hVar, plcVar.GetType()) ?? 0); // Избыточный вариант **
(*) typeof(short) лучше превратить в plcVar.GetType(), это заставит разработчика проинициализировать переменную до ее первого использования.
(**) ReadAny() возвращает object который всегда не-null, иначе выбрасывается исключение, т. е. null не бывает никогда, поэтому можно избежать использования nullable-типов (short?) с дальнейшей проверкой на null или более простой конструкции "??".
Обработка ошибок
Так как до- или во время чтения/записи может произойти обрыв связи или другие неприятности, необходима обработка исключений.
try
{ plcVar = (short)adsClient.ReadAny(hVar, plcVar.GetType()); } catch {}
Но, здесь нет анализа ошибки.
try
{ plcVar = (short)adsClient.ReadAny(hVar, plcVar.GetType()); } catch(Exception ex) { MessageBox.Show(ex.Message); }
Вместо анализа ошибки, закидает пользователя и рабочий стол сообщениями об ошибке.
AdsErrorCode errCode = AdsErrorCode.NoError; try { plcVar = (short)adsClient.ReadAny(hVar, plcVar.GetType()); } catch(AdsErrorException ex) { errCode = ex.ErrorCode; } catch(Exception genex) { // Произошло что-то не связанное с ADS.API } if (errCode != AdsErrorCode.NoError) { switch(ex.ErrorCode) { case AdsErrorCode.ClientSyncTimeOut: // Обрыв связи // OnDisconnected(); break; case AdsErrorCode.DeviceSymbolNotFound: // Переменная пропала (например, была стерта программы) // OnInvalidData(); break; default: // Все, что не требует обработки // OnGeneralError(); break; } }
Вариант правильный, но очень длинный. На каждое чтение/запись такое не напишешь. Поэтому для чтения данных мастерят "обертки", берущие на себя всю рутину обработки ошибок и пр.
No comments
Post a Comment
Note: Only a member of this blog may post a comment.