Skip to content

Commit

Permalink
Merge branch 'release/3.35.0'
Browse files Browse the repository at this point in the history
  • Loading branch information
msqr committed Oct 24, 2024
2 parents 863d297 + de2b899 commit 3024623
Show file tree
Hide file tree
Showing 34 changed files with 3,123 additions and 354 deletions.
2 changes: 1 addition & 1 deletion solarnet/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ subprojects {
jsonSchemaValidatorVersion = '1.3.2'
myBatisStarterVersion = '3.0.3'
saxonVersion = '12.4'
snCommonVersion = '3.24.1'
snCommonVersion = '3.25.0'
snCommonExprSpelVersion = '3.1.0'
snCommonMqttVersion = '5.0.0'
snCommonMqttNettyVersion = '4.0.2'
Expand Down
2 changes: 1 addition & 1 deletion solarnet/cloud-integrations/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ dependencyManagement {
}

description = 'SolarNet: Cloud Integrations'
version = '1.4.0'
version = '1.5.0'

base {
archivesName = 'solarnet-cloud-integrations'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,18 @@
public interface CloudDatumStreamService
extends Identity<String>, SettingSpecifierProvider, LocalizedServiceInfoProvider {

/**
* A standard setting for either a map or comma-delimited mapping list of
* data value references to associated source ID values.
*
* <p>
* This setting is intended to be used by cloud services that can provide
* multiple SolarNetwork datum streams, as a way to map each cloud device to
* a SolarNetwork source.
* </p>
*/
String SOURCE_ID_MAP_SETTING = "sourceIdMap";

/**
* Get a localized collection of the available data value filter criteria.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,25 +29,34 @@
import java.math.BigDecimal;
import java.util.Collection;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import org.springframework.security.crypto.encrypt.TextEncryptor;
import com.fasterxml.jackson.databind.JsonNode;
import net.solarnetwork.central.biz.UserEventAppenderBiz;
import net.solarnetwork.central.c2c.biz.CloudDatumStreamService;
import net.solarnetwork.central.c2c.biz.CloudIntegrationsExpressionService;
import net.solarnetwork.central.c2c.dao.CloudDatumStreamConfigurationDao;
import net.solarnetwork.central.c2c.dao.CloudDatumStreamMappingConfigurationDao;
import net.solarnetwork.central.c2c.dao.CloudDatumStreamPropertyConfigurationDao;
import net.solarnetwork.central.c2c.dao.CloudIntegrationConfigurationDao;
import net.solarnetwork.central.c2c.domain.BasicCloudDatumStreamLocalizedServiceInfo;
import net.solarnetwork.central.c2c.domain.CloudDatumStreamPropertyConfiguration;
import net.solarnetwork.codec.JsonUtils;
import net.solarnetwork.domain.LocalizedServiceInfo;
import net.solarnetwork.domain.datum.DatumSamplesExpressionRoot;
import net.solarnetwork.domain.datum.DatumSamplesType;
import net.solarnetwork.domain.datum.MutableDatum;
import net.solarnetwork.service.IdentifiableConfiguration;
import net.solarnetwork.settings.SettingSpecifier;
import net.solarnetwork.util.IntRange;
import net.solarnetwork.util.StringUtils;

/**
* Base implementation of {@link CloudDatumStreamService}.
*
* @author matt
* @version 1.2
* @version 1.4
*/
public abstract class BaseCloudDatumStreamService extends BaseCloudIntegrationsIdentifiableService
implements CloudDatumStreamService {
Expand Down Expand Up @@ -111,6 +120,55 @@ public BaseCloudDatumStreamService(String serviceIdentifier, String displayName,
"datumStreamPropertyDao");
}

@Override
public LocalizedServiceInfo getLocalizedServiceInfo(Locale locale) {
return new BasicCloudDatumStreamLocalizedServiceInfo(
super.getLocalizedServiceInfo(locale != null ? locale : Locale.getDefault()),
getSettingSpecifiers(), requiresPolling(), supportedPlaceholders(),
supportedDataValueWildcardIdentifierLevels(), dataValueIdentifierLevelsSourceIdRange());
}

/**
* Get the polling requirement.
*
* @return {@literal true} if polling for data is required
* @since 1.3
*/
protected boolean requiresPolling() {
return true;
}

/**
* Get the supported placeholder keys.
*
* @return the supported placeholder key, or {@literal null}
* @since 1.3
*/
protected Iterable<String> supportedPlaceholders() {
return null;
}

/**
* Get the supported data value wildcard levels.
*
* @return the supported data value wildcard levels, or {@literal null}
* @since 1.3
*/
protected Iterable<Integer> supportedDataValueWildcardIdentifierLevels() {
return null;
}

/**
* Get the supported data value identifier levels source ID range.
*
* @return the supported data value identifier levels source ID range, or
* {@literal null}
* @since 1.4
*/
protected IntRange dataValueIdentifierLevelsSourceIdRange() {
return null;
}

/**
* Evaluate a set of property expressions on a set of datum.
*
Expand Down Expand Up @@ -172,4 +230,85 @@ public void evaulateExpressions(Collection<CloudDatumStreamPropertyConfiguration
}
}

/**
* Populate a non-empty JSON field value onto a map.
*
* @param node
* the JSON node to read the field from
* @param fieldName
* the name of the JSON field to read
* @param key
* the map key to populate if the field is a non-empty string
* @param map
* the map to populate with the non-empty string
*/
public static void populateNonEmptyValue(JsonNode node, String fieldName, String key,
Map<String, Object> map) {
String s = JsonUtils.parseNonEmptyStringAttribute(node, fieldName);
if ( s != null ) {
map.put(key, s);
}
}

/**
* Resolve a mapping from a setting on a configuration.
*
* @param configuration
* the configuration to extract the mapping from
* @param key
* the service property key to extract
* @return the mapping, or {@literal null}
* @since 1.4
*/
@SuppressWarnings("unchecked")
public static Map<String, String> servicePropertyStringMap(IdentifiableConfiguration configuration,
String key) {
if ( configuration == null ) {
return null;
}
final Object sourceIdMap = configuration.serviceProperty(SOURCE_ID_MAP_SETTING, Object.class);
final Map<String, String> componentSourceIdMapping;
if ( sourceIdMap instanceof Map<?, ?> ) {
componentSourceIdMapping = (Map<String, String>) sourceIdMap;
} else if ( sourceIdMap != null ) {
componentSourceIdMapping = StringUtils.commaDelimitedStringToMap(sourceIdMap.toString());
} else {
componentSourceIdMapping = null;
}
return componentSourceIdMapping;
}

/**
* Parse a JSON datum property value.
*
* @param val
* the JSON value to parse as a datum property value.
* @param propType
* the desired datum property type
* @return the value, or {@literal null}
*/
public static Object parseJsonDatumPropertyValue(JsonNode val, DatumSamplesType propType) {
return switch (propType) {
case Accumulating, Instantaneous -> {
// convert to number
if ( val.isBigDecimal() ) {
yield val.decimalValue();
} else if ( val.isFloat() ) {
yield val.floatValue();
} else if ( val.isDouble() ) {
yield val.doubleValue();
} else if ( val.isBigInteger() ) {
yield val.bigIntegerValue();
} else if ( val.isLong() ) {
yield val.longValue();
} else if ( val.isFloat() ) {
yield val.floatValue();
} else {
yield narrow(parseNumber(val.asText(), BigDecimal.class), 2);
}
}
case Status, Tag -> val.asText();
};
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,9 @@
import static net.solarnetwork.central.c2c.domain.CloudDataValue.intermediateDataValue;
import static net.solarnetwork.central.c2c.domain.CloudIntegrationsConfigurationEntity.resolvePlaceholders;
import static net.solarnetwork.central.security.AuthorizationException.requireNonNullObject;
import static net.solarnetwork.util.NumberUtils.narrow;
import static net.solarnetwork.util.NumberUtils.parseNumber;
import static net.solarnetwork.util.ObjectUtils.requireNonNullArgument;
import static org.springframework.util.StringUtils.collectionToCommaDelimitedString;
import static org.springframework.web.util.UriComponentsBuilder.fromUri;
import java.math.BigDecimal;
import java.time.Instant;
import java.time.format.DateTimeParseException;
import java.time.temporal.ChronoUnit;
Expand Down Expand Up @@ -137,7 +134,7 @@
* }}</pre>
*
* @author matt
* @version 1.7
* @version 1.8
*/
public class LocusEnergyCloudDatumStreamService extends BaseOAuth2ClientCloudDatumStreamService {

Expand Down Expand Up @@ -168,6 +165,14 @@ public class LocusEnergyCloudDatumStreamService extends BaseOAuth2ClientCloudDat
SETTINGS = List.of(granularitySpec);
}

/**
* The supported placeholder keys.
*
* @since 1.8
*/
public static final List<String> SUPPORTED_PLACEHOLDERS = List.of(SITE_ID_FILTER,
COMPONENT_ID_FILTER);

private AsyncTaskExecutor executor;

/**
Expand Down Expand Up @@ -216,6 +221,11 @@ public LocusEnergyCloudDatumStreamService(AsyncTaskExecutor executor,
this.executor = requireNonNullArgument(executor, "executor");
}

@Override
protected Iterable<String> supportedPlaceholders() {
return SUPPORTED_PLACEHOLDERS;
}

@Override
public Iterable<LocalizedServiceInfo> dataValueFilters(Locale locale) {
MessageSource ms = requireNonNullArgument(getMessageSource(), "messageSource");
Expand Down Expand Up @@ -697,27 +707,7 @@ private CloudDatumStreamQueryResult queryForDatum(CloudDatumStreamConfiguration
JsonNode val = datumValues.get(fieldName);
if ( val != null ) {
DatumSamplesType propType = property.getPropertyType();
Object propVal = switch (propType) {
case Accumulating, Instantaneous -> {
// convert to number
if ( val.isBigDecimal() ) {
yield val.decimalValue();
} else if ( val.isFloat() ) {
yield val.floatValue();
} else if ( val.isDouble() ) {
yield val.doubleValue();
} else if ( val.isBigInteger() ) {
yield val.bigIntegerValue();
} else if ( val.isLong() ) {
yield val.longValue();
} else if ( val.isFloat() ) {
yield val.floatValue();
} else {
yield narrow(parseNumber(val.asText(), BigDecimal.class), 2);
}
}
case Status, Tag -> val.asText();
};
Object propVal = parseJsonDatumPropertyValue(val, propType);
propVal = property.applyValueTransforms(propVal);
samples.putSampleValue(propType, property.getPropertyName(), propVal);
}
Expand Down
Loading

0 comments on commit 3024623

Please sign in to comment.