Fabrication industrielle
Internet des objets industriel | Matériaux industriels | Entretien et réparation d'équipement | Programmation industrielle |
home  MfgRobots >> Fabrication industrielle >  >> Manufacturing Technology >> Technologie industrielle

Peupler une classe C++ depuis XML avec IXMLSerializable dans PLCnext

Résumé

Saviez-vous que les PLCnext Common Classes intègrent nativement la sérialisation XML ? Cet article montre comment utiliser l'interface IXmlSerializable pour peupler les données d'une classe C++.

La description de l'interface se trouve dans la documentation API des PLCnext Common Classes.

Prérequis

Article rédigé avec :

PLCnext Firmware: 2020.6 LTS
PLCnext C++ SDK pour Linux 64 Bit 2020.6 LTS

Les données

Nous voulons peupler notre classe avec le fichier de configuration suivant :

<?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>

La notation $ARP_DATA_DIR$ est un placeholder pour variable d'environnement, ici ARP_DATA_DIR. Les variables d'environnement Arp définies se trouvent dans le fichier de paramètres du périphérique cible /etc/plcnext/Device.acf.settings.

Pour lire les données depuis un fichier XML, il faut implémenter l'interface IXMLSerializable pour notre classe. Pour simplifier, notre classe n'a que deux éléments : un nom DNS et un vecteur de chemins de fichiers.

#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;
  // Les données
public:
  Arp::String DnsName{""};
  std::vector<Arp::String> FileList;
  // Méthodes de support
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

Implémentation

Il faut implémenter les méthodes ReadXml et WriteXml.

WriteXml est simple : on ne veut pas écrire, seulement lire. ReadXml est appelée pour lire les données du fichier XML.

#include "MyConfiguration.hpp"
namespace MyComponent {
void MyConfiguration::WriteXml(Arp::System::Commons::Xml::XmlWriter& writer, Arp::System::Commons::Xml::XmlSerializationContext& context) {
  // pas d'opération
  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) {
  // Utiliser 'context.ResolvePath' pour remplacer les placeholders dans le chemin.
  auto file = Arp::String(context.ResolvePath(reader.GetAttributeValue<Arp::String>("path")));
  this->FileList.push_back(file);
  reader.ReadEndElement();
}
} // namespace MyComponent

Lire les données

On peut maintenant utiliser notre classe avec la classe XMLConfigDocument dans la méthode LoadConfig pour charger les données dans notre classe.

void MyComponent::LoadConfig() {
  // charger la config du projet ici
  using namespace Arp::System::Commons;
  this->log.Info("LoadConfig");
  // Premier argument doit correspondre au nom de l'élément racine XML.
  // Notre instance MyConfiguration this->config sera peuplée.
  Xml::XmlConfigDocument configDoc("MyConfigDocument", this->config);
  if (!Io::File::Exists(this->settingsPath)) {
    this->log.Error("Le fichier de configuration '{}' n'existe pas.", this->settingsPath);
    return;
  }
  try {
    configDoc.Load(this->settingsPath);
  } catch (const Arp::Exception& e) {
    this->log.Error(e.GetMessage());
    throw InvalidConfigException(e.GetMessage());
  }
}

Technologie industrielle

  1. Utiliser l'interface en ligne de commande avec SPICE
  2. Comment la Technologie Transforme la Fabrication : De l'Impression 3D aux Jumeaux Numériques
  3. Maîtriser Java BufferedReader : Gestion efficace des flux de caractères
  4. Maîtriser les opérations sur les fichiers Java avec java.io – Création, lecture, écriture et suppression
  5. Interface vs Classe Abstraite en Java : Comment Choisir la Bonne Abstraction
  6. Comprendre les interfaces Java : Définitions, Usages et Bonnes pratiques
  7. Choisir le bon moteur anti-explosion : guide complet
  8. Types de matrices (dies) en fabrication : Estampage, tréfilage, moulage
  9. Comment les points de test améliorent les tests PCB et l'efficacité de la production