Tablas y bases de datos
Campos de datos, tablas y bases de datos
Para las funciones siguientes, las tablas necesarias se pasan a la función como recurso. La tabla se lee y se prepara internamente.
Como campo de datos, según el valor de entrada se interpola entre elementos de datos individuales.
Como tabla con nombres de campo, en determinados campos clave puede buscarse una entrada coincidente y leerse distintos campos de esa entrada.
Está prevista la implementación de una interfaz sqlite para acceder a una instancia local de base de datos con consultas de búsqueda definibles.
Curva característica, mapa característico "lookup"
Con la función lookup() pueden interpolarse linealmente, a partir de curvas características o mapas característicos, datos correspondientes a una a tres magnitudes de entrada.
L1 = lookup(x, {...});
L2 = lookup(x, y, {...});
L3 = lookup(x, y, z, {...});
// Das Konfigurationsobjekt ist verpflichtend und muss
// auf eine Ressource verweisen:
Lx = lookup(..., {$ref:'myLookupData'});
El objeto de configuración sigue una sintaxis propia y, por ello, debe obtenerse mediante {$ref:'myLookupData'} del área de recursos del módulo matemático. Se admiten dos formatos: el formato de texto clásico y una descripción en el formato JSON simplificado1
Descripción de las entradas / ejes
A cada señal de entrada se le asigna un eje. Este se define por su unidad <unit>, un valor inicial <from>, el incremento <step> y el número de puntos de apoyo <count>. Los ejes no tienen que estar necesariamente ordenados en secuencia numérica ascendente.
Configuración en formato de texto (clásico)
Para cada eje debe añadirse al principio del bloque de texto la siguiente descripción, es decir, al menos una y como máximo tres:
IN <unit> <from> <step> <count>
Configuración en formato JSON:
En el formato JSON es posible, además, indicar un nombre descriptivo e informal <name> y, como alternativa a uno de los parámetros de valor mencionados antes, el valor final <to>. Por tanto, deben definirse exactamente tres de las cuatro propiedades <from>, <step>, <count> o <to>; la cuarta se calcula.
{
axes: [
{ name: <str>, // optional description
unit: <str>, // recommended
// choose 3 as mandatory of the following 4:
from: <dbl>, // left-side value of input axis
step: <dbl>, // increment between interpolation points
count: <uint>, // number of interpolation points
to: <dbl> // right-side value of input axis
}, ...
], ...
}
Descripción de las salidas
La señal de salida se describe mediante una unidad <unit>.
Configuración en formato de texto (clásico)
La siguiente definición debe indicarse una vez después de la definición de las entradas:
OUT <unit>
Configuración en formato JSON:
En el formato JSON es posible, además, indicar un nombre descriptivo e informal <name>. En lugar del objeto JSON para cada salida (recomendado), también puede indicarse únicamente la unidad física como cadena.
{
outputs: [
{ name: <str>, // optional description
unit: <str> // recommended
}, ... // prepared to support vector output in future
<str>, ... // alternative to define output by unit
], ...
Campo de datos
El campo de datos consta de valores <dbl>. El orden de los puntos de apoyo resulta del siguiente pseudocódigo:
foreach(z : axis3)
foreach(y : axis2)
foreach(x : axis1)
Value[x,y,z];
Configuración en formato de texto (clásico)
Los valores de datos simplemente se separan entre sí de cualquier manera mediante espacios, tabuladores o saltos de línea y se leen por orden en los puntos de apoyo de la curva característica o del mapa característico.
0 42.942 87.924 134.946
184.008 235.11 288.252
... continued up to 161 data points
Configuración en formato JSON:
En el formato JSON, todos los valores de datos se indican en un array de números de coma flotante.
{
data: [
<dbl>, ...
], ...
}
Resumen
Configuración en formato de texto (clásico)
Curva característica:
IN kPa 0.0 0.167751 161
OUT l
0
42.942
87.924
134.946
184.008
235.11
288.252
343.434
400.656
459.918
521.22
... continued up to 161 data points
Mapa característico:
IN g/h 0 10000 22
IN 1/min 900 100 10
OUT kW
0.0 40.8 81.6 132.9 185.2 200.0 200.0 200.0 200.0 200.0 200.0 200.0 200.0 200.0 200.0 200.0 200.0 200.0 200.0 200.0 200.0 200.0
0.0 40.0 80.0 131.3 184.3 230.0 230.0 230.0 230.0 230.0 230.0 230.0 230.0 230.0 230.0 230.0 230.0 230.0 230.0 230.0 230.0 230.0
0.0 38.5 76.9 125.8 182.4 233.5 286.9 310.0 310.0 310.0 310.0 310.0 310.0 310.0 310.0 310.0 310.0 310.0 310.0 310.0 310.0 310.0
... 7 more rows with 22 data points each
Configuración en el formato JSON simplificado
El objeto de configuración también puede describirse en el formato JSON simplificado1.
{
axes: [
{ name: <str>, // optional description
unit: <str>, // recommended
from: <dbl>, // left-side value of input axis
step: <dbl>, // increment between interpolation points
count: <uint>, // number of interpolation points
to: <dbl> // right-side value of input axis
}, ...
],
outputs: [
{ name: <str>, // optional description
unit: <str> // recommended
}, ... // prepared to support vector output in future
<str>, ... // alternative to define output by unit
],
data: [
<dbl>, ...
]
}
Puesta a disposición como recurso
Existen múltiples posibilidades de incorporar esta curva característica o descripción de mapa característico en los recursos del módulo matemático.
Para mapas característicos o curvas características pequeños resulta adecuado el formato como recurso de texto:
"resources": {
"myLookupData": [
"IN kPa 0.0 0.167751 161"
, "OUT l"
, "0 42.942 87.924 134.946 184.008 235.11 288.252 343.434"
, "400.656 459.918 521.22 584.562 649.944 717.366 785.90745"
, "854.45145 ... and more"
], ...
Alternativamente, el mismo mapa característico en el formato JSON simplificado:
"resources": {
"myLookupData": {
axes: [{ name: "HydroStatPressure", unit: "kPa"
, from: 0.0, step: 0.167751, count: 161 }],
outputs: [{ name: "Volume", unit: "l" }],
data: [
0, 42.942, 87.924, 134.946, 184.008, 235.11, 288.252, 343.434
, 400.656, 459.918, 521.22, 584.562, 649.944, 717.366, 785.90745
, 854.45145, // ... and more
]
}, ...
Para mapas característicos más grandes, o para aquellos que no deban aparecer o ser legibles en la configuración JSON del smartCORE, sería adecuada una cadena de caracteres procesada.
En primer lugar, con un archivo de mapa característico myLookupData.tab como fuente de datos, que sigue la sintaxis mencionada arriba (texto/JSON):
"resources": {
"myLookupData": {
"decoder": ["file"],
"value": "myLookupData.tab"
}, ...
o, si el mapa característico está disponible en forma comprimida:
"resources": {
"myLookupData": {
"decoder": ["file", "unzip"],
"value": "myLookupData.gz"
}, ...
Alternativamente, los datos también pueden incorporarse al recurso como cadena base64:
"resources": {
"myLookupData": {
"decoder": ["base64"],
"value": "Q09MCWcvaAkwCTEwMDAwCTIyDQpST1cJMS9taW4JOTAwCTEw..."
}, ...
... o el archivo comprimido como cadena base64:
"resources": {
"myLookupData": {
"decoder": ["base64", "unzip"],
"value": "UEsDBBQAAgAIACt3cUoHOG6qmgEAAD4FAAAcAAAAUF9lZmZf..."
}
Consulta de búsqueda "selectRow"
La función selectRow() busca secuencialmente en una tabla sencilla, a través de todas las filas, una coincidencia con las claves indicadas y devuelve el índice de fila encontrado. Si no se encuentra ninguna coincidencia, se devuelve -1. Una búsqueda solo se ejecuta si cambia al menos uno de los valores de clave solicitados.
Este índice de fila puede utilizarse en getField() para leer valores cualesquiera de los campos de datos de la tabla.
m1 = selectRow(key1, ..., { table: <str> // mandatory
, colSep: <regex>
, anyKey: <var>
, key: <str> // mandatory (*)
, keys: [<str>] // mandatory (*)
});
// (*) exactly one of key or keys must be defined
| Propiedad | Valor | Descripción |
|---|---|---|
| table | <str> | Obligatorio: nombre de un recurso de texto que define el contenido de la tabla. Los elementos de datos se interpretan como Variant y, por tanto, proporcionan el mejor tipo de datos posible. Los números de coma flotante deben escribirse con un punto como separador decimal. |
| colSep | <regex> | Expresión regular que define los separadores de columna. Por defecto: "[,;|\t]", de uso universal para un único carácter cualquiera de los indicados entre []. |
| anyKey | <var> | Un elemento de datos con este valor proporciona un resultado positivo para cualquier valor de clave, por defecto: "*" |
| key | <str> | Obligatorio (*, **): un campo de datos de la tabla en el que se busca el primer y único valor de clave key1. |
| keys | [<str>] | Obligatorio (*, **): un vector con varios campos de datos que deben aplicarse, en el orden indicado, a los valores de clave key1, key2, key3, ... |
(*) Debe utilizarse exactamente una de las propiedades key o keys para seleccionar la(s) columna(s) clave.
(**) Por defecto se busca siempre una coincidencia exacta del valor de clave con las entradas de la columna de la tabla. Al identificador del campo se le pueden anteponer opcionalmente los siguientes operadores de comparación para modificar la consulta; se permiten espacios entre el operador y el identificador:
| Operador | Significado | Ejemplo | Ejecución como... |
|---|---|---|---|
KEY sea un identificador de campo en keys:[..] | keyN sea el valor de clave pasado como parámetro | ||
== | Coincidencia exacta (por defecto) | 'KEY''==KEY' | keyN == Table[KEY] |
*= | para <str>: contiene | *=KEY | keyN contains Table[KEY] |
&= | para <uint>|<int>: todos los bits de la máscara activados | '&=KEY' | bAnd(keyN, Table[KEY]) == Table[KEY] |
&> | para <uint>|<int>: al menos un bit de la máscara activado | '&>KEY' | bAnd(keyN, Table[KEY]) > 0 |
&0 | para <uint>|<int>:ningún bit de la máscara activado | '&0 KEY' | bAnd(keyN, Table[KEY]) == 0 |
<= | Menor o igual | '<=KEY' | keyN <= Table[KEY] |
< | Menor que | '<KEY' | keyN < Table[KEY] |
>= | Mayor o igual | '>=KEY' | keyN >= Table[KEY] |
> | Mayor que | '>KEY' | keyN > Table[KEY] |
Para el tipo de datos <str>, las comparaciones se refieren al orden alfabético.
Formato de la tabla
-
La tabla se carga desde un recurso de texto (ASCII, UTF-8)
-
Los separadores de columna se definen mediante la propiedad colSep.
-
La misma tabla puede utilizarse en varias funciones
selectRow(). La indicación de colSep debe ser idéntica en todas, ya que no está establecido cuál de las funciones interpreta la tabla en primer lugar (y, con ello, también para todas las demás instancias). -
La primera línea contiene los nombres de campo, con los que se definen también las columnas clave y los campos de resultado.
-
Las líneas vacías se ignoran.
-
Las líneas que comienzan con 3 caracteres iguales del grupo
_ - = ~ *se interpretan como línea de separación y se ignoran. -
Cada campo de datos se lee y procesa individualmente como Variant. Por tanto, no existen tipos de datos fijos para una columna.
-
La búsqueda se realiza siempre de forma secuencial a través de todas las filas.
Lectura de una entrada de tabla "getField"
La función getField() lee de una tabla table interpretada con selectRow(), para el índice de fila encontrado rowIdx, el elemento de datos correspondiente del campo de datos field. El tipo de datos queda determinado por el elemento de datos como Variant.
Si rowIdx es < 0 o está fuera del intervalo válido, se devuelve el Variant definido con notFound.
v1 = getField(rowIdx, { table: <str> // mandatory
, field: <str> // mandatory
, notFound: <var>
});
| Propiedad | Valor | Descripción |
|---|---|---|
| table | <str> | Obligatorio: nombre de un recurso de texto que define el contenido de la tabla. Esta debe haberse interpretado y cargado en una función selectRow(). |
| field | <str> | Obligatorio: nombre de un campo de datos del que leer el valor de resultado. |
| notFound | <var> | Este Variant se devuelve como resultado si rowIdx no designa un resultado de búsqueda válido. Por defecto: false. |
Reproducción de datos grabados "playback" 2
Esta función está en fase de implementación y todavía no está prevista para el funcionamiento productivo.
La función playback() abre un archivo "Tape" y extrae de él evoluciones temporales de canales de datos seleccionados, "Tracks".
Según la estructura y la longitud de las fuentes de datos utilizadas, todavía pueden producirse violaciones de memoria y bloqueos del smartCORE.
Los tracks pueden publicarse con un prefijo adicional como variables en el módulo Math y, después, transferirse también al smartCORE como variable de salida.
De forma comparable a una grabadora de cinta clásica, pueden establecerse marcadores para la reproducción de un determinado intervalo de tiempo, y las funciones de control (Stop, Play, Pause, Rewind) pueden activarse también mediante una lógica ampliada.

Actualmente se admite el formato de archivo OSF sin comprimir. Otros formatos, como, p. ej., WAV, CSV, ... podrían seguir en versiones futuras.
Las evoluciones que se introducen en el sistema desde un tape pueden utilizarse como referencia para señales de medición, para seguir y supervisar su evolución dinámica tras un evento de inicio.
Para probar nuevas funciones del módulo Math, en un sistema de laboratorio estacionario pueden volver a reproducirse en tiempo real datos de medición del uso productivo. optiCONTROL ofrece distintas posibilidades para la preparación de datos: combinar, recortar o seleccionar canales.
Para proteger los archivos OSF de la unidad /sde frente a la compresión automática, basta con utilizar otra extensión de archivo, p. ej., *.osf_pb o *.osf_tape.
t1 = playback(ctrl, {...});
t2 = playback(ctrl, tInfo, {...});
t3 = playback(ctrl, tInfo, source, {...});
// Configuration is mandatory for paths and keys
tx = playback(..., { directory: <str> // mandatory!
, source: <str> // mandatory! (or by parameter)
, varPrefix: <str>
, keys: [<str>] // mandatory!
, tBegin: <date>
, tRewind: <date>
, tEnd: <date>
, result: <enum>
, loop: <bool>
, tZoom: <dbl>
, endVar: <str>
});
El parámetro tInfo puede contener un valor escalar o un vector de hasta 3 componentes. La descripción también se incluye en la tabla siguiente.
| Propiedad | Valor | Descripción |
|---|---|---|
| directory | <str> | Ruta del directorio con los tapes |
| source | <str> | Tape inicial; puede sobrescribirse mediante parámetro. Cada cambio carga del tape indicado los canales seleccionados mediante keys |
| varPrefix | <str> | El prefijo se antepone a los canales seleccionados para su publicación como variable, p. ej., "Tape." |
| keys | [<str>] | Lista con nombres de canal del archivo de tape que se cargan para la reproducción. |
| tBegin | <date> | Instante de inicio en el tape seleccionado |
| tRewind | <date> | Punto de destino para rebobinar o avanzar |
| tEnd | <date> | Instante final de la reproducción o retroceso automático |
| result | <enum> | Salida de la posición del tape como abs: marca de tiempo absoluta (por defecto) relOrigin: marca de tiempo relativa al inicio del tape relBegin: marca de tiempo relativa a la marca tBeginrelInterval: posición relativa en el intervalo |
| loop | <bool> | Al alcanzar la marca tEnd se salta de vuelta a tRewind y la reproducción continúa. (def.: false) |
| tZoom | <dbl> | Factor de zoom del eje de tiempo en la reproducción (def: 1.0), : avance rápido : cámara lenta |
| endVar | <str> | Variable de salida <bool>; se establece en 'true' cuando se alcanza el marcador tEnd. |
| tInfo | <dbl> | |
| tInfo | [<dbl>, <dbl>] | |
| tInfo | [<dbl>, <dbl>, <dbl>] |
Entrada de control ctrl
El primer parámetro ctrl es el mecanismo de la grabadora de cinta. Es una señal de estado: el valor presente es el estado del mecanismo y se mantiene hasta que otro valor lo sustituye.
La entrada acepta, a elección, una cadena de caracteres (sin distinguir mayúsculas de minúsculas) o el valor de estado numérico:
| ctrl | numérico | Efecto |
|---|---|---|
'stop' | 0 | Reproducción detenida. El cabezal de lectura permanece en su posición. Un track con valor predeterminado (#property <var>, default=<value>) emite ese valor; cualquier otro conserva su último valor |
'play' | 1 | Reproducción desde la posición del cabezal de lectura, escalada con tZoom |
'pause' | 2 | Reproducción detenida. El cabezal de lectura permanece en su posición y cada track conserva el valor situado bajo el cabezal de lectura. 'play' continúa sin desfase |
'start', 'begin' | 3 | Cabezal de lectura a la marca tBegin y detener |
'rewind' | 4 | Cabezal de lectura a la marca tRewind y detener (rebobinar o avanzar) |
Cualquier otro valor, tanto una cadena de caracteres desconocida como un número fuera de la tabla,
se trata como 'stop', sin aviso.
'start' y 'rewind' solo bobinan, no inician la reproducción. El mecanismo queda
parado en la nueva posición tras el bobinado hasta que llega 'play'.
Al alcanzar la marca tEnd no decide ctrl, sino loop: el mecanismo salta
a la marca tRewind (loop: true) o pasa a 'stop'. Un 'play' presente
al mismo tiempo no cambia esto. La variable designada con endVar indica en cada
ciclo si se ha alcanzado la marca tEnd.
La reproducción abarca en cada caso el intervalo semiabierto desde el inicio hasta el siguiente cambio: en el
instante de un cambio ya rige el nuevo estado. Por ello, una muestra situada exactamente en la marca tEnd no se
reproduce, y un 'stop' exactamente en tEnd tiene prioridad sobre el salto por
loop. Con loop: true, la reproducción continúa sin interrupción en el salto: la muestra en la
marca tRewind aparece en el instante de tEnd, sin valor predeterminado intercalado. Una pasada
dura exactamente tEnd − tRewind, escalado con tZoom.
El resultado de playback() es la posición del cabezal de lectura en el formato elegido con result.
Durante la reproducción aumenta de forma continua; por ello, la salida está marcada como interpolable
linealmente, igual que la salida de integrate(). Antes de cada salto a otra
posición (loop, 'start', 'rewind') se emite la posición válida hasta entonces un
paso de tiempo antes, para que la interpolación no atraviese el salto. 'pause' y
'stop' solo detienen el aumento.
// Tape mit einem Trigger starten und am Ende automatisch zurückspulen
ctrl = trg ? 'play' : 'stop';
pos = playback(ctrl, { directory: '/media/tapes'
, source: 'fahrt_2026-02-04.osf_tape'
, keys: ['Drehzahl', 'Moment']
, varPrefix: 'Tape.'
, loop: true
});