Skip to content

Commit

Permalink
Updated dependencies, added jewelcli command line parsing
Browse files Browse the repository at this point in the history
  • Loading branch information
retrodaredevil committed Dec 12, 2020
1 parent 8bbc33a commit e7e7ecf
Show file tree
Hide file tree
Showing 8 changed files with 101 additions and 46 deletions.
14 changes: 7 additions & 7 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,16 @@ description = """SolarThing"""

buildscript {
ext {
junit5Version = '5.7.0'
junit5Version = '5.7.0' // https://junit.org/junit5/
junit5PlatformVersion = '1.7.0'

slf4jVersion = '1.7.30'
log4jVersion = '2.13.3'
jacksonVersion = '2.11.3'
slf4jVersion = '1.7.30' // http://www.slf4j.org/download.html
log4jVersion = '2.14.0' // https://logging.apache.org/log4j/2.x/javadoc.html
jacksonVersion = '2.12.0' // https://github.com/FasterXML/jackson-databind/releases
// retrofit is why we need allow-opens. context: https://stackoverflow.com/questions/60915381/retrofit2-maven-project-illegal-reflective-access-warning // https://github.com/square/retrofit/issues/3341
retrofitVersion = "2.9.0"
shadowVersion = '6.1.0'
ioLibVersion = '2.1.0'
retrofitVersion = "2.9.0" // https://github.com/square/retrofit/releases
shadowVersion = '6.1.0' // https://github.com/johnrengelman/shadow/releases
ioLibVersion = '2.1.0' // https://github.com/retrodaredevil/io-lib/releases
}
}
allprojects {
Expand Down
3 changes: 3 additions & 0 deletions client/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ dependencies {
implementation 'net.bis5.mattermost4j:mattermost4j-core:0.22.1'

implementation 'com.slack.api:slack-api-client:1.1.5'

// https://mvnrepository.com/artifact/com.lexicalscope.jewelcli/jewelcli
implementation group: 'com.lexicalscope.jewelcli', name: 'jewelcli', version: '0.8.9'
}

shadowJar {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package me.retrodaredevil.solarthing.program;

import com.lexicalscope.jewel.cli.Option;
import com.lexicalscope.jewel.cli.Unparsed;

import java.io.File;
import java.util.List;

public interface CommandOptions {
@Option(shortName = "h", longName = "help", helpRequest = true)
boolean isHelp();
@Option(longName = "base", defaultToNull = true, description = "Use alone to define the base configuration file")
File getBaseConfigFile();

@Option(longName = "couchdb-setup", defaultToNull = true, description = "Use alone to define the CouchDB configuration file and launch the CouchDB setup program")
File getCouchDbSetupFile();

@Option(longName = "from", defaultToNull = true, description = "Use with --base and a pvoutput-upload program type")
String getPVOutputFromDate();
@Option(longName = "to", defaultToNull = true, description = "Use with --base and a pvoutput-upload program type")
String getPVOutputToDate();

@Unparsed(name = "LEGACY ARGUMENTS")
List<String> getLegacyOptions();
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

import com.fasterxml.jackson.databind.InjectableValues;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.lexicalscope.jewel.cli.ArgumentValidationException;
import com.lexicalscope.jewel.cli.Cli;
import com.lexicalscope.jewel.cli.CliFactory;
import com.lexicalscope.jewel.cli.HelpRequestedException;
import me.retrodaredevil.couchdb.CouchProperties;
import me.retrodaredevil.couchdb.EktorpUtil;
import me.retrodaredevil.io.IOBundle;
Expand Down Expand Up @@ -162,8 +166,8 @@ public static CouchDbQueryHandler createCouchDbQueryHandler(CouchProperties couc
return new CouchDbQueryHandler(new StdCouchDbConnector(SolarThingConstants.SOLAR_STATUS_UNIQUE_NAME, instance));
}

public static int doMainCommand(String[] args) {
File baseConfigFile = new File(args[0]);
public static int doMainCommand(CommandOptions commandOptions, File baseConfigFile) {
LOGGER.info("Using base configuration file: " + baseConfigFile);
final FileReader fileReader;
try {
fileReader = new FileReader(baseConfigFile);
Expand All @@ -175,7 +179,7 @@ public static int doMainCommand(String[] args) {
try {
options = MAPPER.readValue(fileReader, ProgramOptions.class);
} catch (IOException e) {
LOGGER.error(SolarThingConstants.SUMMARY_MARKER, "(Fatal)Error while parsing ProgramOptions. args=" + Arrays.toString(args), e);
LOGGER.error(SolarThingConstants.SUMMARY_MARKER, "(Fatal)Error while parsing ProgramOptions.", e);
return 1;
}
File dataDirectory = new File(".data");
Expand All @@ -192,7 +196,7 @@ public static int doMainCommand(String[] args) {
} else if(programType == ProgramType.ROVER_SETUP){
return RoverMain.connectRoverSetup((RoverSetupProgramOptions) options);
} else if(programType == ProgramType.PVOUTPUT_UPLOAD){
return PVOutputUploadMain.startPVOutputUpload((PVOutputUploadProgramOptions) options, Arrays.copyOfRange(args, 1, args.length), dataDirectory);
return PVOutputUploadMain.startPVOutputUpload((PVOutputUploadProgramOptions) options, commandOptions, dataDirectory);
} else if(programType == ProgramType.MESSAGE_SENDER){
return MessageSenderMain.startMessageSender((MessageSenderProgramOptions) options);
} else if(programType == ProgramType.REQUEST) {
Expand All @@ -210,39 +214,45 @@ public static int doMainCommand(String[] args) {
public static int doMain(String[] args){
LOGGER.info(SolarThingConstants.SUMMARY_MARKER, "[LOG] Beginning main. Jar: " + JarUtil.getJarFileName());
System.out.println("[stdout] Beginning main");
if(args.length < 1){
System.err.println("Usage: <java -jar ...> main {base config file}");
LOGGER.error(SolarThingConstants.SUMMARY_MARKER, "(Fatal)Incorrect args (Insufficient arguments)");
Cli<CommandOptions> cli = CliFactory.createCli(CommandOptions.class);
final CommandOptions commandOptions;
try {
commandOptions = cli.parseArguments(args);
} catch (ArgumentValidationException ex) {
System.out.println(cli.getHelpMessage());
if (ex instanceof HelpRequestedException) {
return 0;
}
LOGGER.error(SolarThingConstants.SUMMARY_MARKER, ex.getMessage());
LOGGER.error(SolarThingConstants.SUMMARY_MARKER, "(Fatal)Incorrect args");
return 1;
}
if (args.length >= 2) {
String command = args[0];
if ("main".equals(command)) {
return doMainCommand(Arrays.copyOfRange(args, 1, args.length));
} else if ("couchdb".equals(command)) {
final DatabaseConfig config;
try {
config = MAPPER.readValue(new File(args[1]), DatabaseConfig.class);
} catch (IOException e) {
e.printStackTrace();
System.out.println("Problem reading CouchDB database settings file.");
return 1;
}
DatabaseSettings settings = config.getSettings();
if (!(settings instanceof CouchDbDatabaseSettings)) {
System.out.println("Must be CouchDB database settings!");
return 1;
}
return CouchDbSetupMain.doCouchDbSetupMain((CouchDbDatabaseSettings) settings);
} else {
System.err.println("Unknown command " + command);
LOGGER.error(SolarThingConstants.SUMMARY_MARKER, "(Fatal)Incorrect args (Unknown command)");
if (commandOptions.getBaseConfigFile() != null) {
return doMainCommand(commandOptions, commandOptions.getBaseConfigFile());
}
if (commandOptions.getCouchDbSetupFile() != null) {
final DatabaseConfig config;
try {
config = MAPPER.readValue(commandOptions.getCouchDbSetupFile(), DatabaseConfig.class);
} catch (IOException e) {
e.printStackTrace();
System.err.println("Problem reading CouchDB database settings file.");
return 1;
}
DatabaseSettings settings = config.getSettings();
if (!(settings instanceof CouchDbDatabaseSettings)) {
System.err.println("Must be CouchDB database settings!");
return 1;
}
} else {
System.err.println("You should do java -jar solarthing.jar main base.json instead! Future versions will require at least 2 arguments!");
return doMainCommand(args);
return CouchDbSetupMain.doCouchDbSetupMain((CouchDbDatabaseSettings) settings);
}
List<String> legacyArguments = commandOptions.getLegacyOptions();
if (legacyArguments.isEmpty()) {
System.err.println(cli.getHelpMessage());
return 1;
}
System.out.println("Using legacy arguments! Please use --base instead! (If you are running this using ./run.sh, this will be automatically fixed in a future update) (ignore this).");
return doMainCommand(commandOptions, new File(legacyArguments.get(0)));
}

public static void main(String[] args) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import me.retrodaredevil.solarthing.packets.collection.parsing.*;
import me.retrodaredevil.solarthing.packets.handling.PacketHandleException;
import me.retrodaredevil.solarthing.packets.instance.InstancePacket;
import me.retrodaredevil.solarthing.program.CommandOptions;
import me.retrodaredevil.solarthing.program.DatabaseConfig;
import me.retrodaredevil.solarthing.program.PacketUtil;
import me.retrodaredevil.solarthing.program.SolarMain;
Expand Down Expand Up @@ -59,7 +60,7 @@ public class PVOutputUploadMain {


@SuppressWarnings("SameReturnValue")
public static int startPVOutputUpload(PVOutputUploadProgramOptions options, String[] extraArgs, File dataDirectory){
public static int startPVOutputUpload(PVOutputUploadProgramOptions options, CommandOptions commandOptions, File dataDirectory){
LOGGER.info(SolarThingConstants.SUMMARY_MARKER, "Starting PV Output upload program");
TimeZone timeZone = options.getTimeZone();
LOGGER.info(SolarThingConstants.SUMMARY_MARKER, "Using time zone: {}", timeZone.getDisplayName());
Expand Down Expand Up @@ -88,10 +89,11 @@ public static int startPVOutputUpload(PVOutputUploadProgramOptions options, Stri
Retrofit retrofit = PVOutputRetrofitUtil.defaultBuilder().client(client).build();
PVOutputService service = retrofit.create(PVOutputService.class);
PVOutputHandler handler = new PVOutputHandler(timeZone, options.getRequiredIdentifierMap(), options.getVoltageIdentifierFragmentMatcher(), options.getTemperatureIdentifierFragmentMatcher());
if(extraArgs.length >= 2) {

String fromDateString = commandOptions.getPVOutputFromDate();
String toDateString = commandOptions.getPVOutputToDate();
if(fromDateString != null && toDateString != null) {
System.out.println("Starting range upload");
String fromDateString = extraArgs[0];
String toDateString = extraArgs[1];
final SimpleDate fromDate;
final SimpleDate toDate;
try {
Expand All @@ -106,8 +108,8 @@ public static int startPVOutputUpload(PVOutputUploadProgramOptions options, Stri
fromDate, toDate,
options, queryHandler, statusParser, handler, service, options.getTimeZone()
);
} else if (extraArgs.length == 1) {
LOGGER.error(SolarThingConstants.SUMMARY_MARKER, "(Fatal)You need 2 arguments for range upload! You have 1!");
} else if ((fromDateString == null) != (toDateString == null)) {
LOGGER.error(SolarThingConstants.SUMMARY_MARKER, "(Fatal)You need to define both from and to, or define neither to do the normal PVOutput program!");
return 1;
}
AnalyticsManager analyticsManager = new AnalyticsManager(options.isAnalyticsEnabled(), dataDirectory);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ public static MultiPacketConverter createFrom(ObjectMapper baseMapper, Class<? e
}
public static MultiPacketConverter createFrom(ObjectMapper baseMapper, Collection<? extends Class<? extends DocumentedPacket>> packetClasses) {
ObjectMapper mapper = baseMapper.copy();
// mapper.setSubtypeResolver(baseMapper.getSubtypeResolver().copy()); // workaround if we use 2.11.1
mapper.getSubtypeResolver().registerSubtypes(Collections.unmodifiableCollection(packetClasses));
return new MultiPacketConverter(mapper);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ public final class JacksonUtil {
private JacksonUtil(){ throw new UnsupportedOperationException(); }

public static ObjectMapper defaultMapper(ObjectMapper mapper){
// We will eventually use a JsonMapper.Builder (JsonMapper.builder()) to create this, but there are some things that require the direct configuration of an ObjectMapper
mapper.setConfig(
mapper.getDeserializationConfig()
.with(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_USING_DEFAULT_VALUE)
Expand Down
15 changes: 15 additions & 0 deletions other/docs/history.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,12 @@ have the ability to have multiple instances uploading packets to a single databa
* Google Analytics were added
* Did a huge rebase to remove all solarthing.jar files, restructured program directory
* Added ability to read from DS18B20 temperature sensors
* Added GraphQL program
* Added message sending abilities for push notifications (Mattermost and eventually Slack)
* Added automation program that can help automate sending of commands and eventually became the base program
for the message-sender program and other general 'actions'
* Added home assistant upload action
* Added ability to send data to Solcast along with GraphQL queries for Solcast


### Moving from Gson to Jackson
Expand All @@ -60,6 +66,15 @@ to Jackson altogether as explained above.
Currently the configuration is very easy to change. I can swap out what configuration I'm using easily and can
use the same CouchDB or InfluxDB configuration on multiple devices running SolarThing.

### Queries for Grafana
When support for InfluxDB was added in late 2019, it became easy to make a Grafana dashboard to display data.
However, this was not perfect. I had to maintain two different databases. CouchDB for nicely structured JSON
data, and InfluxDB for easy to query data. In 2020, I decided I wanted to be able to query data from CouchDB
without InfluxDB. After some searching, I found the graphql-datasource for Grafana. It was perfect. I did some
research on how to do a code first approach for a GraphQL program and ran into graphql-spqr. Now my schema was
already created without additional setup because of how awesome Java is. Now I could query CouchDB from Grafana
and even add additional data calculations that weren't in the packets to begin with.

### Legacy
[The perl script](../legacy/helloworld.pl) is a legacy program. It was the program that started solarthing.
After learning perl for a day. I went straight back to Java, which I am more familiar with.

0 comments on commit e7e7ecf

Please sign in to comment.