Home Manual Reference Source Test

src/transform/Mapping.js

  1. import { basename } from 'path';
  2. import assert from 'assert';
  3. import { VariantArrayType, DataType } from 'node-opcua/lib/datamodel/variant';
  4. import Transformer from '../lib/transform/Transformer';
  5.  
  6. /**
  7. * Atvise specific types that need special extensions.
  8. * @type {Map<string, Object>}
  9. */
  10. const standardTypes = {
  11. 'VariableTypes.ATVISE.HtmlHelp': {
  12. extension: '.help.html',
  13. dataType: DataType.ByteString,
  14. },
  15. 'VariableTypes.ATVISE.TranslationTable': {
  16. extension: '.locs.xml',
  17. dataType: DataType.XmlElement,
  18. },
  19. };
  20.  
  21. /**
  22. * Extensions to use for {@link node-opcua~DataType}s.
  23. * @type {Map<string, string>}
  24. */
  25. const extensionForDataType = {
  26. [DataType.Boolean.key]: '.bool',
  27. [DataType.SByte.key]: '.sbyte',
  28. [DataType.Byte.key]: '.byte',
  29. [DataType.Int16.key]: '.int16',
  30. [DataType.UInt16.key]: '.uint16',
  31. [DataType.Int32.key]: '.int32',
  32. [DataType.UInt32.key]: '.uint32',
  33. [DataType.Int64.key]: '.int64',
  34. [DataType.UInt64.key]: '.uint64',
  35. [DataType.Float.key]: '.float',
  36. [DataType.Double.key]: '.double',
  37. [DataType.String.key]: '.string',
  38. [DataType.DateTime.key]: '.datetime',
  39. [DataType.Guid.key]: '.guid',
  40. // [DataType.ByteString.key]: '.bytestring',
  41. [DataType.XmlElement.key]: '.xml',
  42. [DataType.NodeId.key]: '.nodeid',
  43. [DataType.ExpandedNodeId.key]: '.enodeid',
  44. [DataType.StatusCode.key]: '.status',
  45. [DataType.QualifiedName.key]: '.name',
  46. [DataType.LocalizedText.key]: '.text',
  47. [DataType.ExtensionObject.key]: '.obj',
  48. [DataType.DataValue.key]: '.value',
  49. [DataType.Variant.key]: '.variant',
  50. [DataType.DiagnosticInfo.key]: '.info',
  51. };
  52.  
  53. /**
  54. * Extensions to use for {@link node-opcua~VariantArrayType}s.
  55. * @type {Map<string, string>}
  56. */
  57. const extensionForArrayType = {
  58. [VariantArrayType.Array.key]: '.array',
  59. [VariantArrayType.Matrix.key]: '.matrix',
  60. };
  61.  
  62. /**
  63. * A Transformer that maps {@link ReadStream.ReadResult}s to {@link AtviseFile}s.
  64. */
  65. export default class MappingTransformer extends Transformer {
  66. /**
  67. * Creates a new mapping transformer.
  68. * @param {Object} [options] The arguments passed to the {@link Transformer} constructor.
  69. */
  70. constructor(options = {}) {
  71. super(options);
  72.  
  73. /**
  74. * Contents of the reference files read but not used yet.
  75. * @type {Object}
  76. */
  77. this._readReferenceFiles = {};
  78. }
  79.  
  80. /**
  81. * Writes an {@link AtviseFile} for each {@link ReadStream.ReadResult} read. If a read file has a
  82. * non-standard type (definition) an additional `rc` file is pushed holding this type.
  83. * @param {Node} node The read result to create the file for.
  84. * @param {string} encoding The encoding used.
  85. * @param {function(err: ?Error, data: ?AtviseFile)} callback Called with the error that occurred
  86. * while transforming the read result or the resulting file.
  87. */
  88. transformFromDB(node, encoding, callback) {
  89. if (!node.fullyMapped && !node.parentResolvesMetadata) {
  90. // Skip mapping for e.g. split files
  91. const typeDefinition = node.typeDefinition;
  92. let isStandardTypeNode = false;
  93.  
  94. // Add extensions for standard types
  95. for (const [def, { extension }] of Object.entries(standardTypes)) {
  96. if (node.isVariable && typeDefinition === def) {
  97. node.renameTo(`${node.name}${extension}`);
  98. isStandardTypeNode = true;
  99.  
  100. // FIXME: Set dataType and mark as resolved
  101. // FIXME: Set typeDefinition and mark as resolved
  102. } else if (node.fileName.endsWith(extension)) {
  103. callback(new Error(`Name conflict: ${node.nodeId} should not end with '${extension}'`));
  104. return;
  105. }
  106. }
  107.  
  108. // Add extensions for data types
  109. for (const [type, ext] of Object.entries(extensionForDataType)) {
  110. if (node.isVariable && node.value && node.value.dataType.key === type) {
  111. if (!isStandardTypeNode) {
  112. node.renameTo(`${node.name}${ext}`);
  113. break;
  114. }
  115.  
  116. // FIXME: Set dataType and mark as resolved
  117. }
  118. }
  119.  
  120. // Add extensions for array types
  121. for (const [type, ext] of Object.entries(extensionForArrayType)) {
  122. if (node.isVariable && node.value.arrayType.key === type) {
  123. if (!isStandardTypeNode) {
  124. node.renameTo(`${node.name}${ext}`);
  125. }
  126.  
  127. // FIXME: Set arrayType and mark as resolved
  128. } else if (node.fileName.endsWith(ext)) {
  129. callback(new Error(`Name conflict: ${node.nodeId} should not end with '${ext}'`));
  130. return;
  131. }
  132. }
  133. }
  134.  
  135. // Compact mapping: Root source folders are AGENT, SYSTEM, ObjectTypes and VariableTypes
  136. // FIXME: Make optional
  137. const ignore = new Set([
  138. 58, // Objects -> Types -> BaseObjectType
  139. 62, // Objects -> Types -> BaseVariableType
  140. 85, // Objects
  141. 86, // Objects -> Types
  142. ]);
  143.  
  144. for (let c = node; c && c.parent && !c._compactMappingApplied; c = c.parent) {
  145. if (ignore.has(c.parent.id.value)) {
  146. c.parent = c.parent.parent;
  147. c = node;
  148. }
  149. }
  150.  
  151. Object.assign(node, {
  152. _compactMappingApplied: true,
  153. });
  154.  
  155. callback(null, node);
  156. }
  157.  
  158. /**
  159. * Writes an {@link AtviseFile} for each {@link Node} read.
  160. * @param {Node} node The raw file.
  161. * @param {string} encoding The encoding used.
  162. * @param {function(err: ?Error, data: ?AtviseFile)} callback Called with the error that occurred
  163. * while transforming the read result or the resulting file.
  164. */
  165. transformFromFilesystem(node, encoding, callback) {
  166. let isStandardTypeNode = false;
  167.  
  168. // Resolve standard type from extension
  169. for (const [, { extension }] of Object.entries(standardTypes)) {
  170. if (node.name.endsWith(extension)) {
  171. isStandardTypeNode = true;
  172.  
  173. // FIXME: Set dataType and mark as resolved
  174. // FIXME: Set typeDefinition and mark as resolved
  175.  
  176. node.renameTo(basename(node.name, extension));
  177. }
  178. }
  179.  
  180. // Resolve arrayType from extension
  181. for (const [type, extension] of Object.entries(extensionForArrayType)) {
  182. if (node.name.endsWith(extension) && !isStandardTypeNode) {
  183. assert.equal(node.arrayType.key, type);
  184.  
  185. // FIXME: Set arrayType and mark as resolved
  186.  
  187. node.renameTo(basename(node.name, extension));
  188. break;
  189. }
  190. }
  191.  
  192. // Resolve dataType from extension
  193. for (const [type, extension] of Object.entries(extensionForDataType)) {
  194. if (node.name.endsWith(extension) && !isStandardTypeNode && node.dataType.key === type) {
  195. // FIXME: Set dataType and mark as resolved
  196.  
  197. node.renameTo(basename(node.name, extension));
  198. break;
  199. }
  200. }
  201.  
  202. return callback(null, node);
  203. }
  204.  
  205. /**
  206. * `true` as the mapping transformer should infer references from config files.
  207. */
  208. get transformsReferenceConfigFiles() {
  209. return true;
  210. }
  211. }