Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Added type conversion math component #5339

Draft
wants to merge 1 commit into
base: develop
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--

Copyright (c) 2024 Eurotech and/or its affiliates and others

This program and the accompanying materials are made
available under the terms of the Eclipse Public License 2.0
which is available at https://www.eclipse.org/legal/epl-2.0/

SPDX-License-Identifier: EPL-2.0

Contributors:
Eurotech

-->
<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0"
name="org.eclipse.kura.wire.TypeConversion"
activate="activate"
deactivate="deactivate"
modified="updated"
enabled="true"
immediate="true"
configuration-policy="require">
<implementation class="org.eclipse.kura.example.wire.math.singleport.typeconversion.TypeConversionComponent"/>
<service>
<provide interface="org.eclipse.kura.configuration.ConfigurableComponent"/>
<provide interface="org.eclipse.kura.wire.WireComponent"/>
<provide interface="org.osgi.service.wireadmin.Consumer"/>
<provide interface="org.eclipse.kura.wire.WireEmitter"/>
<provide interface="org.eclipse.kura.wire.WireReceiver"/>
<provide interface="org.osgi.service.wireadmin.Producer"/>
</service>
<reference bind="bindWireHelperService"
cardinality="1..1"
interface="org.eclipse.kura.wire.WireHelperService"
name="WireHelperService"
policy="static"
unbind="unbindWireHelperService"/>
<property name="service.pid" value="org.eclipse.kura.wire.TypeConversion"/>
<property name="kura.ui.service.hide" type="Boolean" value="true"/>
<property name="input.cardinality.minimum" type="Integer" value="1"/>
<property name="input.cardinality.maximum" type="Integer" value="1"/>
<property name="input.cardinality.default" type="Integer" value="1"/>
<property name="output.cardinality.minimum" type="Integer" value="1"/>
<property name="output.cardinality.maximum" type="Integer" value="1"/>
<property name="output.cardinality.default" type="Integer" value="1"/>
</scr:component>
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--

Copyright (c) 2024 Eurotech and/or its affiliates and others

This program and the accompanying materials are made
available under the terms of the Eclipse Public License 2.0
which is available at https://www.eclipse.org/legal/epl-2.0/

SPDX-License-Identifier: EPL-2.0

Contributors:
Eurotech

-->
<MetaData xmlns="http://www.osgi.org/xmlns/metatype/v1.2.0" localization="en_us">
<OCD id="org.eclipse.kura.wire.TypeConversion"
name="GainOffset"
description="A wire component that applies a type conversion to a configurable set of numerical input properties.">

<AD id="channels"
name="Channels and Conversion"
type="String" cardinality="0"
required="true"
default="channel1|Long"
description="Specifies the properties on which apply the type conversion. Separated by a new line|textarea">
</AD>

<AD id="emit.received.properties"
name="emit.received.properties"
type="Boolean" cardinality="0"
required="true"
default="false"
description="Specifies whether received properties should be included in the emitted envelope or not.">
</AD>
</OCD>

<Designate pid="org.eclipse.kura.wire.TypeConversion" factoryPid="org.eclipse.kura.wire.TypeConversion">
<Object ocdref="org.eclipse.kura.wire.TypeConversion"/>
</Designate>
</MetaData>
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
/*******************************************************************************
* Copyright (c) 2024 Eurotech and/or its affiliates and others
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Eurotech
*******************************************************************************/

package org.eclipse.kura.example.wire.math.singleport.typeconversion;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.eclipse.kura.configuration.ConfigurableComponent;
import org.eclipse.kura.type.DataType;
import org.eclipse.kura.type.TypedValue;
import org.eclipse.kura.type.TypedValues;
import org.eclipse.kura.wire.WireComponent;
import org.eclipse.kura.wire.WireEmitter;
import org.eclipse.kura.wire.WireEnvelope;
import org.eclipse.kura.wire.WireHelperService;
import org.eclipse.kura.wire.WireReceiver;
import org.eclipse.kura.wire.WireRecord;
import org.eclipse.kura.wire.WireSupport;
import org.osgi.framework.ServiceReference;
import org.osgi.service.component.ComponentContext;
import org.osgi.service.wireadmin.Wire;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class TypeConversionComponent implements WireEmitter, WireReceiver, ConfigurableComponent {

private static final Logger logger = LoggerFactory.getLogger(TypeConversionComponent.class);

private WireHelperService wireHelperService;
private WireSupport wireSupport;

private TypeConversionComponentOptions options;

public void bindWireHelperService(final WireHelperService wireHelperService) {
this.wireHelperService = wireHelperService;
}

public void unbindWireHelperService(final WireHelperService wireHelperService) {
if (this.wireHelperService == wireHelperService) {
this.wireHelperService = null;
}
}

public void activate(final Map<String, Object> properties, ComponentContext componentContext) {
this.wireSupport = this.wireHelperService.newWireSupport(this,
(ServiceReference<WireComponent>) componentContext.getServiceReference());
updated(properties);
}

public void updated(final Map<String, Object> properties) {
try {
this.options = new TypeConversionComponentOptions(properties);
} catch (Exception e) {
logger.warn("Invalid configuration, please review", e);
this.options = null;
}
}

public void deactivate() {
}

@Override
public Object polled(Wire wire) {
return wireSupport.polled(wire);
}

@Override
public void consumersConnected(Wire[] wires) {
wireSupport.consumersConnected(wires);
}

@Override
public void updated(Wire wire, Object value) {
wireSupport.updated(wire, value);
}

@Override
public void producersConnected(Wire[] wires) {
wireSupport.producersConnected(wires);
}

@Override
public void onWireReceive(WireEnvelope wireEnvelope) {
if (options == null) {
logger.warn("Invalid configuration, please review");
}
final List<WireRecord> inputRecords = wireEnvelope.getRecords();
final List<WireRecord> records = new ArrayList<>(inputRecords.size());
for (final WireRecord record : inputRecords) {
records.add(processRecord(record));
}
this.wireSupport.emit(records);
}

private WireRecord processRecord(WireRecord record) {
final Map<String, TypedValue<?>> inputProperties = record.getProperties();
final Map<String, TypedValue<?>> outProperties = new HashMap<>();
if (this.options.shouldEmitReceivedProperties()) {
outProperties.putAll(inputProperties);
}
for (TypeConversionEntry e : this.options.getEntries()) {
final String propertyName = e.getPropertyName();
final TypedValue<?> typedValue = inputProperties.get(propertyName);
if (typedValue == null) {
continue;
}
final Object value = typedValue.getValue();
if (!(value instanceof Number)) {
logger.warn("Invalid property value: {}={}", propertyName, typedValue);
continue;
}
if (e.getType().equals(DataType.DOUBLE)) {
outProperties.put(propertyName,
TypedValues.newDoubleValue(((Number) value).doubleValue()));
} else if (e.getType().equals(DataType.FLOAT)) {
outProperties.put(propertyName,
TypedValues.newFloatValue(((Number) value).floatValue()));
} else if (e.getType().equals(DataType.INTEGER)) {
outProperties.put(propertyName,
TypedValues.newFloatValue(((Number) value).intValue()));
} else if (e.getType().equals(DataType.LONG)) {
outProperties.put(propertyName,
TypedValues.newFloatValue(((Number) value).longValue()));
}
}
return new WireRecord(outProperties);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/*******************************************************************************
* Copyright (c) 2024 Eurotech and/or its affiliates and others
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Eurotech
*******************************************************************************/

package org.eclipse.kura.example.wire.math.singleport.typeconversion;

import java.util.List;
import java.util.Map;

public class TypeConversionComponentOptions {

private static final String CONFIGURATION_PROP_NAME = "channels";
private static final String EMIT_RECEIVED_PROPERTIES_PROP_NAME = "emit.received.properties";

private static final boolean EMIT_RECEIVED_PROPERTIES_DEFAULT = false;
private static final String CONFIGURATION_DEFAULT = "";

private List<TypeConversionEntry> entries;
private boolean emitReceivedProperties;

public TypeConversionComponentOptions(final Map<String, Object> properties) {
this.emitReceivedProperties = getSafe(properties.get(EMIT_RECEIVED_PROPERTIES_PROP_NAME),
EMIT_RECEIVED_PROPERTIES_DEFAULT);
this.entries = TypeConversionEntry
.parseAll(getSafe(properties.get(CONFIGURATION_PROP_NAME), CONFIGURATION_DEFAULT));
}

public List<TypeConversionEntry> getEntries() {
return entries;
}

public boolean shouldEmitReceivedProperties() {
return emitReceivedProperties;
}

@SuppressWarnings("unchecked")
private <T> T getSafe(Object o, T defaultValue) {
if (defaultValue.getClass().isInstance(o)) {
return (T) o;
}
return defaultValue;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
/*******************************************************************************
* Copyright (c) 2024 Eurotech and/or its affiliates and others
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Eurotech
*******************************************************************************/

package org.eclipse.kura.example.wire.math.singleport.typeconversion;

import static java.util.Objects.requireNonNull;

import java.util.List;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

import org.eclipse.kura.type.DataType;

public class TypeConversionEntry {

private static final Pattern ENTRY_DELIMITER = Pattern.compile("[;\n]");
private static final Pattern FIELD_DELIMITER = Pattern.compile("[|]");

private final String propertyName;
private final DataType type;

public TypeConversionEntry(final String propertyName, final DataType type) {
this.propertyName = propertyName;
this.type = type;
}

public String getPropertyName() {
return propertyName;
}

public DataType getType() {
return type;
}

private static void clear(String[] entryArray) {
entryArray[0] = null;
entryArray[1] = null;
}

private static TypeConversionEntry parse(final String entryString, final String[] tempArray) {
try {
clear(tempArray);

FIELD_DELIMITER.splitAsStream(entryString).map(String::trim).filter(s -> !s.isEmpty())
.toArray(i -> tempArray);

requireNonNull(tempArray[0]);
requireNonNull(tempArray[1]);
String typeTemp = tempArray[1];
DataType dataType;

if ("double".equalsIgnoreCase(typeTemp)) {
dataType = DataType.DOUBLE;
} else if ("float".equalsIgnoreCase(typeTemp)) {
dataType = DataType.FLOAT;
} else if ("integer".equalsIgnoreCase(typeTemp)) {
dataType = DataType.INTEGER;
} else if ("long".equalsIgnoreCase(typeTemp)) {
dataType = DataType.LONG;
} else {
throw new IllegalArgumentException("Unsupported type");
}

return new TypeConversionEntry(tempArray[0], dataType);
} catch (Exception e) {
throw new IllegalArgumentException("Invalid entry: " + entryString);
}
}

public static List<TypeConversionEntry> parseAll(String configuration) {
final String[] tempArray = new String[3];
return ENTRY_DELIMITER.splitAsStream(configuration).map(String::trim).filter(s -> !s.isEmpty())
.map(entryString -> parse(entryString, tempArray)).collect(Collectors.toList());
}
}