This is an automated email from the ASF dual-hosted git repository.
cgarcia pushed a commit to branch feature/merlot
in repository https://gitbox.apache.org/repos/asf/plc4x-extras.git
The following commit(s) were added to refs/heads/feature/merlot by this push:
new a3dd723 Conversion of VType to EPICSevent types for serialization.
a3dd723 is described below
commit a3dd72324183509e0873173a390528ff82034609
Author: César García <[email protected]>
AuthorDate: Mon Apr 6 11:26:48 2026 -0400
Conversion of VType to EPICSevent types for serialization.
---
.../org.apache.plc4x.merlot.archiver/pom.xml | 38 ++++-
.../random_data.pbraw | Bin 0 -> 252 bytes
.../plc4x/merlot/archiver/api/MerlotHtc.java | 34 ++--
.../merlot/archiver/command/MerlotHtcCommand.java | 96 ++++++++---
.../archiver/core/MerlotPBRawSerializer.java | 117 +++++++++++++
.../merlot/archiver/core/MerlotPayloadMapping.java | 188 +++++++++++++++++++++
.../merlot/archiver/core/MerlotTypeMapping.java | 77 +++++++++
.../impl/MerlotDataBrowserRTGetDataPVImpl.java | 25 ++-
.../merlot/archiver/impl/MerlotHtcIoTDBImpl.java | 2 +-
.../merlot/archiver/impl/MerlotHtcRTImpl.java | 93 +++++-----
.../archiver/test/MerlotPayloadMappingTest.java | 94 +++++++++++
.../archiver/test/MerlotTypeMappingTest.java | 123 ++++++++++++++
.../merlot/archiver/test/PBRawFileSerializer.java | 135 +++++++++++++++
.../merlot/org.apache.plc4x.merlot.das.api/pom.xml | 5 +
14 files changed, 938 insertions(+), 89 deletions(-)
diff --git a/plc4j/tools/merlot/org.apache.plc4x.merlot.archiver/pom.xml
b/plc4j/tools/merlot/org.apache.plc4x.merlot.archiver/pom.xml
index fc27612..6755351 100644
--- a/plc4j/tools/merlot/org.apache.plc4x.merlot.archiver/pom.xml
+++ b/plc4j/tools/merlot/org.apache.plc4x.merlot.archiver/pom.xml
@@ -104,7 +104,14 @@
</execution>
</executions>
</plugin>
-
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>exec-maven-plugin</artifactId>
+ <version>3.5.1</version>
+ <configuration>
+
<mainClass>org.apache.plc4x.merlot.archiver.test.NewMain</mainClass>
+ </configuration>
+ </plugin>
</plugins>
</build>
<dependencies>
@@ -206,5 +213,34 @@
<artifactId>org.apache.plc4x.merlot.api</artifactId>
<version>0.13.0-SNAPSHOT</version>
</dependency>
+ <dependency>
+ <groupId>com.google.protobuf</groupId>
+ <artifactId>protobuf-java</artifactId>
+ <version>4.34.1</version>
+ </dependency>
+ <dependency>
+ <groupId>org.junit.jupiter</groupId>
+ <artifactId>junit-jupiter-api</artifactId>
+ <version>5.10.3</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.junit.jupiter</groupId>
+ <artifactId>junit-jupiter-params</artifactId>
+ <version>5.10.3</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.junit.jupiter</groupId>
+ <artifactId>junit-jupiter-engine</artifactId>
+ <version>5.10.3</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.opentest4j</groupId>
+ <artifactId>opentest4j</artifactId>
+ <version>1.3.0</version>
+ <scope>test</scope>
+ </dependency>
</dependencies>
</project>
diff --git
a/plc4j/tools/merlot/org.apache.plc4x.merlot.archiver/random_data.pbraw
b/plc4j/tools/merlot/org.apache.plc4x.merlot.archiver/random_data.pbraw
new file mode 100644
index 0000000..294b85d
Binary files /dev/null and
b/plc4j/tools/merlot/org.apache.plc4x.merlot.archiver/random_data.pbraw differ
diff --git
a/plc4j/tools/merlot/org.apache.plc4x.merlot.archiver/src/main/java/org/apache/plc4x/merlot/archiver/api/MerlotHtc.java
b/plc4j/tools/merlot/org.apache.plc4x.merlot.archiver/src/main/java/org/apache/plc4x/merlot/archiver/api/MerlotHtc.java
index 66411ae..3c52460 100644
---
a/plc4j/tools/merlot/org.apache.plc4x.merlot.archiver/src/main/java/org/apache/plc4x/merlot/archiver/api/MerlotHtc.java
+++
b/plc4j/tools/merlot/org.apache.plc4x.merlot.archiver/src/main/java/org/apache/plc4x/merlot/archiver/api/MerlotHtc.java
@@ -27,42 +27,52 @@ import org.epics.vtype.VType;
public interface MerlotHtc {
/*
- *
+ * Bundle Initialization
*/
public void init();
/*
- *
+ * Bundle Destruction
*/
public void destroy();
/*
- *
+ * Identify the Bundle's internal ID, required for remote commands
+ * and functions.
*/
public String getID();
/*
- *
+ * Add a tag to the historian. Only tags using the PVA protocol
+ * are supported.
+ * @param strPV
+ * @param maxRate
*/
- void addPV(String strPV, Double interval);
+ void addPV(String strPV, Double maxRate);
/*
- *
+ * Removes a tag from the historian. Intended for use only from
+ * the command line.
+ * @param strPV
*/
void removePV(String strPV);
-
-
-
/*
- *
+ * A set of tags currently being processed by the historian.
*/
Set<String> getPVs();
/*
- *
+ * It returns the values stored in the historian as pairs of
+ * LocalDateTime and VType (value) pairs, for subsequent processing
+ * according to the format required by the client application—for example,
+ * PBRAW, JSON, or XML.
+ * @param strPV
+ * @param from
+ * @param to
+ * @return A List of Pair
*/
- List<Pair<LocalDateTime, VType>> getPVs(String strPV, String init, String
end);
+ List<VType> getPVs(String strPV, String init, String end);
}
diff --git
a/plc4j/tools/merlot/org.apache.plc4x.merlot.archiver/src/main/java/org/apache/plc4x/merlot/archiver/command/MerlotHtcCommand.java
b/plc4j/tools/merlot/org.apache.plc4x.merlot.archiver/src/main/java/org/apache/plc4x/merlot/archiver/command/MerlotHtcCommand.java
index 5b26b97..37b529f 100644
---
a/plc4j/tools/merlot/org.apache.plc4x.merlot.archiver/src/main/java/org/apache/plc4x/merlot/archiver/command/MerlotHtcCommand.java
+++
b/plc4j/tools/merlot/org.apache.plc4x.merlot.archiver/src/main/java/org/apache/plc4x/merlot/archiver/command/MerlotHtcCommand.java
@@ -17,11 +17,13 @@
package org.apache.plc4x.merlot.archiver.command;
+import java.time.Instant;
import java.time.LocalDateTime;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import org.apache.commons.lang3.tuple.Pair;
import org.apache.karaf.shell.api.action.Action;
+import org.apache.karaf.shell.api.action.Argument;
import org.apache.karaf.shell.api.action.Command;
import org.apache.karaf.shell.api.action.Option;
import org.apache.karaf.shell.api.action.lifecycle.Reference;
@@ -45,19 +47,47 @@ public class MerlotHtcCommand implements Action {
String strHtc;
@Option(name = "-p", aliases = "--pv", description = "Displays the stored
values of the process variable.", required = false, multiValued = false)
- String strPV;
+ String strPV;
+
+ @Option(name = "-a", aliases = "--add", description = "The designated PV
tags must be incorporated into the collector.", required = false, multiValued =
false)
+ Boolean blnAdd;
+
+ @Option(name = "-f", aliases = "--from", description = "Start date for
data query.", required = false, multiValued = false)
+ String from;
+
+ @Option(name = "-t", aliases = "--to", description = "End date for data
consultation.", required = false, multiValued = false)
+ String to;
+
+ @Option(name = "-r", aliases = "--remove", description = "The designated
PV tags must be remove from the collector.", required = false, multiValued =
false)
+ String strRemovePV;
+
+
+ @Argument(index = 0, name = "htc", description = "The Collector to which
the PV tag will be assigned.", required = false, multiValued = false)
+ String strMainHtc;
+
+ @Argument(index = 1, name = "rate", description = "Maximum rate at which a
tag change must be acquired.", required = false, multiValued = false)
+ String strMaxRate;
+
+ @Argument(index = 2, name = "pvs", description = "List of PVS tags to
htc.", required = false, multiValued = true)
+ List<String> strPVs;
+
+
@Override
public Object execute() throws Exception {
- if (null == strHtc){
+
+ if ((null == strHtc) && (null == strMainHtc) ){
ListCollectorsServices(null);
- } else {
- if (null == strPV) {
+ } else if ((null != strHtc) && (null == strMainHtc) && (null ==
strPV)) {
ListCollectorsServices(strHtc);
- } else {
- ListHistoricalValues(strPV, null, null);
- }
- }
+ } else if ((null != strHtc) && (null == strMainHtc) && (null !=
strPV)){
+ ListHistoricalValues(strPV, from, to);
+ } else if ((blnAdd) && (null != strMainHtc) && (null != strMaxRate)
&& (null != strPVs)){
+ addPV(strMainHtc, strMaxRate, strPVs);
+ } else if ((null != strRemovePV) && (null != strMainHtc)) {
+ removePV(strMainHtc, strRemovePV);
+ }
+
return null;
}
@@ -73,7 +103,7 @@ public class MerlotHtcCommand implements Action {
} else {
htcs.forEach(h -> {
- if(h.getID().equalsIgnoreCase(strHtc)){
+ if(h.getID().equalsIgnoreCase(id)){
int count = 1;
ShellTable table = new ShellTable();
table.column("Item");
@@ -88,22 +118,44 @@ public class MerlotHtcCommand implements Action {
}
}
- private void ListHistoricalValues(String pv, String ini, String end){
+ private void ListHistoricalValues(String pv, String init, String end){
+ int[] counter = new int[1];
+ htcs.forEach(h -> {
+ if(h.getID().equalsIgnoreCase(strHtc)){
+ String strInit = (null == init)?Instant.MIN.toString():init;
+ String strEnd = (null == end)?Instant.MAX.toString():end;
+
+ var pvs = h.getPVs(pv, strInit, strEnd);
+ ShellTable table = new ShellTable();
+ table.column("Date");
+ table.column("Value");
+ counter[0] = 1;
+ pvs.forEach((p) ->{
+ table.addRow().addContent(counter[0], p.toString());
+ counter[0]++;
+ });
+ table.print(System.out);
+ }
+ });
+ }
+
+ private void addPV(String pv, String maxRate, List<String> listPVs) {
htcs.forEach(h -> {
- if(h.getID().equalsIgnoreCase(strHtc)){
- System.out.println(LocalDateTime.MIN.toString());
- System.out.println(LocalDateTime.MAX.toString());
- var pvs = h.getPVs(pv, LocalDateTime.MIN.toString(),
LocalDateTime.MAX.toString());
- ShellTable table = new ShellTable();
- table.column("Date");
- table.column("Value");
- pvs.forEach((p) ->{
- table.addRow().addContent(p.getLeft(), p.getRight());
+ if(h.getID().equalsIgnoreCase(strMainHtc)){
+ listPVs.forEach(s ->{
+ h.addPV(s, Double.MAX_VALUE);
});
- table.print(System.out);
- }
- });
+ }
+ });
}
+
+ private void removePV(String htc, String strpv) {
+ htcs.forEach(h -> {
+ if(h.getID().equalsIgnoreCase(htc)){
+ h.removePV(strpv);
+ }
+ });
+ }
}
diff --git
a/plc4j/tools/merlot/org.apache.plc4x.merlot.archiver/src/main/java/org/apache/plc4x/merlot/archiver/core/MerlotPBRawSerializer.java
b/plc4j/tools/merlot/org.apache.plc4x.merlot.archiver/src/main/java/org/apache/plc4x/merlot/archiver/core/MerlotPBRawSerializer.java
new file mode 100644
index 0000000..9e0139d
--- /dev/null
+++
b/plc4j/tools/merlot/org.apache.plc4x.merlot.archiver/src/main/java/org/apache/plc4x/merlot/archiver/core/MerlotPBRawSerializer.java
@@ -0,0 +1,117 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.plc4x.merlot.archiver.core;
+
+import org.apache.plc4x.merlot.api.PB.EPICSEvent;
+import org.epics.vtype.VType;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.List;
+
+/**
+ * Clase utilitaria para la generación de archivos en formato PBRAW compatibles
+ * con el EPICS Archiver Appliance.
+ */
+public final class MerlotPBRawSerializer {
+
+ // Constructor privado para evitar instanciación de clase utilitaria
+ private MerlotPBRawSerializer() {}
+
+ /**
+ * Serializa una lista de eventos VType a un archivo en formato .pbraw.
+ *
+ * @param events Lista de eventos provenientes de la red de control.
+ * @param pvName Nombre de la Variable de Proceso (PV).
+ * @param fileName Nombre del archivo de salida.
+ * @throws IOException Si ocurre un error durante la escritura.
+ */
+ public static void serializeToPBRaw(List<VType> events, String pvName,
OutputStream out) throws IOException {
+
+ if (events == null || events.isEmpty()) {
+ return;
+ }
+
+ // 1. Escribir el PayloadInfo (Metadatos obligatorios para
pbrawclient)
+ // Se asume el tipo basado en el primer elemento de la lista
+ MerlotPayloadMapping mapping =
MerlotPayloadMapping.fromVType(events.get(0));
+ if (mapping == null) {
+ throw new IOException("Tipo de VType no soportado para
serialización.");
+ }
+
+ EPICSEvent.PayloadInfo info = EPICSEvent.PayloadInfo.newBuilder()
+ .setPvname(pvName)
+ .setType(MerlotTypeMapping.fromVType(events.get(0)))
+ .setElementCount(1)
+ .build();
+
+ info.writeDelimitedTo(out);
+
+ // 2. Serializar cada evento VType usando la factoría
MerlotPayloadMapping
+ for (VType vType : events) {
+ Object pbEvent = MerlotPayloadMapping.createEvent(vType);
+
+ if (pbEvent != null) {
+ writeEventToStream(out, pbEvent);
+ }
+ }
+ }
+
+ /**
+ * Escribe el objeto de Protocol Buffers en el stream usando formato
delimitado.
+ */
+ private static void writeEventToStream(OutputStream out, Object pbEvent)
throws IOException {
+ if (pbEvent instanceof com.google.protobuf.MessageLite) {
+ ((com.google.protobuf.MessageLite) pbEvent).writeDelimitedTo(out);
+ } else {
+ // Manejo manual de varint si no es un mensaje directo de Protobuf
+ byte[] bytes = serializeToBytes(pbEvent);
+ if (bytes != null) {
+ writeVarint32(out, bytes.length);
+ out.write(bytes);
+ }
+ }
+ }
+
+ /**
+ * Utilidad para escribir el prefijo de tamaño (Varint32) requerido por el
protocolo.
+ */
+ private static void writeVarint32(OutputStream out, int value) throws
IOException {
+ while (true) {
+ if ((value & ~0x7F) == 0) {
+ out.write(value);
+ return;
+ } else {
+ out.write((value & 0x7F) | 0x80);
+ value >>>= 7;
+ }
+ }
+ }
+
+ /**
+ * Convierte el objeto del evento en su representación de bytes.
+ */
+ private static byte[] serializeToBytes(Object pbEvent) {
+ if (pbEvent instanceof EPICSEvent.ScalarDouble) {
+ return ((EPICSEvent.ScalarDouble) pbEvent).toByteArray();
+ } else if (pbEvent instanceof EPICSEvent.ScalarInt) {
+ return ((EPICSEvent.ScalarInt) pbEvent).toByteArray();
+ }
+ // Añadir otros tipos según sea necesario
+ return null;
+ }
+}
diff --git
a/plc4j/tools/merlot/org.apache.plc4x.merlot.archiver/src/main/java/org/apache/plc4x/merlot/archiver/core/MerlotPayloadMapping.java
b/plc4j/tools/merlot/org.apache.plc4x.merlot.archiver/src/main/java/org/apache/plc4x/merlot/archiver/core/MerlotPayloadMapping.java
new file mode 100644
index 0000000..6d0b852
--- /dev/null
+++
b/plc4j/tools/merlot/org.apache.plc4x.merlot.archiver/src/main/java/org/apache/plc4x/merlot/archiver/core/MerlotPayloadMapping.java
@@ -0,0 +1,188 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.plc4x.merlot.archiver.core;
+
+
+import org.epics.vtype.*;
+import java.time.Instant;
+import java.time.ZoneId;
+import java.time.ZonedDateTime;
+import java.util.Arrays;
+import java.util.EnumMap;
+import java.util.List;
+import java.util.function.Function;
+import org.apache.plc4x.merlot.api.PB.EPICSEvent;
+import org.apache.plc4x.merlot.api.PB.EPICSEvent.PayloadType;
+import org.epics.util.array.IteratorDouble;
+
+/**
+ * Mapeador de tipos entre la jerarquía VType de EPICS y los mensajes Protobuf
+ * de la arquitectura del Archiver Appliance.
+ */
+public enum MerlotPayloadMapping {
+ // Escalares
+ SCALAR_DOUBLE(PayloadType.SCALAR_DOUBLE, VDouble.class),
+ SCALAR_FLOAT(PayloadType.SCALAR_FLOAT, VFloat.class),
+ SCALAR_INT(PayloadType.SCALAR_INT, VInt.class),
+ SCALAR_SHORT(PayloadType.SCALAR_SHORT, VShort.class),
+ SCALAR_BYTE(PayloadType.SCALAR_BYTE, VByte.class),
+ SCALAR_STRING(PayloadType.SCALAR_STRING, VString.class),
+ SCALAR_ENUM(PayloadType.SCALAR_ENUM, VEnum.class),
+
+ // Vectores (Waveforms)
+ VECTOR_DOUBLE(PayloadType.WAVEFORM_DOUBLE, VDoubleArray.class),
+ VECTOR_FLOAT(PayloadType.WAVEFORM_FLOAT, VFloatArray.class),
+ VECTOR_INT(PayloadType.WAVEFORM_INT, VIntArray.class),
+ VECTOR_SHORT(PayloadType.WAVEFORM_SHORT, VShortArray.class),
+ VECTOR_BYTE(PayloadType.WAVEFORM_BYTE, VByteArray.class);
+
+ private final PayloadType protoType;
+ private final Class<? extends VType> vTypeClass;
+
+ private static final EnumMap<MerlotPayloadMapping, Function<VType,
Object>> CONSTRUCTORS = new EnumMap<>(MerlotPayloadMapping.class);
+
+ static {
+ // Registro de factorías para Escalares
+ CONSTRUCTORS.put(SCALAR_DOUBLE, (v) -> buildScalarDouble((VDouble) v));
+ CONSTRUCTORS.put(SCALAR_FLOAT, (v) -> buildScalarFloat((VFloat) v));
+ CONSTRUCTORS.put(SCALAR_INT, (v) -> buildScalarInt((VInt) v));
+ CONSTRUCTORS.put(SCALAR_SHORT, (v) -> buildScalarShort((VShort) v));
+ CONSTRUCTORS.put(SCALAR_BYTE, (v) -> buildScalarByte((VByte) v));
+ CONSTRUCTORS.put(SCALAR_STRING, (v) -> buildScalarString((VString) v));
+ CONSTRUCTORS.put(SCALAR_ENUM, (v) -> buildScalarEnum((VEnum) v));
+
+ // Registro de factorías para Vectores
+ CONSTRUCTORS.put(VECTOR_DOUBLE, (v) ->
buildVectorDouble((VDoubleArray) v));
+ CONSTRUCTORS.put(VECTOR_FLOAT, (v) -> buildVectorFloat((VFloatArray)
v));
+ CONSTRUCTORS.put(VECTOR_INT, (v) -> buildVectorInt((VIntArray) v));
+ CONSTRUCTORS.put(VECTOR_SHORT, (v) -> buildVectorShort((VShortArray)
v));
+ CONSTRUCTORS.put(VECTOR_BYTE, (v) -> buildVectorByte((VByteArray)
v));
+ }
+
+ MerlotPayloadMapping(PayloadType protoType, Class<? extends VType>
vTypeClass) {
+ this.protoType = protoType;
+ this.vTypeClass = vTypeClass;
+ }
+
+ /**
+ * Punto de entrada principal para crear un evento serializable a partir
de un VType.
+ */
+ public static Object createEvent(VType vtype) {
+ MerlotPayloadMapping mapping = fromVType(vtype);
+ if (mapping != null && CONSTRUCTORS.containsKey(mapping)) {
+ return CONSTRUCTORS.get(mapping).apply(vtype);
+ }
+ return null;
+ }
+
+ public static MerlotPayloadMapping fromVType(VType v) {
+ for (MerlotPayloadMapping m : values()) {
+ if (m.vTypeClass.isInstance(v)) return m;
+ }
+ return null;
+ }
+
+ // --- Implementación de Builders Escalares ---
+
+ private static EPICSEvent.ScalarDouble buildScalarDouble(VDouble v) {
+ TimeInfo ti = getTimeInfo(v.getTime().getTimestamp());
+ return EPICSEvent.ScalarDouble.newBuilder()
+
.setSecondsintoyear(ti.sec).setNano(ti.nano).setVal(v.getValue())
+ .setSeverity(v.getAlarm().getStatus().ordinal())
+ .setStatus(v.getAlarm().getStatus().ordinal()).build();
+ }
+
+ private static EPICSEvent.ScalarFloat buildScalarFloat(VFloat v) {
+ TimeInfo ti = getTimeInfo(v.getTime().getTimestamp());
+ return EPICSEvent.ScalarFloat.newBuilder()
+
.setSecondsintoyear(ti.sec).setNano(ti.nano).setVal(v.getValue())
+ .setSeverity(v.getAlarm().getStatus().ordinal()).build();
+ }
+
+ private static EPICSEvent.ScalarInt buildScalarInt(VInt v) {
+ TimeInfo ti = getTimeInfo(v.getTime().getTimestamp());
+ return EPICSEvent.ScalarInt.newBuilder()
+
.setSecondsintoyear(ti.sec).setNano(ti.nano).setVal(v.getValue())
+ .setSeverity(v.getAlarm().getStatus().ordinal()).build();
+ }
+
+ private static EPICSEvent.ScalarShort buildScalarShort(VShort v) {
+ TimeInfo ti = getTimeInfo(v.getTime().getTimestamp());
+ return EPICSEvent.ScalarShort.newBuilder()
+
.setSecondsintoyear(ti.sec).setNano(ti.nano).setVal(v.getValue())
+ .setSeverity(v.getAlarm().getStatus().ordinal()).build();
+ }
+
+ private static EPICSEvent.ScalarString buildScalarString(VString v) {
+ TimeInfo ti = getTimeInfo(v.getTime().getTimestamp());
+ return EPICSEvent.ScalarString.newBuilder()
+
.setSecondsintoyear(ti.sec).setNano(ti.nano).setVal(v.getValue())
+ .setSeverity(v.getAlarm().getStatus().ordinal()).build();
+ }
+
+ private static EPICSEvent.ScalarEnum buildScalarEnum(VEnum v) {
+ TimeInfo ti = getTimeInfo(v.getTime().getTimestamp());
+ return EPICSEvent.ScalarEnum.newBuilder()
+
.setSecondsintoyear(ti.sec).setNano(ti.nano).setVal(v.getIndex())
+ .setSeverity(v.getAlarm().getStatus().ordinal()).build();
+ }
+
+ // --- Implementación de Builders de Vectores (Waveforms) ---
+
+ private static EPICSEvent.VectorDouble buildVectorDouble(VDoubleArray v) {
+ TimeInfo ti = getTimeInfo(v.getTime().getTimestamp());
+ double[] values = v.getData().toArray(new double[0]);
+ List<Double> iterableValues = Arrays.stream(values)
+ .boxed()
+ .toList();
+ return EPICSEvent.VectorDouble.newBuilder()
+
.setSecondsintoyear(ti.sec).setNano(ti.nano).addAllVal(iterableValues)
+ .setSeverity(v.getAlarm().getStatus().ordinal()).build();
+ }
+
+ private static EPICSEvent.VectorInt buildVectorInt(VIntArray v) {
+ TimeInfo ti = getTimeInfo(v.getTime().getTimestamp());
+ Integer[] values = v.getData().toArray(new Integer[0]);
+ Iterable<Integer> iterableValues = Arrays.asList(values);
+ return EPICSEvent.VectorInt.newBuilder()
+
.setSecondsintoyear(ti.sec).setNano(ti.nano).addAllVal(iterableValues)
+ .setSeverity(v.getAlarm().getStatus().ordinal()).build();
+ }
+
+ // Métodos abreviados para otros tipos numéricos
+
+ private static EPICSEvent.ScalarByte buildScalarByte(VByte v) { return
null;}
+
+ private static Object buildVectorFloat(VFloatArray v) { return null; }
+
+ private static Object buildVectorShort(VShortArray v) { return null; }
+
+ private static Object buildVectorByte(VByteArray v) { return null; }
+
+ // --- Utilidad de Tiempo ---
+
+ private static class TimeInfo { int sec; int nano; }
+
+ private static TimeInfo getTimeInfo(Instant ts) {
+ ZonedDateTime zdt = ZonedDateTime.ofInstant(ts, ZoneId.of("UTC"));
+ long yearStart = ZonedDateTime.of(zdt.getYear(), 1, 1, 0, 0, 0, 0,
ZoneId.of("UTC")).toEpochSecond();
+ TimeInfo ti = new TimeInfo();
+ ti.sec = (int) (ts.getEpochSecond() - yearStart);
+ ti.nano = ts.getNano();
+ return ti;
+ }
+}
diff --git
a/plc4j/tools/merlot/org.apache.plc4x.merlot.archiver/src/main/java/org/apache/plc4x/merlot/archiver/core/MerlotTypeMapping.java
b/plc4j/tools/merlot/org.apache.plc4x.merlot.archiver/src/main/java/org/apache/plc4x/merlot/archiver/core/MerlotTypeMapping.java
new file mode 100644
index 0000000..2a67646
--- /dev/null
+++
b/plc4j/tools/merlot/org.apache.plc4x.merlot.archiver/src/main/java/org/apache/plc4x/merlot/archiver/core/MerlotTypeMapping.java
@@ -0,0 +1,77 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.plc4x.merlot.archiver.core;
+
+
+import org.epics.vtype.*;
+import java.util.HashMap;
+import java.util.Map;
+import org.apache.plc4x.merlot.api.PB.EPICSEvent.PayloadType;
+
+public enum MerlotTypeMapping {
+ SCALAR_FLOAT(PayloadType.SCALAR_FLOAT, VFloat.class),
+ SCALAR_DOUBLE(PayloadType.SCALAR_DOUBLE, VDouble.class),
+ SCALAR_INT(PayloadType.SCALAR_INT, VInt.class),
+ SCALAR_STRING(PayloadType.SCALAR_STRING, VString.class),
+ SCALAR_ENUM(PayloadType.SCALAR_ENUM, VEnum.class),
+ VECTOR_DOUBLE(PayloadType.WAVEFORM_DOUBLE, VDoubleArray.class),
+ VECTOR_INT(PayloadType.WAVEFORM_INT, VIntArray.class);
+
+ private final PayloadType protoType;
+ private final Class<? extends VType> vTypeClass;
+
+ // Mapa para búsqueda rápida inversa
+ private static final Map<PayloadType, MerlotTypeMapping> BY_PROTO = new
HashMap<>();
+
+ static {
+ for (MerlotTypeMapping mapping : values()) {
+ BY_PROTO.put(mapping.protoType, mapping);
+ }
+ }
+
+ MerlotTypeMapping(PayloadType protoType, Class<? extends VType>
vTypeClass) {
+ this.protoType = protoType;
+ this.vTypeClass = vTypeClass;
+ }
+
+ public PayloadType getProtoType() { return protoType; }
+ public Class<? extends VType> getVTypeClass() { return vTypeClass; }
+
+ /**
+ * Obtiene el mapeo a partir del tipo de Protobuf
+ */
+ public static MerlotTypeMapping fromProto(PayloadType type) {
+ return BY_PROTO.get(type);
+ }
+
+ /**
+ * Obtiene el PayloadType correspondiente a una instancia de VType.
+ * @param vtype La instancia de datos de EPICS (VDouble, VInt, etc.)
+ * @return El PayloadType de EPICSEvent o null si no hay coincidencia.
+ */
+ public static PayloadType fromVType(VType vtype) {
+ if (vtype == null) return null;
+
+ for (MerlotTypeMapping mapping : values()) {
+ // Verificamos si la instancia de vtype implementa la clase del
mapeo
+ if (mapping.getVTypeClass().isInstance(vtype)) {
+ return mapping.getProtoType();
+ }
+ }
+ return null;
+ }
+}
diff --git
a/plc4j/tools/merlot/org.apache.plc4x.merlot.archiver/src/main/java/org/apache/plc4x/merlot/archiver/impl/MerlotDataBrowserRTGetDataPVImpl.java
b/plc4j/tools/merlot/org.apache.plc4x.merlot.archiver/src/main/java/org/apache/plc4x/merlot/archiver/impl/MerlotDataBrowserRTGetDataPVImpl.java
index 9d9b280..e0aed53 100644
---
a/plc4j/tools/merlot/org.apache.plc4x.merlot.archiver/src/main/java/org/apache/plc4x/merlot/archiver/impl/MerlotDataBrowserRTGetDataPVImpl.java
+++
b/plc4j/tools/merlot/org.apache.plc4x.merlot.archiver/src/main/java/org/apache/plc4x/merlot/archiver/impl/MerlotDataBrowserRTGetDataPVImpl.java
@@ -17,14 +17,12 @@
package org.apache.plc4x.merlot.archiver.impl;
import java.io.IOException;
+import java.io.OutputStream;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
-import org.apache.plc4x.merlot.api.PB.EPICSEvent;
-import org.apache.plc4x.merlot.api.PB.EPICSEvent.PayloadInfo;
-import org.apache.plc4x.merlot.api.PB.EPICSEvent.PayloadInfo.Builder;
import org.apache.plc4x.merlot.archiver.api.MerlotHtc;
public class MerlotDataBrowserRTGetDataPVImpl extends HttpServlet {
@@ -38,20 +36,19 @@ public class MerlotDataBrowserRTGetDataPVImpl extends
HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
String nombre = req.getParameter("nombre");
- resp.setContentType("text/plain");
+ resp.setContentType("text/plain");
PrintWriter pw = resp.getWriter();
pw.println("Tu nombre: " + nombre);
pw.close();
}
-
-// private Builder buildHeader(){
-// Builder builder = PayloadInfo.newBuilder()
-// .setPvname("")
-// .setType(EPICSEvent.PayloadType.SCALAR_ENUM)
-// .setYear(0).
-// .setElementCount(0);
-// return null;
-// }
-
+ /*
+ *
+ */
+ private void PBRawResponse(String pv req, OutputStream out) {
+ String[] pvs = req.getParameterValues("pv");
+
+
+ }
+
}
diff --git
a/plc4j/tools/merlot/org.apache.plc4x.merlot.archiver/src/main/java/org/apache/plc4x/merlot/archiver/impl/MerlotHtcIoTDBImpl.java
b/plc4j/tools/merlot/org.apache.plc4x.merlot.archiver/src/main/java/org/apache/plc4x/merlot/archiver/impl/MerlotHtcIoTDBImpl.java
index e8e2651..8b34897 100644
---
a/plc4j/tools/merlot/org.apache.plc4x.merlot.archiver/src/main/java/org/apache/plc4x/merlot/archiver/impl/MerlotHtcIoTDBImpl.java
+++
b/plc4j/tools/merlot/org.apache.plc4x.merlot.archiver/src/main/java/org/apache/plc4x/merlot/archiver/impl/MerlotHtcIoTDBImpl.java
@@ -106,7 +106,7 @@ public class MerlotHtcIoTDBImpl implements MerlotHtc {
}
@Override
- public List<Pair<LocalDateTime, VType>> getPVs(String strPV, String init,
String end) {
+ public List<VType> getPVs(String strPV, String init, String end) {
return null;
}
diff --git
a/plc4j/tools/merlot/org.apache.plc4x.merlot.archiver/src/main/java/org/apache/plc4x/merlot/archiver/impl/MerlotHtcRTImpl.java
b/plc4j/tools/merlot/org.apache.plc4x.merlot.archiver/src/main/java/org/apache/plc4x/merlot/archiver/impl/MerlotHtcRTImpl.java
index 4e80cdb..6ad4817 100644
---
a/plc4j/tools/merlot/org.apache.plc4x.merlot.archiver/src/main/java/org/apache/plc4x/merlot/archiver/impl/MerlotHtcRTImpl.java
+++
b/plc4j/tools/merlot/org.apache.plc4x.merlot.archiver/src/main/java/org/apache/plc4x/merlot/archiver/impl/MerlotHtcRTImpl.java
@@ -16,8 +16,10 @@
*/
package org.apache.plc4x.merlot.archiver.impl;
+import java.time.Instant;
import java.time.LocalDateTime;
-import java.util.Collection;
+import java.time.format.DateTimeFormatter;
+import java.time.format.DateTimeFormatterBuilder;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
@@ -26,12 +28,10 @@ import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import org.apache.commons.collections4.queue.CircularFifoQueue;
-import org.apache.commons.lang3.tuple.ImmutablePair;
-import org.apache.commons.lang3.tuple.Pair;
import org.apache.plc4x.merlot.archiver.api.MerlotGPClient;
import org.apache.plc4x.merlot.archiver.api.MerlotHtc;
import org.epics.gpclient.GPClientInstance;
-import org.epics.gpclient.PV;
+import org.epics.gpclient.PVEvent;
import org.epics.gpclient.PVReader;
import org.epics.gpclient.datasource.CompositeDataSource;
import org.epics.vtype.VDouble;
@@ -49,42 +49,32 @@ public class MerlotHtcRTImpl implements MerlotHtc {
private static final Pattern SIM_PATTERN = Pattern.compile("(^noise)");
private static final String strID = "rt";
+
+ private final DateTimeFormatter formatter = new DateTimeFormatterBuilder()
+ .append(DateTimeFormatter.ISO_INSTANT)
+ .appendLiteral("(0)")
+ .toFormatter();
CompositeDataSource cds = new CompositeDataSource();
private Map<String, PVReader<VType>> readerPvs = new ConcurrentHashMap<>();
- private Map<String, CircularFifoQueue<Pair<LocalDateTime, VType>>> pvs =
new ConcurrentHashMap<>();
+ private Map<String, CircularFifoQueue<VType>> pvs = new
ConcurrentHashMap<>();
GPClientInstance gpClient;
-
- private PVReader<VType> pv1;
- private PVReader<VType> pv2;
+
public MerlotHtcRTImpl(MerlotGPClient gpMerlotClient) {
- gpClient = gpMerlotClient.gpClientDefaultInstance();
+ gpClient = gpMerlotClient.gpClientFactory("HtcRT ");
}
@Override
public void init() {
- CircularFifoQueue<Pair<LocalDateTime, VType>> queue = new
CircularFifoQueue<Pair<LocalDateTime, VType>>(2400);
- pvs.put("sim://noise", queue);
-
- pv1 = gpClient.read("sim://noise")
- .addReadListener((event, p) -> {
- pvs.get("sim://noise").add(new
ImmutablePair(LocalDateTime.now(), p.getValue()));
- })
- .start();
-
- readerPvs.put("sim://noise", pv1);
-
+ //
}
@Override
public void destroy() {
try {
- pv1.close();
- pv2.close();
- gpClient.getDefaultDataSource().getChannels().clear();
gpClient.close();
} catch (IllegalStateException e) {
e.printStackTrace();
@@ -97,13 +87,31 @@ public class MerlotHtcRTImpl implements MerlotHtc {
}
@Override
- public void addPV(String strPV, Double interval) {
- //
+ public void addPV(String strPV, Double maxRate) {
+
+ PVReader<VType> pv ;
+ CircularFifoQueue<VType> queue = new CircularFifoQueue<VType>(2400);
+ pvs.put(strPV, queue);
+ String strPVA = "pva://" + strPV.trim();
+
+ pv = gpClient.read(strPVA)
+ .addReadListener((event, p) -> {
+ if (event.isType(PVEvent.Type.VALUE)) {
+ pvs.get(strPV).add(p.getValue());
+ }
+ })
+ .start();
+
+ readerPvs.put(strPV, pv);
+
}
@Override
public void removePV(String strPV) {
- //
+ var pv = readerPvs.get(strPV);
+ readerPvs.remove(strPV);
+ pv.close();
+ pvs.remove(strPV);
}
@Override
@@ -115,19 +123,26 @@ public class MerlotHtcRTImpl implements MerlotHtc {
}
@Override
- public List<Pair<LocalDateTime, VType>> getPVs(String strPV, String init,
String end) {
- List<Pair<LocalDateTime, VType>> result;
- LocalDateTime inicio = LocalDateTime.parse(init);
- LocalDateTime fin = LocalDateTime.parse(end);
-
- return pvs.values().stream() // Obtenemos todas las CircularFifoQueue
- .flatMap(Collection::stream) // Aplanamos todas las colas en
un solo stream de pares
- .filter(pair -> {
- LocalDateTime fecha = pair.getLeft();
- // Filtro: inicio <= fecha <= fin
- return !fecha.isBefore(inicio) && !fecha.isAfter(fin);
- })
- .collect(Collectors.toList());
+ public List<VType> getPVs(String strPV, String init, String end) {
+ List<VType> result;
+ if (init.indexOf("(") > 0) {
+ init = init.substring(0, init.indexOf("("));
+ }
+ if (end.indexOf("(") > 0) {
+ end = end.substring(0, end.indexOf("("));
+ }
+
+ Instant inicio = Instant.parse(init);
+ Instant fin = Instant.parse(end);
+
+ var queue = pvs.get(strPV);
+
+ return queue.stream()
+ .filter(v -> {
+ Instant fecha = ((VDouble) v).getTime().getTimestamp();
+ return !fecha.isBefore(inicio) && !fecha.isAfter(fin);
+ })
+ .collect(Collectors.toList());
}
}
diff --git
a/plc4j/tools/merlot/org.apache.plc4x.merlot.archiver/src/test/java/org/apache/plc4x/merlot/archiver/test/MerlotPayloadMappingTest.java
b/plc4j/tools/merlot/org.apache.plc4x.merlot.archiver/src/test/java/org/apache/plc4x/merlot/archiver/test/MerlotPayloadMappingTest.java
new file mode 100644
index 0000000..85d2e68
--- /dev/null
+++
b/plc4j/tools/merlot/org.apache.plc4x.merlot.archiver/src/test/java/org/apache/plc4x/merlot/archiver/test/MerlotPayloadMappingTest.java
@@ -0,0 +1,94 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.plc4x.merlot.archiver.test;
+
+import java.time.Instant;
+import java.util.Arrays;
+import java.util.List;
+import org.apache.plc4x.merlot.api.PB.EPICSEvent;
+import org.apache.plc4x.merlot.archiver.core.MerlotPayloadMapping;
+import org.epics.vtype.Alarm;
+import org.epics.vtype.Display;
+import org.epics.vtype.Time;
+import org.epics.vtype.VDouble;
+import org.epics.vtype.VDoubleArray;
+import org.epics.vtype.VInt;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+
+class MerlotPayloadMappingTest {
+
+ @Test
+ @DisplayName("Debe mapear correctamente un VDouble a ScalarDouble de
Protobuf")
+ void testCreateScalarDouble() {
+ // 1. Preparar datos de prueba
+ double testValue = 123.456;
+ Instant now = Instant.now();
+ // Usamos implementaciones estándar de VType para la prueba
+ VDouble vDouble = VDouble.of(testValue, Alarm.none(), Time.of(now),
Display.none());
+
+ // 2. Ejecutar el mapeo
+ Object result = MerlotPayloadMapping.createEvent(vDouble);
+
+ // 3. Verificaciones
+ assertNotNull(result);
+ assertTrue(result instanceof EPICSEvent.ScalarDouble);
+
+ EPICSEvent.ScalarDouble pbEvent = (EPICSEvent.ScalarDouble) result;
+ assertEquals(testValue, pbEvent.getVal(), 0.0001);
+ assertEquals(now.getNano(), pbEvent.getNano());
+ assertTrue(pbEvent.getSecondsintoyear() >= 0);
+ }
+
+ @Test
+ @DisplayName("Debe mapear correctamente un VDoubleArray a VectorDouble de
Protobuf")
+ void testCreateVectorDouble() {
+ // 1. Preparar Waveform de prueba
+ List<Double> testData = Arrays.asList(1.0, 2.0, 3.0);
+ VDoubleArray vArray =
VDoubleArray.of(org.epics.util.array.ArrayDouble.of(1.0, 2.0, 3.0),
+ Alarm.none(), Time.now(),
Display.none());
+
+ // 2. Ejecutar
+ Object result = MerlotPayloadMapping.createEvent(vArray);
+
+ // 3. Verificaciones
+ assertTrue(result instanceof EPICSEvent.VectorDouble);
+ EPICSEvent.VectorDouble vector = (EPICSEvent.VectorDouble) result;
+ assertEquals(3, vector.getValCount());
+ assertEquals(1.0, vector.getVal(0));
+ }
+
+ @Test
+ @DisplayName("Debe identificar el tipo MerlotPayloadMapping desde una
instancia")
+ void testFromVType() {
+ VInt vInt = VInt.of(10, Alarm.none(), Time.now(), Display.none());
+ MerlotPayloadMapping mapping = MerlotPayloadMapping.fromVType(vInt);
+
+ assertEquals(MerlotPayloadMapping.SCALAR_INT, mapping);
+// assertEquals(EPICSEvent.PayloadType.SCALAR_INT, mapping.SCALAR_INT.);
+ }
+
+ @Test
+ @DisplayName("Debe retornar null para tipos no soportados o nulos")
+ void testUnsupportedTypes() {
+ assertNull(MerlotPayloadMapping.createEvent(null));
+ }
+}
\ No newline at end of file
diff --git
a/plc4j/tools/merlot/org.apache.plc4x.merlot.archiver/src/test/java/org/apache/plc4x/merlot/archiver/test/MerlotTypeMappingTest.java
b/plc4j/tools/merlot/org.apache.plc4x.merlot.archiver/src/test/java/org/apache/plc4x/merlot/archiver/test/MerlotTypeMappingTest.java
new file mode 100644
index 0000000..2ad7a54
--- /dev/null
+++
b/plc4j/tools/merlot/org.apache.plc4x.merlot.archiver/src/test/java/org/apache/plc4x/merlot/archiver/test/MerlotTypeMappingTest.java
@@ -0,0 +1,123 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.plc4x.merlot.archiver.test;
+
+/*
+ * Click nbfs://nbhost/SystemFileSystem/Templates/Licenses/license-default.txt
to change this license
+ * Click
nbfs://nbhost/SystemFileSystem/Templates/UnitTests/JUnit5TestClass.java to edit
this template
+ */
+
+import java.time.Instant;
+import org.apache.plc4x.merlot.api.PB.EPICSEvent.PayloadType;
+import org.apache.plc4x.merlot.archiver.core.MerlotTypeMapping;
+import org.epics.vtype.Alarm;
+import org.epics.vtype.AlarmSeverity;
+import org.epics.vtype.Display;
+import org.epics.vtype.Time;
+import org.epics.vtype.VDouble;
+import org.epics.vtype.VType;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+import static org.junit.jupiter.api.Assertions.*;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.EnumSource;
+
+/**
+ *
+ * @author cgarcia
+ */
+public class MerlotTypeMappingTest {
+
+ public MerlotTypeMappingTest() {
+ }
+
+ @BeforeAll
+ public static void setUpClass() {
+ }
+
+ @AfterAll
+ public static void tearDownClass() {
+ }
+
+ @BeforeEach
+ public void setUp() {
+ }
+
+ @AfterEach
+ public void tearDown() {
+ }
+
+ @Test
+ @DisplayName("Debe retornar el mapeo correcto para SCALAR_DOUBLE")
+ void testScalarDoubleMapping() {
+ MerlotTypeMapping mapping =
MerlotTypeMapping.fromProto(PayloadType.SCALAR_DOUBLE);
+
+ assertNotNull(mapping, "El mapeo no debería ser nulo");
+ assertEquals(VDouble.class, mapping.getVTypeClass(),
+ "SCALAR_DOUBLE debe mapear a la interfaz VDouble");
+ }
+
+ @ParameterizedTest
+ @EnumSource(MerlotTypeMapping.class)
+ @DisplayName("Validar integridad de todos los mapeos definidos")
+ void testAllMappings(MerlotTypeMapping mapping) {
+ // Verifica que la clase VType asociada sea efectivamente una
subinterfaz de VType
+ assertTrue(VType.class.isAssignableFrom(mapping.getVTypeClass()),
+ "La clase mapeada " + mapping.getVTypeClass().getName() + " debe
ser un VType");
+
+ // Verifica que el tipo de proto no sea nulo
+ assertNotNull(mapping.getProtoType(),
+ "El tipo de Protobuf para " + mapping.name() + " no debe ser
nulo");
+ }
+
+ @Test
+ @DisplayName("Debe manejar tipos de payload desconocidos devolviendo null")
+ void testUnknownPayloadType() {
+ // Usamos un valor que no esté en nuestro Enum de mapeo (si existe en
el .proto)
+ // o simplemente verificamos el comportamiento con null
+ assertNull(MerlotTypeMapping.fromProto(null),
+ "El mapeo de un tipo null debe resultar en null");
+ }
+
+ @Test
+ @DisplayName("Verificar consistencia del mapa estático inverso")
+ void testReverseMapConsistency() {
+ for (MerlotTypeMapping mapping : MerlotTypeMapping.values()) {
+ MerlotTypeMapping retrieved =
MerlotTypeMapping.fromProto(mapping.getProtoType());
+ assertEquals(mapping, retrieved,
+ "El mapeo recuperado por PayloadType debe coincidir con la
instancia del Enum");
+ }
+ }
+
+ @Test
+ @DisplayName("Debe detectar el PayloadType correcto desde una instancia
concreta")
+ void testFromVTypeInstance() {
+ // Creamos una instancia de prueba (VDouble)
+ VDouble myValue = VDouble.of(10.5, Alarm.none(), Time.now(),
Display.none());
+
+ PayloadType detectedType = MerlotTypeMapping.fromVType(myValue);
+
+ assertEquals(PayloadType.SCALAR_DOUBLE, detectedType,
+ "Una instancia de VDouble debe ser reconocida como SCALAR_DOUBLE");
+ }
+
+
+}
diff --git
a/plc4j/tools/merlot/org.apache.plc4x.merlot.archiver/src/test/java/org/apache/plc4x/merlot/archiver/test/PBRawFileSerializer.java
b/plc4j/tools/merlot/org.apache.plc4x.merlot.archiver/src/test/java/org/apache/plc4x/merlot/archiver/test/PBRawFileSerializer.java
new file mode 100644
index 0000000..0297338
--- /dev/null
+++
b/plc4j/tools/merlot/org.apache.plc4x.merlot.archiver/src/test/java/org/apache/plc4x/merlot/archiver/test/PBRawFileSerializer.java
@@ -0,0 +1,135 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.plc4x.merlot.archiver.test;
+
+/**
+ *
+ * @author cgarcia
+ */
+import org.epics.vtype.VDouble;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.time.Instant;
+import java.time.ZoneId;
+import java.time.ZonedDateTime;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Random;
+import org.apache.plc4x.merlot.api.PB.EPICSEvent;
+import org.epics.vtype.Alarm;
+import org.epics.vtype.AlarmSeverity;
+import org.epics.vtype.Display;
+import org.epics.vtype.Time;
+
+public class PBRawFileSerializer {
+
+ // Caracter de escape y delimitadores según el protocolo del Archiver
+ private static final int ESCAPE = 0x1B;
+ private static final int NEWLINE = 0x0A;
+
+ /**
+ * @param args the command line arguments
+ */
+ public static void main(String[] args) {
+ PBRawFileSerializer serializer = new PBRawFileSerializer();
+ List<VDouble> randomEvents = new ArrayList<>();
+ Random rand = new Random();
+
+ // Generar 10 valores aleatorios empezando desde ahora
+ Instant now = Instant.now();
+ for (int i = 0; i < 10; i++) {
+ double randomValue = 20.0 + (30.0 - 20.0) * rand.nextDouble();
+ // Incrementamos el tiempo en 1 segundo por cada muestra
+ Instant timestamp = now.plusSeconds(i);
+ Time ts = Time.of(timestamp);
+
+ randomEvents.add(VDouble.of(randomValue,
+ Alarm.none(),
+ ts,
+ Display.none()));
+ }
+
+ try {
+ String fileName = "random_data.pbraw";
+ serializer.serializeToPBRaw(randomEvents, "MY:RANDOM:PV",
fileName);
+ System.out.println("Archivo '" + fileName + "' generado con 10
valores aleatorios.");
+ } catch (IOException e) {
+ System.err.println("Error al generar el archivo: " +
e.getMessage());
+ }
+ }
+
+
+ public void serializeToPBRaw(List<VDouble> events, String pvName, String
fileName) throws IOException {
+ try (FileOutputStream fos = new FileOutputStream(fileName)) {
+
+ // 1. Escribir el PayloadInfo (Metadatos iniciales)
+ // El cliente necesita esto para saber que los datos son
ScalarDouble
+ EPICSEvent.PayloadInfo info = EPICSEvent.PayloadInfo.newBuilder()
+ .setPvname(pvName)
+ .setType(EPICSEvent.PayloadType.SCALAR_DOUBLE)
+ .setYear(2026)
+ .setElementCount(1)
+ .build();
+
+ info.writeDelimitedTo(fos);
+
+ // 2. Serializar cada VDouble
+ for (VDouble vDouble : events) {
+ EPICSEvent.ScalarDouble pbEvent = buildProtosEvent(vDouble);
+
+ // El cliente PBRAW espera los datos escapados si se
transmiten por stream
+ // Para un archivo local simple, writeDelimitedTo suele
bastar,
+ // pero implementamos el guardado binario puro aquí:
+ byte[] eventBytes = pbEvent.toByteArray();
+
+ // Escribir tamaño del mensaje (como varint o delimitado)
+ writeVarint32(fos, eventBytes.length);
+ fos.write(eventBytes);
+ }
+ }
+ }
+
+ private EPICSEvent.ScalarDouble buildProtosEvent(VDouble v) {
+ Instant ts = v.getTime().getTimestamp();
+ int year = ZonedDateTime.ofInstant(ts, ZoneId.of("UTC")).getYear();
+ long yearStart = ZonedDateTime.of(year, 1, 1, 0, 0, 0, 0,
ZoneId.of("UTC")).toEpochSecond();
+
+ return EPICSEvent.ScalarDouble.newBuilder()
+ .setSecondsintoyear((int) (ts.getEpochSecond() - yearStart))
+ .setNano(ts.getNano())
+ .setVal(v.getValue())
+ .setSeverity(v.getAlarm().getStatus().ordinal())
+ .build();
+ }
+
+ // Utilidad para escribir el prefijo de tamaño que espera el
deserializador de Google
+ private void writeVarint32(OutputStream out, int value) throws IOException
{
+ while (true) {
+ if ((value & ~0x7F) == 0) {
+ out.write(value);
+ return;
+ } else {
+ out.write((value & 0x7F) | 0x80);
+ value >>>= 7;
+ }
+ }
+ }
+
+
+
+}
diff --git a/plc4j/tools/merlot/org.apache.plc4x.merlot.das.api/pom.xml
b/plc4j/tools/merlot/org.apache.plc4x.merlot.das.api/pom.xml
index 2b87992..d45abf4 100644
--- a/plc4j/tools/merlot/org.apache.plc4x.merlot.das.api/pom.xml
+++ b/plc4j/tools/merlot/org.apache.plc4x.merlot.das.api/pom.xml
@@ -244,6 +244,11 @@
<version>0.13.0-SNAPSHOT</version>
</dependency>
-->
+ <dependency>
+ <groupId>com.google.protobuf</groupId>
+ <artifactId>protobuf-java</artifactId>
+ <version>4.34.1</version>
+ </dependency>
</dependencies>
<profiles>