Представление 64 битных беззнаковых целочисленных значений в InTouch

В классическом InTouch и в Application Server нельзя использовать теги и переменные типа Integer емкостью более 32 бит. Но, если при работе с Application Server есть доступ к классу MS .NET System.UInt64 (System.Int64), то в классическом InTouch этот функционал отсутствует. В практике, периодически, возникают задачи принять данные с разрядностью более 32 бит.

Недавно передо мной встала проблема отображения данных со счетчика, подключенного по Modbus RTU. Счетчик выдает данные в 3-х шестнадцати разрядных регистрах в целочисленном формате, т.е. для отображения этого значения мне потребуется не менее 48 бит. Но я взялся за более общую задачу — завести и отобразить переменную емкостью 64 бита (на самом деле описанный ниже способ легко переделать и для еще большей емкости переменной :)) хоть такого в практике я уже и не встречал).

Для начала немного теории:

Тип данных

Допустимые значения

Комментарии

signed 64-bit integer
  • -9,223,372,036,854,775,808
  • 9,223,372,036,854,775,807
unsigned 64-bit integer
  • 0
  • 18,446,744,073,709,551,615
signed 32-bit integer
  • -2,147,483,648
  • 2,147,483,647
Именно этот тип данных используется в InTouch тегах типа и атрибутах Application Server для типа Integer
Посмотрев на эти большие числа сложно представить реальную задачу для вывода на экран оператора скажем числа 18 квинтиллионов  (18*10^18), но ранее приведенный пример со счетчиком показывает, что 32 бит все таки может быть мало.

Пример для InTouch Stand Alone

Сразу оговорюсь, что данный пример для ситуации так называемого приложения InTouch Stand Alone, т.е. без доступа к MS .NET функционалу (без Application Server, без ArchestrA graphic и пр.)
Итак, посмотрим чем мы располагаем.
В InTouch есть тип тега Integer (memory или IO) — через теги этого типа мы будем заводить данные. Тип Integer позволяет оперировать целочисленными 32 битными знаковыми данными. В свойствах тегов ОБЯЗАТЕЛЬНО необходимо прописать min=-2147483648,   max=2147483647
Для ввода данных мы будем использовать 4 тега типа Integer. Четыре, потому что:
  1. Знак нам в расчетах будет только мешать, а значит нельзя использовать числа емкостью более 31 бита
  2. В дальнейшем скрипт будет легко использовать (или переделать) и для 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)

 

 

 

 

Добавить комментарий