Сохрание %MW в файл

Модератор: SaniOK

Сохрание %MW в файл

Сообщение Fessar » 23 окт 2015, 10:35

В контроллерах Schneider есть такая вещь, как холодный старт. Он возникает при следующих ситуациях:
-Загрузка приложения
-Нажата кнопка RESET
-Перемещение ручки или вставка/удаление карты памяти
-Форсирование системного бита %S0
-Восстановление после отсутствия питания с потерей контекста

У контроллера M340(а может и других) есть неприятная особенность - по умолчанию после холодного старта, значения, сохранённые в инициализированных в области %MW переменных либо обнуляются, либо принимают значение, заданное в строке value. Чтобы этого избежать, в шнайдере реализовали возможность сохранять эти значения и восстанавливать их после холодного старта. Для этого нужно снять галочку "Initialize %MWi on cold start" и выставить бит %SW96 в единичку. НО, сохранение произойдёт только после перевода контроллера из режима RUN в режим STOP, но в некоторых случаях остановка контроллера не допустима.

В какой-то момент мне в голову пришла идея записывать значения %MWв файл на карте памяти. Наступив на кучу граблей, оставленных разработчиками UnitPro, заморочился и написал таки нужный алгоритм(для работы нужны карты BMX RMS 008MPF или BMX RMS 128MPF).
Код: выделить все
(*Чтение значений из файла в массив при первом цикле после холодного старта
  или по 1 в переменной first_cycle. %SW10.0 - бит, который при первом цикле
  после холодного старта равен 0, после чего выставляется в 1*)
filename:='Test4.txt';(*задаём имя файла*)
if not %SW10.0 OR re(first_cycle) then
   open_req_1:=1;
   first_cycle:=1;
end_if;
(*Открываем файл*)
OPEN_FILE_1 (REQ := open_req_1,
             SLOT := 0,
             FILENAME := filename,(*имя читаемого файла*)
             MODEFLAG := 2,
             DONE => done_open_1,
             ERROR => error_open_1,
        STATUS => status_open_1);
open_req_1:=0;

(*Если файл не существует - создаём его, если существует - читаем*)
if not %SW10.0 or first_cycle then
   if error_open_1 and status_open_1=7 then
      first_cycle:=0;
      create_req_0:=1;
      cmd_create_0:=1;
   end_if;
end_if;

if not %SW10.0 or first_cycle then
   if done_open_1 and not error_open_1 then
      first_cycle:=0;
      read_req_3:=1;
      cmd_read_3:=1;
      filedesc:=OPEN_FILE_1.filedesc;
   end_if;
end_if;


(*создаём файл*)
CREATE_FILE_0 (REQ := create_req_0,
               SLOT := 0,
               FILENAME := filename,
               MODEFLAG := 2,
               DONE => done_create_0,
               ERROR => error_create_0);
create_req_0:=0;
(*после создания закрываем*)
if cmd_create_0 then
   if done_create_0 and not error_create_0 then
      cmd_create_0:=0;
      close_req_1:=1;
      cmd_close_1:=1;
                filedesc:=CREATE_FILE_0.filedesc;
   end_if;
end_if;


(*читаем данные из файла в строку*)
RD_FILE_TO_DATA_3 (REQ := read_req_3,
                   FILEDESC := OPEN_FILE_1.filedesc,
                   VARIABLE => big_string_0,
         DONE => done_read_3,
         ERROR => error_read_3,
         STATUS => status_read_3,
         NBBYTESRD => status_NBBYTESRD_3);
read_req_3:=0;

if cmd_read_3 then
   if done_read_3 and not error_read_3 then
      cmd_read_3:=0;
      close_req_1:=1;
      cmd_close_1:=1;
   end_if;
end_if;

(*Закрываем файл*)
CLOSE_FILE_1 (REQ := close_req_1,
              FILEDESC := filedesc,
         DONE=>done_close_1,
         ERROR=>error_close_1,
         STATUS=>status_close_1);
close_req_1:=0;

if cmd_close_1 then
   if done_close_1 and not error_close_1 then
      cmd_close_1:=0;
   end_if;
start_string_to_array:=1;(*Устанавливаем флаг начала записи из строки в массив*)
first_cycle:=0;
end_if;

(*Начало записи значений из строки в массив*)
if start_string_to_array then
index_INT:=100;(*начальный индекс для поиска в файле*)
for i:=0 TO 149 BY 1 DO
index:=INT_TO_STRING(index_INT);    (*преобразование числа в строку*)
index_2:=INT_TO_STRING(index_INT+1);(*преобразование числа в строку*)
position := INSERT_INT   (IN1 := 'xx', (*запись метки "хх"  с индексом "index_INT" в строку, например x+00100x*)
                          IN2 := index,
                          P := 1);
position_2 := INSERT_INT (IN1 := 'xx', (**запись метки "хх"  с индексом "index_INT+1" в строку, например x+00101x*)
                          IN2 := index_2,
                          P := 1);
label_before := FIND_INT (IN1 := big_string_0, (*поиск позиции в строке по x index_INT x*)
                          IN2 := position);
label_after := FIND_INT  (IN1 := big_string_0, (*поиск позиции в строке по x index_INT+1 x*)
                          IN2 := position_2);

temp_string := MID_INT   (IN := big_string_0, (*запись актуального значения из строки во врЕменную строку*)
                          N := label_after-(label_before+8),
                          P := label_before+8);
(*вновь созданный фал пуст, а соответственно будет пустой и строка. При этом в операции
  Value_MW[i]:=STRING_TO_REAL (temp_string) в ячейки массива будут записываться +NAN,
  что вызовет ошибку в дальнейшем. По этому, если строка пустая(в label_before будет -1)
  в массив, где по умолчанию нули, ничего не записываем*)
if label_before<>-1 then
Value_MW[i]:=STRING_TO_REAL (temp_string);(*запись из временной строки в ячейку массива*)
end_if;
index_INT:=index_INT+1;
end_for;
start_string_to_array:=0;
start_array_to_MW:=1;(*Флаг записи из массива в переменные с %MW*)
end_if;
   



(*-------------------------------------------------------------------------------------------------*)


(*Запись из массива в %MW*)
if start_array_to_MW then
wefwr(*%MW*):=REAL_TO_INT (Value_MW[0]);
start_array_to_MW:=0;
start_comparison:=1;
end_if;

(*Сравнение массива с %MW. Если значения различаются,
  то актуальные значения записывается в массив и
  выставляется флаг записи массива в файл*)
if start_comparison then
if Value_MW[0] <> INT_TO_REAL (wefwr)(*%MW*) then Value_MW[0]:=INT_TO_REAL (wefwr); need_to_rewrite:=1; end_if;
end_if;
(*wefwr - это переменная для тестирования. Вместо неё писать нужные %MW. Для каждой %MW новая строчка,
  т.к. у разных %MW может быть разный тип данных и соответственно нужно будет применять разные функции
  для преобразований*)


(*--------------------------------------------------------------------------------------------------*)


if need_to_rewrite and not first_cycle and %SW10.0 then
(*запись меток в строку*)
Length_Str:=0;
index_INT:=100;
for i:=0 TO 150 BY 1 DO
index:=INT_TO_STRING(index_INT);    (*преобразование числа в строку*)

position := INSERT_INT   (IN1 := 'xx', (*запись индекса x index_INT x в строку*)
                          IN2 := index,
                          P := 1);

(*первая итеррация цикла. очищаем строку*)
if i=0 then
Length_Str:= LEN_INT(big_string_0); (*подсчитываем длину строки*)
big_string_0 := DELETE_INT (IN := big_string_0, (*стираем строку*)
                            N := Length_Str,
                            P := 0);
(*INSERT_INT не может вставлять что-то в начало строки, т.е. в нулевую позицию,
  а RD_FILE_TO_DATA ничего не прочитает, если в начале файла ничего нет, по этому
  используем CONCAT_STR*)
big_string_0 := CONCAT_STR (IN1 := big_string_0,
                            IN2 := position);
else
Length_Str:= LEN_INT(big_string_0); (*подсчитываем длину строки*)
big_string_0 := INSERT_INT   (IN1 := big_string_0, (*запись индекса x index_INT x в строку*)
                              IN2 := position,
                              P := Length_Str);
end_if;

(*записываем метку в строку*)
(*2...151 итеррации. Записываем значения из массива в строку*)
if i<150 then
value_array:=REAL_TO_STRING (Value_MW[i]);
Length_Str:= LEN_INT(big_string_0); (*подсчитываем длину строки*)
big_string_0:= INSERT_INT   (IN1 := big_string_0,
                             IN2 := value_array,
                             P := Length_Str);
end_if;
index_INT:=index_INT+1;
end_for;
need_to_rewrite:=0;
start_rewrite:=1;
end_if;





(*Начало записи строки в файл.*)
if RE(start_rewrite) then
   OPEN_FILE_0_req:=1;
end_if;
(*Открываем файл*)
OPEN_FILE_0 (REQ := OPEN_FILE_0_req,
             SLOT := 0,
             FILENAME := filename,
             MODEFLAG := 2,
        DONE=>done_open_0,
        ERROR=>error_open_0);
OPEN_FILE_0_req:=0;

if start_rewrite then
   if done_open_0 and not error_open_0 then
      start_rewrite:=0;
      seek_req_0:=1;
      cmd_seek_0:=1;
   end_if;
end_if;

(*Выставляем курсор в файле в нужную позицию. В нашем случае это 0*)
SEEK_FILE_0 (REQ := seek_req_0,
             FILEDESC := OPEN_FILE_0.filedesc,
             OFFSET := 0,
             WHENCE := 0,
        DONE => done_seek_0,
        ERROR => error_seek_0);
seek_req_0:=0;

if cmd_seek_0 then
   if done_seek_0 and not error_seek_0 then
      cmd_seek_0:=0;
      write_req_1:=1;
      cmd_write_1:=1;
   end_if;
end_if;

(*Записываем строку в файл начиная с 0 позиции*)
WR_DATA_TO_FILE_1 (REQ := write_req_1,
                   FILEDESC := OPEN_FILE_0.filedesc,
                   VARIABLE := big_string_0,
                   DONE => done_write_1,
                   ERROR => error_write_1,
                   STATUS => status_write_1);
write_req_1:=0;

if cmd_write_1 then
   if done_write_1 and not error_write_1 then
      cmd_write_1:=0;
      close_req_2:=1;
      cmd_close_2:=1;
   end_if;
end_if;

(*Закрываем файл*)
CLOSE_FILE_2 (REQ := close_req_2,
              FILEDESC := OPEN_FILE_0.filedesc,
              DONE => done_close_2,
              ERROR => error_close_2);
close_req_1:=0;

if cmd_close_2 then
   if done_close_2 and not error_close_2 then
      cmd_close_2:=0;
   end_if;
end_if;

Пояснения:
Алгоритм разбил условно на три части
---Часть первая---
1) пытаемся открыть файл;
2) если файл не открылся по причине того, что его нет на карте - создаём файл;
3) если файл открылся - читаем файл в строку;
4) закрываем файл;
5) из строки вычленяем значения переменных. Тут надо пояснить. Возьму начало строки - x+00100x+1.2530000e+03x+00101x+0.0000000e+00x+00102x
x+00100x - метка начала первой переменной,
+1.2530000e+03 - значение первой переменной
x+00101x - метка конца первой переменной и одновременно начало второй переменной
и т.д. То есть программа вычленяет всё, что находится между Х число от 100 до 150 Х
Почему нельзя было просто читать значения определённой длины просто делая смещение начала чтения? Дело в том, что в %MW могут храниться разные типы данных. Например число в формате INT запишется в строку как +00999, а число в REAL как +9.9900000e+02.
6) заносим значения в массив. У меня массив на 150 ячеек. При желании его можно увеличить или уменьшить.
---Часть вторая---
Здесь, к сожалению в цикле работать не получится, т.к. %MW могут идти не по порядку.
1) переписываем значения из массива(актуальные значения) в %MW
2) данная операция выполняется постоянно в каждом цикле программы. Если с верхнего уровня изменили значения в %MW, это значение записывается в ячейку массива и выставляется флаг записи в файл
---Часть третья---
1) значения из всех ячеек массива записываются в одну строку
2) строка записывается в файл

То есть порядок такой: из файла в строку-->из строки в массив-->из массива в %MW-->из %MW в массив-->из массива в строку-->из строки в файл.
Заморочено? Да, но это сделано, что выполнять большинство операций в циклах и тем самым избежать индусского кода, а так же из-за особенностей работы файловых и строчных функций в UnitPro.


Теперь что делать тому, кто решится это использовать:
1) в filename задаём нужное имя файла
2) во второй части для, например, %MW100(Real) и %MW307(INT) пишем

Код: выделить все
if start_array_to_MW then
%MW100:=Value_MW[0];
%MW307:=REAL_TO_INT(Value_MW[1]);
end_if;

if start_comparison then
if Value_MW[0] <> %MW100 then Value_MW[0]:=  %MW100; need_to_rewrite:=1; end_if;
if Value_MW[1] <> INT_TO_REAL (%MW307) then Value_MW[1]:=  INT_TO_REAL (%MW307); need_to_rewrite:=1; end_if;
end_if;


Собственно всё. Нужной карты памяти, чтобы проверить работу на контроллере, у меня нет, но в эмуляторе всё работает как задумано.
Ссылка на файл для импорта программной секции https://yadi.sk/d/71NQPlOvjwZNG
Для просмотра созданного файла перейти по ссылке C:\Users\имя_пользователя\AppData\Local\Temp.
Fessar
 
Сообщений: 5
Зарегистрирован: 25 сен 2015, 09:36
Благодарил (а): 0 раз.
Поблагодарили: 0 раз.
Пункты репутации: 1

Re: Сохрание %MW в файл

Сообщение leon78 » 28 окт 2015, 09:17

Бит %S94=1 обновляет начальные значения, используемые для инициализации переменных с галочкой "save" при холодном старте, текущими значениями переменных.
Но как раз с M340 есть особенность, с которой надо разбираться:
For Modicon M340, on a %S94 rising edge, the internal RAM and the memory card content are different (%S96 = 0 and the CARDERR LED is on). On cold start, the current values are replaced by the most recent initial values only if a save to memory card function (Backup Save or %S66 rising edge) was done.
leon78
 
Сообщений: 20
Зарегистрирован: 06 июн 2012, 13:51
Благодарил (а): 0 раз.
Поблагодарили: 0 раз.
Пункты репутации: 0

Re: Сохрание %MW в файл

Сообщение Автоматчик » 31 июл 2019, 09:58

Добрый день! Было ли испытано на железе сохранение %MW в файл по выше описанной методике? Или может было побеждено сохранение %MW во флэше без остановки контроллера? (обсуждалось здесь http://forum.se-automation.in.ua/viewtopic.php?f=8&t=361&start=120#p2579 )
Автоматчик
 
Сообщений: 1
Зарегистрирован: 31 июл 2019, 09:52
Благодарил (а): 0 раз.
Поблагодарили: 0 раз.
Пункты репутации: 0

Re: Сохрание %MW в файл

Сообщение SaniOK » Вчера, 17:06

Приветствую!

Тут нужно понимать, для чего это нужно:
1. Если вам нужно сохранить все параметры после наладки, то это может выполнить человек с помочью Unity Pro/Control Expert (тот же функционал есть и в UnityLoader'е). Для этого в меню PLC выбираеться пункт "Save Data from PLC to file". Можно выбрать все данные или определённый масив который нужен. При желании, потом можно откатить данные с помощью записи данных из файла в ПЛК - PLC/Restore data from file to PLC
2. Если же нужно делать это автономно без присутствия человека, то это делаеться по принципу как писал leon78, но скоректирую: В переменных вносим значение в поле «Value» и активируем поле «Save». Во время работы есть возможность активировать системный бит %S94, что позволит контроллеру подменить все стандартные значения (те что в поле «Value») на текущие значения. При этом у нас будет различие программы во внутренней памяти и но флеш карте. Поэтому по факту переключения бита %S94 с 1 на 0 (система сама переключет по факту завершения операции) нужно активировать %S66 . Это позволит заменить программу на флеш накопителе той, что у нас в RAM.
Оба эти бита нельзя принудительно переводить в 0. Для деталей можно ознакомится в Help.

Дополню тем, что всё это происходит на лету без остановки контроллера!
SaniOK
 
Сообщений: 4
Зарегистрирован: 30 ноя 2017, 13:55
Благодарил (а): 0 раз.
Поблагодарили: 0 раз.
Пункты репутации: 0


Вернуться в Высокоуровневые системы автоматизации - M340, M580, Premium, Quantum и Unity

Кто сейчас на форуме

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 1