July 2, 2020

Как загружаются параметры конфигурации

Как на самом деле запускается текущая конфигурация TwinCAT 3?

Все ниже перечисленные действия можно и нужно делать через официальный Automation Interface, а пока соберем пачку файлов из загрузочного каталога C:\TwinCAT\3.1\Boot\ и сравним их между собой с помощью бесплатной WinMerge.



Нам нужно больше файлов. Будем последовательно вносить изменения в текущую конфигурацию, активировать ее, а затем копировать из каталога Boot только интересные файлы.


... и сравнивать их между собой. Может быть и не так много файлов, как я собрал.


CurrentConfig.tszip


Это обычный zip-архив. Хранит копию проекта .tsproj для текущей конфигурации. Например, TwinCAT Project1.tsproj. Здесь хранятся значения параметров предусмотренные конфигурацией изначально. На самом деле при старте системы загружаются не они, а значения, хранящиеся в файле CurrentConfig.xml.


CurrentConfig.xml


Файл состоит из ряда xml-секций. Часть секций содержит параметры текущей конфигурации:
  • TcBootProject — корень. Содержит дату активации конфигурации CreateTime.
  • System — системные настройки: сколько выделить памяти, за каким ядром закрепить, размер стека, ...
  • Drivers — какие подсистемы TwinCAT 3 загрузить при старте: IO, NC, RTime, ... Аналогично тому, что я писал про загрузку системного модуля NC PTP.
  • InitCmds — команды при старте системы
  • PostCmds — команды после старта системы
  • ProjectInfo — описание текущего проекта конфигурации: уникальный идентификатор {GUID}, путь к файлу проекта, типы данных, ...


Offline → Online


Для примера я буду использовать параметры энкодера NC-оси. Это удобно, так как я только что изменял параметры NC оси официальным путем через функции типа MC_WriteParameter, но у меня остался открытым вопрос, как работает MC_WriteParameterPersistent.

После ряда экспериментов в текстовом редакторе, выяснилось, что содержимое .tsproj файла проекта содержит параметры по умолчанию. Эти параметры можно ассоциировать с графой Offline Value в таблицах параметров энкодера. Если изменить эти параметры в файле проекта, а затем перезапустить TwinCAT (Run → Config → Run), то текущее значение параметров в графе Online Value не изменится. Получается, что эти изменения никак не влияют на параметры работающего в данный момент проекта.

После небольшой проверки стало понятно, что при активации проекта эти данные транслируются и сохраняются в CurrentConfig.xml. Параметры этого xml-файла как раз и являются Online данными. Если их изменить, а затем перезапустить TwinCAT (Run → Config → Run), то мы применим новые значения к работающему проекту. Именно эти параметры применяются при загрузке текущего проекта и старте TwinCAT 3. Возможно, что TwinCAT 2 ведет себя аналогично.


InitCmds


Эта xml-секция содержит список команд или операций отправляющих значения параметров в сервис или устройство. Например, для NC один из множества параметров - это настройки энкодера. Параметры записываются пачками, в виде большого (или не очень) бинарного пакета.
  • key — описание операции.
  • port — ADS-порт устройства или сервиса.
  • iGrp — индекс группы параметра.
  • iOffs — индекс смещения параметра.
  • data — бинарный пакет байтов, содержащий данные для параметров. В шестнадцатеричных кодах, то есть два символа на байт. Данные пишутся в память, поэтому какие-либо границы параметров не указываются. Бинарный массив просто накладывается на пул адресов.
  • message — сообщение для лога TwinCAT.


Внедряемся в конфиг


Сравнив файлы, я нашел, что для задания параметров энкодера необходимо обращаться к ADS-сервису Port: 500; iGrp: 5124; iOffs: 0. ADS порт 500 отвечает за системный сервис NC (см. AmsPort Enumeration). Секция data начинается с номера оси `01`, затем идут какие-то неинтересные в данный момент данные, и с позиции 96 длинною в 16 символов идет значение Scaling Factor Numerator. Шестнадцать символов — это hex-представление 8 байт данных, что хорошо укладывается в тип данных LREAL/double.

Теперь задача заключается в следующем: найти секцию InitCmd у которой есть потомки с port = 500, iGrp = 5124 и ключ data начинающийся символами `01`. Затем преобразовать новое значение параметра в hex-вид и перезаписать его значение в ключ data. По окончании сохранить XML, перезапустить TwinCAT и наслаждаться результатом изменившегося параметра в графе Online Value.



Код можно смотреть и редактировать на гитхабе:

using System;
using System.Linq;
using System.Xml.Linq;
namespace Tc3_CurrentConfig
{
public class CurrentConfig
{
const string CURRENTCONFIG_PATH = @"C:\TwinCAT\3.1\Boot\CurrentConfig.xml";
/// <summary>
/// Writes Scaling Factor Numerator
/// </summary>
/// <param name="newValue">New value of Scaling Factor Numerator</param>
/// <param name="axisNo">NC axis number. Count begins from 1. 1 is a first axis.</param>
/// <returns>Current value of Scaling Factor Numerator</returns>
public static double WriteScalingFactorNumerator(double newValue, int axisNo)
{
if (axisNo < 1 || axisNo > 255)
throw new ArgumentOutOfRangeException( "axisNo", axisNo,
"1 >= axisNo <= 255. Count begins from 1. 1 is a first axis.");
const int HEX_START_INDEX = 96;
const int HEX_VALUE_LENGTH = 16; // double = 8 bytes length
string hexAxisNo = axisNo.ToString("X2");
/// Loading ///////////////////////////////////////////////////////
XDocument doc = XDocument.Load(CURRENTCONFIG_PATH);
var xEncAxis1 = doc.Descendants()
.Single(e =>
e.Name.LocalName == "InitCmd"
&& e.Elements().Where(
ee => (ee.Name.LocalName == "port" && ee.Value == "500" )
|| (ee.Name.LocalName == "iGrp" && ee.Value == "5124")
|| (ee.Name.LocalName == "iOffs" && ee.Value == "0" )
|| (ee.Name.LocalName == "data" && ee.Value.StartsWith(hexAxisNo)) )
.Count() == 4 );
var xData = xEncAxis1.Elements().Single(e => e.Name.LocalName == "data");
string data = xData.Value;
/// Current Value /////////////////////////////////////////////////
string hexValue = data.Substring(HEX_START_INDEX, HEX_VALUE_LENGTH);
double currentDoubleValue = BitConverter.ToDouble(HexToByteArray(hexValue));
/// Write New Value ///////////////////////////////////////////////
hexValue = BitConverter.ToString(BitConverter.GetBytes(newValue))
.Replace("-","") // BitConverter returns like this AA-BB-CC-...
.ToLower(); // Tc3 writes hex in lower case
xData.Value = data.Substring(0, HEX_START_INDEX) + hexValue + data.Substring(HEX_START_INDEX + HEX_VALUE_LENGTH);
doc.Save(CURRENTCONFIG_PATH);
return currentDoubleValue;
}
// inefficient but fun, see: https://stackoverflow.com/a/321404
public static byte[] HexToByteArray(string hexValue)
{
return Enumerable.Range(0, hexValue.Length / 2)
.Select(x => Convert.ToByte(hexValue.Substring(x * 2, 2), 16))
.ToArray();
}
}
}


MC_WriteParameterPersistent


Используя тестовую программу из статьи про параметры NC осей, я параллельно сохранял файлы из загрузочного каталога Boot. Так я смог быстро отследить, что именно происходит и каким-таким образом параметры переживают перезагрузку.

И снова CurrentConfig.xml

<InitCmd runtimeadded="true">
<key>Init34\NC: ChangeParameter 400100010023</key>
<port>500</port>
<iGrp>16385</iGrp>
<iOffs>65571</iOffs>
<data>011764cbf275493f</data>
<message>Initialization of boot parameter failed (MC_WriteParameterPersistent)</message>
</InitCmd>

Система создает специальную запись в конце файла CurrentConfig.xml. Посмотрите, как она отметила эту запись специальным атрибутом runtimeadded="true". Теперь понятно, как такие параметры переживают перезагрузку.
 
Номер порта 500 остался без изменения, а вот группа-смещение изменились, да и содержимое секции data похудело, выродившись в конкретное значение параметра (16 символов → 8 байт → LREAL/double тип).

Если обратиться к документации TwinCAT 3 ADS Interface NC в инфосисе, обнаружится, что наконец-то используются официально задокументированные штуки. Смотреть в п.2.4.1 "Index offset" specification for axis parameter (Index group 0x4000 + AxisID).

iGrp:  16385 = 0x00004001 (0x4000 + AxisID)
iOffs: 65571 = 0x00010023 (0x00n10023. Component of the scaling factor: numerator)

Всё, секрет раскрыт.

No comments

Post a Comment

Note: Only a member of this blog may post a comment.