Popular Classe C++ a partir de XML Usando IXMLSerializable no PLCnext
Resumo
Você sabia que as PLCnext Common Classes têm suporte nativo para serialização XML? Este artigo mostra como usar a interface IXmlSerializable para popular dados em uma classe C++.
Encontre a descrição da Interface na documentação da API das PLCnext Common Classes.
Requisitos
Este artigo foi escrito com:
PLCnext Firmware: 2020.6 LTS
PLCnext C++ SDK para Linux 64 Bit 2020.6 LTS
Os Dados
Queremos popular nossa classe com o seguinte arquivo de configuração:
<?xml version="1.0" encoding="UTF-8"?>
<MyConfigDocument schemaVersion="1.0">
<Server dnsName="server.domain.tld" />
<FileList>
<File path="$ARP_DATA_DIR$/Services/MyComponent/file1.txt" />
<File path="$ARP_DATA_DIR$/Services/MyComponent/file2.txt" />
</FileList>
</MyConfigDocument>
A notação $ARP_DATA_DIR$ é placeholder para variável de ambiente, neste caso ARP_DATA_DIR. Encontre as variáveis de ambiente Arp definidas no arquivo de configuração do dispositivo no target /etc/plcnext/Device.acf.settings.
Para ler dados de arquivo XML, devemos implementar interface IXMLSerializable para nossa classe. Para simplificar, nossa classe tem apenas dois elementos: nome DNS e vetor de caminhos de arquivo.
#pragma once
#include "Arp/System/Core/Arp.h"
#include "Arp/System/Commons/Xml/IXmlSerializable.hpp"
#include <vector>
namespace MyComponent {
class MyConfiguration : public Arp::System::Commons::Xml::IXmlSerializable {
public:
MyConfiguration() = default;
~MyConfiguration() = default;
// Interface IXMLSerializable
public:
void ReadXml(Arp::System::Commons::Xml::XmlReader& reader,
Arp::System::Commons::Xml::XmlSerializationContext& context) override;
void WriteXml(Arp::System::Commons::Xml::XmlWriter& writer,
Arp::System::Commons::Xml::XmlSerializationContext& context) override;
// Os dados
public:
Arp::String DnsName{""};
std::vector<Arp::String> FileList;
// Métodos auxiliares
private:
void readFileList(Arp::System::Commons::Xml::XmlReader& reader,
Arp::System::Commons::Xml::XmlSerializationContext& context);
void readFile(Arp::System::Commons::Xml::XmlReader& reader,
Arp::System::Commons::Xml::XmlSerializationContext& context);
};
} // namespace MyComponent
Implementação
Devemos implementar os métodos ReadXml e WriteXml.
O método WriteXml é direto, não queremos escrever, apenas ler dados do XML. O método ReadXml é chamado quando queremos ler dados do XML.
#include "MyConfiguration.hpp"
namespace MyComponent {
void MyConfiguration::WriteXml(Arp::System::Commons::Xml::XmlWriter& writer,
Arp::System::Commons::Xml::XmlSerializationContext& context) {
// sem operação
return;
}
void MyConfiguration::ReadXml(Arp::System::Commons::Xml::XmlReader& reader,
Arp::System::Commons::Xml::XmlSerializationContext& context) {
Arp::String elementName;
while (reader.TryReadStartElement(elementName)) {
if (elementName == Arp::System::Commons::Xml::XmlSerializationContext::IncludesXmlName) {
context.ReadIncludesElement(reader);
} else if (elementName == "Server") {
this->DnsName = reader.GetAttributeValue<Arp::String>("dnsName");
reader.ReadEndElement();
} else if (elementName == "FileList") {
this->readFileList(reader, context);
} else {
context.InvalidXmlElementOccurs(reader, elementName);
reader.ReadEndElement();
}
}
}
void MyConfiguration::readFileList(Arp::System::Commons::Xml::XmlReader& reader,
Arp::System::Commons::Xml::XmlSerializationContext& context) {
if (reader.IsEmptyElement()){
return;
}
if (reader.ReadToDescendant("File")) {
this->readFile(reader, context);
while (reader.ReadToNextSibling("File")) {
this->readFile(reader, context);
}
} else {
reader.ReadEndElement();
}
}
void MyConfiguration::readFile(Arp::System::Commons::Xml::XmlReader& reader,
Arp::System::Commons::Xml::XmlSerializationContext& context) {
// Use 'context.ResolvePath' para substituir placeholders no path
auto file = Arp::String(context.ResolvePath(reader.GetAttributeValue<Arp::String>("path")));
this->FileList.push_back(file);
reader.ReadEndElement();
}
} // namespace MyComponent
Ler os dados
Agora podemos usar nossa classe com a classe XMLConfigDocument no método LoadConfig para carregar dados na classe.
void MyComponent::LoadConfig() {
// carregar config do projeto aqui
using namespace Arp::System::Commons;
this->log.Info("LoadConfig");
// Primeiro argumento deve corresponder ao nome do elemento raiz XML.
// Nossa instância MyConfiguration this->config será populada.
Xml::XmlConfigDocument configDoc("MyConfigDocument", this->config);
if (!Io::File::Exists(this->settingsPath)) {
this->log.Error("Arquivo de configuração '{}' não existe.", this->settingsPath);
return;
}
try {
configDoc.Load(this->settingsPath);
} catch (const Arp::Exception& e) {
this->log.Error(e.GetMessage());
throw InvalidConfigException(e.GetMessage());
}
}
Tecnologia industrial
- Usando a Interface de Linha de Comando com SPICE
- Como Tecnologia Está Transformando Manufatura: Da Impressão 3D a Gêmeos Digitais
- Dominando Java BufferedReader: Manipulação Eficiente de Streams de Caracteres
- Dominando Operações de Arquivo em Java com java.io – Criação, Leitura, Escrita e Exclusão
- Interface vs Classe Abstrata em Java: Como Escolher a Abstração Certa
- Entendendo Interfaces em Java: Definições, Usos e Boas Práticas
- Escolhendo o Motor à Prova de Explosão Certo: Guia Abrangente
- Tipos de Matrizes na Manufatura: Estampagem, Trefilação e Fundição Explicados
- Como Pontos de Teste Melhoram Testes de PCB e Eficiência de Produção
- Como a Automação Está Transformando a Manufatura: Aumentando Eficiência e Cortando Custos