В классическом InTouch и в Application Server нельзя использовать теги и переменные типа Integer емкостью более 32 бит. Но, если при работе с Application Server есть доступ к классу MS .NET System.UInt64 (System.Int64), то в классическом InTouch этот функционал отсутствует. В практике, периодически, возникают задачи принять данные с разрядностью более 32 бит.
Недавно передо мной встала проблема отображения данных со счетчика, подключенного по Modbus RTU. Счетчик выдает данные в 3-х шестнадцати разрядных регистрах в целочисленном формате, т.е. для отображения этого значения мне потребуется не менее 48 бит. Но я взялся за более общую задачу — завести и отобразить переменную емкостью 64 бита (на самом деле описанный ниже способ легко переделать и для еще большей емкости переменной :)) хоть такого в практике я уже и не встречал).
Содержание
Для начала немного теории:
Тип данных |
Допустимые значения |
Комментарии |
signed 64-bit integer |
|
|
unsigned 64-bit integer |
|
|
signed 32-bit integer |
|
Именно этот тип данных используется в InTouch тегах типа и атрибутах Application Server для типа Integer |
Пример для InTouch Stand Alone
- Знак нам в расчетах будет только мешать, а значит нельзя использовать числа емкостью более 31 бита
- В дальнейшем скрипт будет легко использовать (или переделать) и для 48 и для 32 битных без знаковых переменных
В InTouch есть тип данных message (memory или IO) — его мы будем использовать для вывода результата.
Сам предложенный мной алгоритм прост: принять четыре переменных типа UINT16, представить их в шестнадцатеричной системе счисления (с ней просто проще работать) в виде строки, затем посимвольно преобразовать из шестнадцатеричной в десятичную систему счисления, параллельно складывая их значения и результат поместить опять таки в строку. В общем-то подобные заморочки с преобразованием туда-сюда обусловлены наличием определенных функций в InTouch Script и тем, что при сложении нам не подойдет только тип real, т.к. он будет обрезать нам результат. Если кто увидит решение проще — прошу написать или на почту или в комментариях к этому посту.
1. Для решения задачи создадим QuickFunction convertTo64 и добавим ей 4-ре аргумента типа Integer: w1, w2, w3, w4. Это наши сырые данные в четырех 16-ти разрядных переменных. Напомню что: в свойствах тегов для которых будет наш скрипт вызываться ОБЯЗАТЕЛЬНО необходимо прописать min=-2147483648, max=2147483647
2. Определим вспомогательные переменные для хранения наших данных в 16-ном формате:
s — для хранения всего числа, s1…s4 — для получения отдельных 16-ти битных данных от w1…w4
DIM s AS MESSAGE; DIM s1 AS MESSAGE; DIM s2 AS MESSAGE; DIM s3 AS MESSAGE; DIM s4 AS MESSAGE;
3. Используя функцию StringFromIntg мы одновременно преобразуем w1…w4 в строки и конвертируем данные в шестнадцатеричный формат
s1 = StringFromIntg( w1, 16 ); s2 = StringFromIntg( w2, 16 ); s3 = StringFromIntg( w3, 16 ); s4 = StringFromIntg( w4, 16 );
4. Так как получившееся шестнадцатеричное число может быть не полным, т.е. занимать менее 4-х символов, то необходимо строки дополнить нулями:
DIM i AS INTEGER; FOR i= StringLen( s1) +1 TO 4 s1="0"+s1; NEXT; FOR i= StringLen( s2) +1 TO 4 s2="0"+s2; NEXT; FOR i= StringLen( s3) +1 TO 4 s3="0"+s3; NEXT; FOR i= StringLen( s4) +1 TO 4 s4="0"+s4; NEXT;
5. Итог положим в переменную s. Кстати, именно на этом шаге можно переопределить последовательность слов в итоговом числе. У меня s1 старшее слово, s4 — младшее. За одним используя функцию StringUpper приведем всю нашу строку в верхнему регистру.
s = s1 + s2 + s3 + s4; s = StringUpper( s );
6. Последний шаг — самый важный. Здесь происходит преобразование числа хранящегося в шестнадцатеричном формате в строке s в число типа real. А затем и формирование результата в виде строки и возврат этого значения.
В целом переменные:
d — для хранения части числа в формате real. Части, потому что типа real не хватит для записи без потерь всего числа.
si — текущий выбранный из строки символ в строковом формате
ii — текущий выбранный из строки символ в шестнадцатеричном формате, символы A…F уже заменены числами 10…15 (для удобства расчета)
sTemp и rTemp — временные переменные для хранения части числа и строки, опять таки потому что типа real не хватает для записи данных без потерь
DIM d AS REAL; d=0; DIM si AS MESSAGE; DIM ii AS INTEGER; DIM sTemp AS MESSAGE; DIM rTEMP AS REAL; FOR i = 15 TO 0 STEP -1 si = StringMid(s,16-i,1); IF StringCompare (si, "A") == 0 THEN ii = 10; ELSE IF StringCompare (si, "B") == 0 THEN ii = 11; ELSE IF StringCompare (si, "C") == 0 THEN ii = 12; ELSE IF StringCompare (si, "D") == 0 THEN ii = 13; ELSE IF StringCompare (si, "E") == 0 THEN ii = 14; ELSE IF StringCompare (si, "F") == 0 THEN ii = 15; ELSE ii = StringToIntg(si); ENDIF; ENDIF; ENDIF; ENDIF; ENDIF; ENDIF; d = d+ii*16**i; IF i==8 THEN rTemp = Trunc(d/10**15); IF rTemp > 0 THEN sTemp = StringFromIntg(rTemp, 10); d = d - rTemp*10**15; ENDIF; ENDIF; NEXT; RETURN sTemp + StringFromReal(d, 0, "f");
Пример для InTouch Managed
Так как InTouch Managed приложение позволяет использовать ArchestrA символы с доступом к функционалу MS .NET Framework то шаг 6 выше описанного примера будет гораздо проще:
DIM i64 as System.UInt64; i64 = System.UInt64.Parse(s, System.Globalization.NumberStyles.HexNumber); sVal = i64.ToString();
Здесь sVal — строковая переменная с результатом.
Остальные шаги 1…5 останутся теми же.
Сам скрипт можно вставить внутрь ArchestrA символа. Или при использовании Application Server — внутрь объекта.
Выводы
Приведенные примеры позволяют только отобразить данные, но не позволяют записывать их обратно. Но я думаю сам алгоритм понятен и при необходимости написать скрипт для обратного преобразования из строки в несколько 16-ти разрядных регистров не проблема.
Кроме этого хочу отметить, что в Application Server есть возможность использования атрибутов типа double (2.23E-308 … 1.80E+308)