Сегодня вновь столкнулся с этой SysLibCom. Так получилось, что раньше я работал напрямую только с 485 портом и до сегодняшнего дня не знал, насколько всё плохо на самом деле. Если в двух словах, то документация — враньё.
Началось с того, что я попробовал что-нибудь записать в 232 порт (COM1, порядковый 1). SysComWrite никак не работала, а результат SysComSetSettings я никак не мог понять (да, я знаю про NOT). Оказывается...
SysLibCom на контроллерах Овен вопреки документации вообще не работает с дескрипторами! Везде только номера портов!
Например, SysComOpen возвращает не дескриптор, а всего лишь успех/неудачу. В этом легко убедиться, если попробовать открыть сразу два разных порта — там и там функция вернёт ноль, но дескрипторы двух разных объектов не могут быть одинаковыми. На то они и дескрипторы. В общем, ноль — порт открыт, 16#FFFFFFFF — порт занят (очень похоже на дескриптор, но это не так). То есть по факту это булевая функция. Пользоваться ею надо так: portOpened := SysComOpen(COM1) = 0.
Дальше жирный вывод подтверждается тем, что все остальные функции не работают с «дескриптором», который возвращает SysComOpen. Лишь по случайности они работают для RS-485-1, т.к. он нулевой по порядку, и лже-дескриптор от SysComOpen тоже нулевой. Из-за этого совпадения я заблуждался с 2011 года.
Вот так нужно открывать порты и работать с ними в контроллерах Овен:
Код:
PROGRAM PLC_PRG
VAR CONSTANT
port: COMSETTINGS := (Port := COM1, dwBaudRate := 38400);
END_VAR
VAR
portReady: BOOL;
buf: ARRAY [0..512] OF BYTE;
END_VAR
IF NOT portReady THEN
(* Сразу открываем и настраиваем порт *)
portReady := SysComOpen(port.Port) = 0 = SysComSetSettings(port.Port, ADR(port));
ELSE
(* Работаем с открытым портом *)
SysComRead(port.Port, ADR(buf), 1000, 0);
END_IF
А вот так надо открывать порты и работать с ними, если руководствоваться документацией (аккуратно, нерабочий код!):
Код:
PROGRAM PLC_PRG
VAR CONSTANT
port: COMSETTINGS := (Port := COM1, dwBaudRate := 1200);
handle: DWORD;
END_VAR
VAR
portReady: BOOL;
buf: ARRAY [0..512] OF BYTE;
END_VAR
IF NOT portReady THEN
(* Сначала открываем порт и получаем дескриптор *)
handle := SysComOpen(port.Port);
(* Теперь настраиваем порт через дескриптор; не забываем про странный NOT *)
portReady := handle <> INVALID_HANDLE AND NOT SysComSetSettings(handle, ADR(port));
ELSE
(* Работаем с открытым портом — тоже через дескриптор *)
SysComRead(handle, ADR(buf), 1000, 0);
END_IF
Работа через дескрипторы правильно реализована в SysLibFile — можете посмотреть примеры с ней, функции там похожие.
Вот такая подстава, господа. И напоследок небольшой ликбез по следующему вопросу:
Хоть файл это и не аппаратное устройство, но File file = new File("path");
Приблизительно во всех одинаково, так file же неизменен, да его экземпляр имеет уникальный хеш но в проге, Вы будете использовать только имя file
Хеш — вряд ли. Класс File как раз будет обёрткой вокруг дескриптора. Поле со словом handle в имени почти наверняка будет членом этого класса. А дескриптор, в свою очередь, это своего рода номерной билет на доступ к ресурсу, который выдаётся операционной системой. Номер в этом билете может быть любой, и упорядоченность этих номеров никак не гарантируется. Зато гарантируется уникальность в пределах контекста, то есть два файла не могут быть открыты под одинаковыми дескрипторами, и два порта тоже не могут.