Task 858: .ZRX File Format

Task 858: .ZRX File Format

1. Properties of the .ZRX File Format

The .ZRX file format corresponds to the ZRXP data exchange format, a line-oriented text-based format designed for time series data, developed by Kisters. It uses ISO-8859-1 encoding and supports the exchange of various information, including time series values and metadata. The format is backward compatible, with all segments in a file required to use the same ZRXP version. Comments are denoted by lines starting with "##", and empty lines are ignored. The structure consists of one or more segments, each comprising a basic data header (lines starting with "#") and a time series value block.

The intrinsic properties are defined through keyword-value pairs in the header and column layouts in the data block. These properties include:

  • SANR: Alphanumerical station number.
  • SNAME: Station name.
  • SWATER: River name.
  • CDASA: Remote call logger/meter number.
  • CDASANAME: DASA name.
  • CCHANNEL: DASA channel name.
  • CCHANNELNO: DASA channel number.
  • CMW: Values per day for equidistant time series.
  • CNAME: Parameter name.
  • CNR: Parameter number.
  • CUNIT: Unit of data value.
  • REXCHANGE: Import number of import agent.
  • RINVAL: Value for missing/invalid data (default: -777.0).
  • RTIMELVL: Time series time level.
  • XVLID: Internal time series ID.
  • TSPATH: Absolute path in KiTSM.
  • CTAG: Special tag.
  • CTAGKEY: Special tag key.
  • XTRUNCATE: Flag to remove existing data before import.
  • METCODE: Metering code (BDEW energy market).
  • METERNUMBER: Meter number (BDEW).
  • EDIS: EDIS/OBIS code (BDEW).
  • TZ: Time zone of timestamps.
  • ZDATE: Timestamp of meter reading.
  • ZRXPVERSION: ZRXP format version (mandatory).
  • ZRXPCREATOR: Name of creation tool.
  • LAYOUT: Defines column layout for the data block (mandatory).
  • TASKID: Internal task identifier.
  • SOURCESYSTEM: Designator of source system.
  • SOURCEID: Time series identifier by source.

Column properties defined via LAYOUT (case-insensitive aliases):

  • timestamp: Primary timestamp (format: yyyymmdd[hhmmss]).
  • value: Primary numeric value.
  • primary_status: Primary status code (integer 0–255).
  • system_status: System status (string).
  • additional_status: Additional status (string).
  • interpolation_type: Interpolation type (integer).
  • remark: Remarks (string).
  • timestampoccurrence: Occurrence timestamp.
  • occurrencecount: Reset number.
  • member: Ensemble member identifier.
  • forecast: Forecast timestamp.
  • signature: Signature code.
  • reset_number: Reset number.
  • reset_timestamp: Reset timestamp.
  • releaselevel: Release level.
  • dispatchinfo: Dispatch information.

3. Ghost Blog Embedded HTML JavaScript for Drag and Drop .ZRX File

Drop .ZRX file here

4. Python Class for .ZRX File

import os

class ZRXFile:
    def __init__(self, filepath):
        self.filepath = filepath
        self.header = {}
        self.columns = []
        self.data = []
        self.encoding = 'iso-8859-1'

    def read(self):
        with open(self.filepath, 'r', encoding=self.encoding) as f:
            content = f.readlines()
        in_header = True
        for line in content:
            line = line.strip()
            if line.startswith('##') or not line:
                continue
            if line.startswith('#'):
                parts = line[1:].split('|*|') or line[1:].split(';*;')
                for part in parts:
                    if '<' in part and '>' in part:
                        key, value = part.split('<', 1)[0], part.split('<', 1)[1].split('>')[0]
                        if key == 'LAYOUT':
                            self.columns = value.strip('()').split(',')
                        else:
                            self.header[key] = value
            else:
                if in_header:
                    in_header = False
                values = line.split()
                self.data.append(values)
        return self.header, self.columns, self.data

    def decode(self):
        # Decoding is parsing the text content as per read method
        return self.read()

    def write(self, new_filepath=None):
        if not new_filepath:
            new_filepath = self.filepath
        with open(new_filepath, 'w', encoding=self.encoding) as f:
            for key, value in self.header.items():
                f.write(f'#{key}<{value}>|*|\n')
            if self.columns:
                f.write(f'#LAYOUT({" ".join(self.columns)})\n')
            for row in self.data:
                f.write(' '.join(row) + '\n')

    def print_properties(self):
        print("Header Properties:")
        for key, value in self.header.items():
            print(f"{key}: {value}")
        print("\nColumns:")
        print(self.columns)
        print("\nData Sample (first 5 rows):")
        for row in self.data[:5]:
            print(row)

# Example usage:
# zrx = ZRXFile('example.zrx')
# zrx.read()
# zrx.print_properties()
# zrx.write('new.zrx')

5. Java Class for .ZRX File

import java.io.*;
import java.util.*;

public class ZRXFile {
    private String filepath;
    private Map<String, String> header = new HashMap<>();
    private List<String> columns = new ArrayList<>();
    private List<List<String>> data = new ArrayList<>();
    private String encoding = "ISO-8859-1";

    public ZRXFile(String filepath) {
        this.filepath = filepath;
    }

    public void read() throws IOException {
        try (BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(filepath), encoding))) {
            String line;
            boolean inHeader = true;
            while ((line = reader.readLine()) != null) {
                line = line.trim();
                if (line.startsWith("##") || line.isEmpty()) continue;
                if (line.startsWith("#")) {
                    String[] parts = line.substring(1).split("\\|\\*\\||;\\*;");
                    for (String part : parts) {
                        if (part.contains("<") && part.contains(">")) {
                            String key = part.substring(0, part.indexOf("<")).trim();
                            String value = part.substring(part.indexOf("<") + 1, part.indexOf(">")).trim();
                            if (key.equals("LAYOUT")) {
                                columns = Arrays.asList(value.replaceAll("[()]", "").split(","));
                            } else {
                                header.put(key, value);
                            }
                        }
                    }
                } else {
                    if (inHeader) inHeader = false;
                    String[] values = line.split("\\s+");
                    data.add(Arrays.asList(values));
                }
            }
        }
    }

    public void decode() throws IOException {
        // Decoding is equivalent to reading the text content
        read();
    }

    public void write(String newFilepath) throws IOException {
        if (newFilepath == null) newFilepath = filepath;
        try (PrintWriter writer = new PrintWriter(new OutputStreamWriter(new FileOutputStream(newFilepath), encoding))) {
            for (Map.Entry<String, String> entry : header.entrySet()) {
                writer.println("#" + entry.getKey() + "<" + entry.getValue() + ">|*|");
            }
            if (!columns.isEmpty()) {
                writer.println("#LAYOUT(" + String.join(",", columns) + ")");
            }
            for (List<String> row : data) {
                writer.println(String.join(" ", row));
            }
        }
    }

    public void printProperties() {
        System.out.println("Header Properties:");
        header.forEach((key, value) -> System.out.println(key + ": " + value));
        System.out.println("\nColumns:");
        System.out.println(columns);
        System.out.println("\nData Sample (first 5 rows):");
        for (int i = 0; i < Math.min(5, data.size()); i++) {
            System.out.println(data.get(i));
        }
    }

    // Example usage:
    // public static void main(String[] args) throws IOException {
    //     ZRXFile zrx = new ZRXFile("example.zrx");
    //     zrx.read();
    //     zrx.printProperties();
    //     zrx.write("new.zrx");
    // }
}

6. JavaScript Class for .ZRX File

class ZRXFile {
  constructor(filepath) {
    this.filepath = filepath;
    this.header = {};
    this.columns = [];
    this.data = [];
    this.encoding = 'ISO-8859-1'; // Note: JavaScript FileReader uses UTF-8 by default; use TextDecoder for ISO-8859-1 if needed
  }

  async read() {
    const fs = require('fs'); // For Node.js
    const content = fs.readFileSync(this.filepath, this.encoding);
    const lines = content.split('\n');
    let inHeader = true;
    lines.forEach((line) => {
      line = line.trim();
      if (line.startsWith('##') || !line) return;
      if (line.startsWith('#')) {
        const parts = line.slice(1).split(/\|\*\|/);
        parts.forEach((part) => {
          const match = part.match(/(\w+)<([^>]+)>/);
          if (match) {
            const key = match[1];
            const value = match[2];
            if (key === 'LAYOUT') {
              this.columns = value.replace(/[()]/g, '').split(',');
            } else {
              this.header[key] = value;
            }
          }
        });
      } else {
        if (inHeader) inHeader = false;
        const values = line.split(/\s+/).filter(Boolean);
        if (values.length > 0) this.data.push(values);
      }
    });
  }

  decode() {
    // Decoding is parsing as per read
    return this.read();
  }

  write(newFilepath = this.filepath) {
    const fs = require('fs'); // For Node.js
    let content = '';
    for (const [key, value] of Object.entries(this.header)) {
      content += `#${key}<${value}>|*|\n`;
    }
    if (this.columns.length > 0) {
      content += `#LAYOUT(${this.columns.join(',')})\n`;
    }
    this.data.forEach((row) => {
      content += `${row.join(' ')}\n`;
    });
    fs.writeFileSync(newFilepath, content, this.encoding);
  }

  printProperties() {
    console.log('Header Properties:');
    console.log(this.header);
    console.log('\nColumns:');
    console.log(this.columns);
    console.log('\nData Sample (first 5 rows):');
    console.log(this.data.slice(0, 5));
  }
}

// Example usage in Node.js:
// const zrx = new ZRXFile('example.zrx');
// await zrx.read();
// zrx.printProperties();
// zrx.write('new.zrx');

7. C Implementation for .ZRX File (Using Struct and Functions, as C Does Not Have Classes)

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

#define MAX_LINE 1024
#define MAX_KEYS 50
#define MAX_COLS 20
#define MAX_DATA_ROWS 1000 // Arbitrary limit for sample

typedef struct {
    char *filepath;
    char *encoding; // ISO-8859-1, but C file ops use default; assume ASCII compatible
    struct {
        char *key[MAX_KEYS];
        char *value[MAX_KEYS];
        int count;
    } header;
    char *columns[MAX_COLS];
    int col_count;
    char *data[MAX_DATA_ROWS][MAX_COLS];
    int data_rows;
} ZRXFile;

ZRXFile* zrx_create(const char *filepath) {
    ZRXFile *zrx = malloc(sizeof(ZRXFile));
    zrx->filepath = strdup(filepath);
    zrx->encoding = "ISO-8859-1"; // Note: C stdio doesn't handle encoding directly
    zrx->header.count = 0;
    zrx->col_count = 0;
    zrx->data_rows = 0;
    return zrx;
}

void zrx_destroy(ZRXFile *zrx) {
    for (int i = 0; i < zrx->header.count; i++) {
        free(zrx->header.key[i]);
        free(zrx->header.value[i]);
    }
    for (int i = 0; i < zrx->col_count; i++) {
        free(zrx->columns[i]);
    }
    for (int i = 0; i < zrx->data_rows; i++) {
        for (int j = 0; j < zrx->col_count; j++) {
            free(zrx->data[i][j]);
        }
    }
    free(zrx->filepath);
    free(zrx);
}

void zrx_read(ZRXFile *zrx) {
    FILE *file = fopen(zrx->filepath, "r");
    if (!file) return;
    char line[MAX_LINE];
    int in_header = 1;
    while (fgets(line, MAX_LINE, file)) {
        char *trimmed = line;
        while (isspace(*trimmed)) trimmed++;
        if (strncmp(trimmed, "##", 2) == 0 || *trimmed == '\0') continue;
        if (*trimmed == '#') {
            trimmed++;
            char *token = strtok(trimmed, "|*| ;*;");
            while (token) {
                char *open = strchr(token, '<');
                char *close = strchr(token, '>');
                if (open && close) {
                    *open = '\0';
                    *close = '\0';
                    char *key = strdup(token);
                    char *value = strdup(open + 1);
                    if (strcmp(key, "LAYOUT") == 0) {
                        char *col_token = strtok(value, "(), ");
                        while (col_token && zrx->col_count < MAX_COLS) {
                            zrx->columns[zrx->col_count++] = strdup(col_token);
                            col_token = strtok(NULL, "(), ");
                        }
                    } else {
                        zrx->header.key[zrx->header.count] = key;
                        zrx->header.value[zrx->header.count++] = value;
                    }
                }
                token = strtok(NULL, "|*| ;*;");
            }
        } else {
            if (in_header) in_header = 0;
            if (zrx->data_rows >= MAX_DATA_ROWS) continue;
            char *val_token = strtok(trimmed, " \t");
            int col = 0;
            while (val_token && col < zrx->col_count) {
                zrx->data[zrx->data_rows][col++] = strdup(val_token);
                val_token = strtok(NULL, " \t");
            }
            if (col > 0) zrx->data_rows++;
        }
    }
    fclose(file);
}

void zrx_decode(ZRXFile *zrx) {
    // Decoding is parsing as per read
    zrx_read(zrx);
}

void zrx_write(ZRXFile *zrx, const char *new_filepath) {
    const char *path = new_filepath ? new_filepath : zrx->filepath;
    FILE *file = fopen(path, "w");
    if (!file) return;
    for (int i = 0; i < zrx->header.count; i++) {
        fprintf(file, "#%s<%s>|*|\n", zrx->header.key[i], zrx->header.value[i]);
    }
    if (zrx->col_count > 0) {
        fprintf(file, "#LAYOUT(");
        for (int i = 0; i < zrx->col_count; i++) {
            fprintf(file, "%s%s", zrx->columns[i], i < zrx->col_count - 1 ? "," : "");
        }
        fprintf(file, ")\n");
    }
    for (int i = 0; i < zrx->data_rows; i++) {
        for (int j = 0; j < zrx->col_count; j++) {
            fprintf(file, "%s ", zrx->data[i][j]);
        }
        fprintf(file, "\n");
    }
    fclose(file);
}

void zrx_print_properties(ZRXFile *zrx) {
    printf("Header Properties:\n");
    for (int i = 0; i < zrx->header.count; i++) {
        printf("%s: %s\n", zrx->header.key[i], zrx->header.value[i]);
    }
    printf("\nColumns:\n");
    for (int i = 0; i < zrx->col_count; i++) {
        printf("%s ", zrx->columns[i]);
    }
    printf("\n\nData Sample (first 5 rows):\n");
    for (int i = 0; i < 5 && i < zrx->data_rows; i++) {
        for (int j = 0; j < zrx->col_count; j++) {
            printf("%s ", zrx->data[i][j]);
        }
        printf("\n");
    }
}

// Example usage:
// int main() {
//     ZRXFile *zrx = zrx_create("example.zrx");
//     zrx_read(zrx);
//     zrx_print_properties(zrx);
//     zrx_write(zrx, "new.zrx");
//     zrx_destroy(zrx);
//     return 0;
// }