diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5f354ae..543aa57 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -7,7 +7,6 @@ on: - '**/*.md' branches: - master - - release/** pull_request: paths-ignore: - '*.md' @@ -45,9 +44,21 @@ jobs: name: codecov-umbrella fail_ci_if_error: true - - name: Release - if: github.event_name != 'pull_request' && startsWith(github.ref, 'refs/tags/v') - env: - GITHUB_PKG_USERNAME: javatmc - GITHUB_PKG_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: ./gradlew publish + # - name: Release + # if: github.event_name != 'pull_request' && startsWith(github.ref, 'refs/tags/v') + # env: + # GITHUB_PKG_USERNAME: javatmc + # GITHUB_PKG_TOKEN: ${{ secrets.GITHUB_TOKEN }} + # run: ./gradlew publish + + lint: + name: Lint + runs-on: ubuntu-latest + container: + image: openjdk:8 + steps: + - name: Checkout + uses: actions/checkout@v2 + + - name: Checkstyle + run: ./gradlew checkstyleMain --info diff --git a/build.gradle b/build.gradle index c425f36..28b2d1a 100644 --- a/build.gradle +++ b/build.gradle @@ -11,6 +11,7 @@ subprojects { apply plugin: 'java' apply plugin: 'maven-publish' apply plugin: 'jacoco' + apply plugin: 'checkstyle' repositories { jcenter() @@ -46,6 +47,14 @@ subprojects { } } + checkstyle { + toolVersion '8.39' + sourceSets = [] + configFile = file("${rootDir}/config/checkstyle/checkstyle.xml") + ignoreFailures = false + maxWarnings = 0 + } + tasks.withType(JavaCompile) { options.encoding = 'UTF-8' } diff --git a/codecov.yml b/codecov.yml index 7061475..08c8263 100644 --- a/codecov.yml +++ b/codecov.yml @@ -9,4 +9,4 @@ coverage: base: auto flags: - unittests - if_ci_failed: error \ No newline at end of file + if_ci_failed: error diff --git a/config/checkstyle/checkstyle-suppressions.xml b/config/checkstyle/checkstyle-suppressions.xml new file mode 100644 index 0000000..0595d59 --- /dev/null +++ b/config/checkstyle/checkstyle-suppressions.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + diff --git a/config/checkstyle/checkstyle.xml b/config/checkstyle/checkstyle.xml new file mode 100644 index 0000000..37c4bbf --- /dev/null +++ b/config/checkstyle/checkstyle.xml @@ -0,0 +1,361 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/measure-apps/src/main/java/org/jtmc/app/BatterySimulator.java b/measure-apps/src/main/java/org/jtmc/app/BatterySimulator.java index 8ec9cf6..7fa3e93 100644 --- a/measure-apps/src/main/java/org/jtmc/app/BatterySimulator.java +++ b/measure-apps/src/main/java/org/jtmc/app/BatterySimulator.java @@ -1,8 +1,9 @@ package org.jtmc.app; /** - * BatterySimulator + * BatterySimulator is an application used to simulate battery discharge over + * time. */ public class BatterySimulator { - -} \ No newline at end of file + +} diff --git a/measure-apps/src/main/java/org/jtmc/app/BodePlotter.java b/measure-apps/src/main/java/org/jtmc/app/BodePlotter.java index 8080bda..8e81ccf 100644 --- a/measure-apps/src/main/java/org/jtmc/app/BodePlotter.java +++ b/measure-apps/src/main/java/org/jtmc/app/BodePlotter.java @@ -1,20 +1,21 @@ package org.jtmc.app; /** - * BodePlotter + * BodePlotter is an application used to plot the amplitude and phase difference + * when a device under test is given a stimuli in a range of frequencies. */ public class BodePlotter { - float amplitude; + float amplitude; - float offset; + float offset; - float load; + float load; - float startFrequency; + float startFrequency; - float stopFrequency; + float stopFrequency; - int points; + int points; -} \ No newline at end of file +} diff --git a/measure-apps/src/main/java/org/jtmc/app/DCEfficiency.java b/measure-apps/src/main/java/org/jtmc/app/DCEfficiency.java index 79b7d8d..fd5dc0b 100644 --- a/measure-apps/src/main/java/org/jtmc/app/DCEfficiency.java +++ b/measure-apps/src/main/java/org/jtmc/app/DCEfficiency.java @@ -1,16 +1,17 @@ package org.jtmc.app; /** - * DCEfficiency + * DCEfficiency is an application to measure the efficiency of a DC Power supply + * over the given current range at the set voltage. */ public class DCEfficiency { - double voltage; + double voltage; - double startCurrent; + double startCurrent; - double stopCurrent; + double stopCurrent; - int points; - -} \ No newline at end of file + int points; + +} diff --git a/measure-apps/src/main/java/org/jtmc/app/IVPlotter.java b/measure-apps/src/main/java/org/jtmc/app/IVPlotter.java index 7ae7b28..5104188 100644 --- a/measure-apps/src/main/java/org/jtmc/app/IVPlotter.java +++ b/measure-apps/src/main/java/org/jtmc/app/IVPlotter.java @@ -1,9 +1,9 @@ package org.jtmc.app; /** - * IVPlotter + * IVPlotter is an application used to plot the current under the given voltage + * range. */ public class IVPlotter { - -} \ No newline at end of file +} diff --git a/measure-cli/src/main/java/org/jtmc/cli/JMeasureCLI.java b/measure-cli/src/main/java/org/jtmc/cli/JMeasureCLI.java index f1ab7d0..124ed65 100644 --- a/measure-cli/src/main/java/org/jtmc/cli/JMeasureCLI.java +++ b/measure-cli/src/main/java/org/jtmc/cli/JMeasureCLI.java @@ -2,20 +2,22 @@ import org.jtmc.cli.lxi.LXIDiscover; import org.jtmc.cli.scpi.SCPISend; - import picocli.CommandLine; import picocli.CommandLine.Command; +/** + * JMeasureCLI provides command line access to instruments. + */ @Command( - subcommands = { - LXIDiscover.class, - SCPISend.class - } + subcommands = { + LXIDiscover.class, + SCPISend.class + } ) public class JMeasureCLI { - public static void main(String[] args) { - int exitCode = new CommandLine(new JMeasureCLI()).execute(args); - System.exit(exitCode); - } -} \ No newline at end of file + public static void main(String[] args) { + int exitCode = new CommandLine(new JMeasureCLI()).execute(args); + System.exit(exitCode); + } +} diff --git a/measure-cli/src/main/java/org/jtmc/cli/lxi/LXIDiscover.java b/measure-cli/src/main/java/org/jtmc/cli/lxi/LXIDiscover.java index 409de22..0f66933 100644 --- a/measure-cli/src/main/java/org/jtmc/cli/lxi/LXIDiscover.java +++ b/measure-cli/src/main/java/org/jtmc/cli/lxi/LXIDiscover.java @@ -1,5 +1,6 @@ package org.jtmc.cli.lxi; +import com.fasterxml.jackson.databind.ObjectMapper; import java.net.NetworkInterface; import java.util.ArrayList; import java.util.Collections; @@ -11,158 +12,196 @@ import java.util.concurrent.Callable; import java.util.stream.Collectors; import java.util.stream.Stream; - -import com.fasterxml.jackson.databind.ObjectMapper; - import org.jtmc.core.lxi.LXIDiscovery; -import org.jtmc.core.lxi.LXIDiscovery.LXIInstrumentEndpoint; +import org.jtmc.core.lxi.LXIInstrumentEndpoint; import org.jtmc.core.lxi.mdns.MDNSDiscovery; import org.jtmc.core.lxi.mdns.MDNSServiceType; import org.jtmc.core.lxi.vxi11.VXI11Discovery; - import picocli.CommandLine.ArgGroup; import picocli.CommandLine.Command; import picocli.CommandLine.Option; /** - * LXICommand + * LXICommand is used to discover instruments on a network. */ @Command(name = "lxi-discover") public class LXIDiscover implements Callable { - @Option( - names = { "--interface", "-i" }, - description = "Network interface to discover on, if not specified then it will broadcast on all interfaces", - required = false) - public NetworkInterface networkInterface; - - @ArgGroup(exclusive = true) - public DiscoverySettings discoverySettings; - - @Option(names = {"--json"}, description = "Outputs a JSON formatted instrument array", defaultValue = "false") - public boolean jsonOutput; - - @Option(names = {"--no-warnings"}, description = "Disables warnings in the output", defaultValue = "false") - public boolean noWarnings; - - public LXIDiscover() { - this.discoverySettings = new DiscoverySettings(); - this.discoverySettings.vxi11Settings = new VXI11Settings(); - } - - public static class DiscoverySettings { - @ArgGroup(exclusive = false) - public VXI11Settings vxi11Settings; - - @ArgGroup(exclusive = false) - public MDNSSettings mdnsSettings; - - LXIDiscovery discovery() { - return vxi11Settings != null ? vxi11Settings.discovery() : mdnsSettings.discovery(); - } - } - - public static class VXI11Settings { - @Option(names = { "--vxi11" }, description = "Enables VXI-11 based instrument discovery", defaultValue = "true") - public boolean vxi11DiscoveryEnabled; - - @Option(names = {"--vxi11-no-resolve"}, description = "Disables automatic device identification", defaultValue = "false") - public boolean vxi11noResolve; - - @Option(names = {"--vxi11-retransmissions" }, description = "Number of retransmission, useful when using unreliable connection", defaultValue = "1") - public int vxi11retransmissions; - - @Option(names = {"--vxi11-timeout" }, description = "Specify the timeout of a response in milliseconds", defaultValue = "1000") - public int vxi11timeout; - - public VXI11Settings() { - this.vxi11DiscoveryEnabled = true; - this.vxi11noResolve = false; - this.vxi11retransmissions = 1; - this.vxi11timeout = 1000; - } - - LXIDiscovery discovery() { - return new VXI11Discovery(!vxi11noResolve, vxi11retransmissions, vxi11timeout); - } - } - - public static class MDNSSettings { - @Option(names = { "--mdns" }, description = "Enables mDNS based instrument discovery", defaultValue = "false") - public boolean mdnsDiscoveryEnabled; - - @Option(names = { "--mdns-services" }, description = "Specify mDNS service types to search") - public List mdnsServiceTypes = Collections.singletonList(MDNSServiceType.LXI); - - LXIDiscovery discovery() { - return new MDNSDiscovery(mdnsServiceTypes); - } - } - - @Override - public Integer call() throws Exception { - //Collecting network interfaces to discover on - Stream interfaces; - if (networkInterface == null) { - // Not using NetworkInterface.networkInterfaces() as it's only introduced in - // Java 9 - List interfaceList = new LinkedList<>(); - Enumeration interfaceEnumeration = NetworkInterface.getNetworkInterfaces(); - while (interfaceEnumeration.hasMoreElements()) { - interfaceList.add(interfaceEnumeration.nextElement()); - } - interfaces = interfaceList.stream(); - } else { - interfaces = Collections.singletonList(networkInterface).stream(); - } - - //Running discovery tool - LXIDiscovery discoveryTool = discoverySettings.discovery(); - List warnings = new ArrayList<>(); - Set instruments = interfaces.flatMap(i -> { - try { - return discoveryTool.discover(i).stream(); - } catch (Exception e) { - if(!noWarnings) { - warnings.add("Warning " + e.getClass() + ":" + e.getMessage()); - } - return Collections.emptySet().stream(); - } - }).collect(Collectors.toCollection(HashSet::new)); - - //Printing out the result - if(jsonOutput) { - ObjectMapper mapper = new ObjectMapper(); - InstrumentListJSON json = new InstrumentListJSON(instruments, warnings); - mapper.writeValue(System.out, json); - } - else { - instruments.forEach(endpoint -> { - System.out.println("Found " + endpoint.getDeviceIdentifier().value() + " at " + endpoint.getHost() + ":" + endpoint.getPort() + " ("+endpoint.getClass().getSimpleName()+")"); - }); - warnings.forEach(warning -> { - System.out.println(warning); - }); - } - return 0; - } - - public static class InstrumentListJSON { - Set instruments; - - List warnings; - - public InstrumentListJSON(Set instruments, List warnings) { - this.instruments = instruments; - this.warnings = warnings; - } - - public Set getInstruments() { - return instruments; - } - - public List getWarnings() { - return warnings; - } - } -} \ No newline at end of file + @Option( + names = { "--interface", "-i" }, + description = "Network interface to run discovery on, " + + "if not specified then it will broadcast on all interfaces", + required = false) + public NetworkInterface networkInterface; + + @ArgGroup(exclusive = true) + public DiscoverySettings discoverySettings; + + @Option( + names = {"--json"}, + description = "Outputs a JSON formatted instrument array", + defaultValue = "false") + public boolean jsonOutput; + + @Option( + names = {"--no-warnings"}, + description = "Disables warnings in the output", + defaultValue = "false") + public boolean noWarnings; + + public LXIDiscover() { + this.discoverySettings = new DiscoverySettings(); + this.discoverySettings.vxi11Settings = new VXI11Settings(); + } + + /** + * DiscoverySettings holds the possible discovery methods. + */ + public static class DiscoverySettings { + @ArgGroup(exclusive = false) + public VXI11Settings vxi11Settings; + + @ArgGroup(exclusive = false) + public MDNSSettings mdnsSettings; + + LXIDiscovery discovery() { + return vxi11Settings != null ? vxi11Settings.discovery() : mdnsSettings.discovery(); + } + } + + /** + * VXI11Settings holds the options for VXI11 based instrument discovery. + */ + public static class VXI11Settings { + @Option( + names = { "--vxi11" }, + description = "Enables VXI-11 based instrument discovery", + defaultValue = "true") + public boolean vxi11DiscoveryEnabled; + + @Option( + names = {"--vxi11-no-resolve"}, + description = "Disables automatic device identification", + defaultValue = "false") + public boolean vxi11noResolve; + + @Option( + names = {"--vxi11-retransmissions" }, + description = "Number of retransmission, useful when using unreliable connection", + defaultValue = "1") + public int vxi11retransmissions; + + @Option( + names = {"--vxi11-timeout" }, + description = "Specify the timeout of a response in milliseconds", + defaultValue = "1000") + public int vxi11timeout; + + VXI11Settings() { + this.vxi11DiscoveryEnabled = true; + this.vxi11noResolve = false; + this.vxi11retransmissions = 1; + this.vxi11timeout = 1000; + } + + LXIDiscovery discovery() { + return new VXI11Discovery(!vxi11noResolve, vxi11retransmissions, vxi11timeout); + } + } + + /** + * MDNSSettings holds the options for mDNS based instrument discovery. + */ + public static class MDNSSettings { + @Option( + names = { "--mdns" }, + description = "Enables mDNS based instrument discovery", + defaultValue = "false") + public boolean mdnsDiscoveryEnabled; + + @Option( + names = { "--mdns-services" }, + description = "Specify mDNS service types to search") + public List mdnsServiceTypes = Collections.singletonList(MDNSServiceType.LXI); + + LXIDiscovery discovery() { + return new MDNSDiscovery(mdnsServiceTypes); + } + } + + @Override + public Integer call() throws Exception { + //Collecting network interfaces to discover on + Stream interfaces; + if (networkInterface == null) { + // Not using NetworkInterface.networkInterfaces() as it's only introduced in + // Java 9 + List interfaceList = new LinkedList<>(); + Enumeration interfaceEnumeration = NetworkInterface.getNetworkInterfaces(); + while (interfaceEnumeration.hasMoreElements()) { + interfaceList.add(interfaceEnumeration.nextElement()); + } + interfaces = interfaceList.stream(); + } else { + interfaces = Collections.singletonList(networkInterface).stream(); + } + + //Running discovery tool + LXIDiscovery discoveryTool = discoverySettings.discovery(); + List warnings = new ArrayList<>(); + Set instruments = interfaces.flatMap(i -> { + try { + return discoveryTool.discover(i).stream(); + } catch (Exception e) { + if (!noWarnings) { + warnings.add("Warning " + e.getClass() + ":" + e.getMessage()); + } + return Collections.emptySet().stream(); + } + }).collect(Collectors.toCollection(HashSet::new)); + + //Printing out the result + if (jsonOutput) { + ObjectMapper mapper = new ObjectMapper(); + InstrumentListJSON json = new InstrumentListJSON(instruments, warnings); + mapper.writeValue(System.out, json); + } else { + instruments.forEach(endpoint -> { + System.out.printf("Found %s at %s:%g (%s)", + endpoint.getDeviceIdentifier().value(), + endpoint.getHost(), + endpoint.getPort(), + endpoint.getClass().getSimpleName()); + }); + warnings.forEach(warning -> { + System.out.println(warning); + }); + } + return 0; + } + + /** + * InstrumentListJSON is a container for instrument discovery result. + */ + public static class InstrumentListJSON { + + Set instruments; + + List warnings; + + public InstrumentListJSON(Set instruments, List warnings) { + this.instruments = instruments; + this.warnings = warnings; + } + + public Set getInstruments() { + return instruments; + } + + public List getWarnings() { + return warnings; + } + + } + +} diff --git a/measure-cli/src/main/java/org/jtmc/cli/scpi/SCPISend.java b/measure-cli/src/main/java/org/jtmc/cli/scpi/SCPISend.java index c5addfb..f6c02bb 100644 --- a/measure-cli/src/main/java/org/jtmc/cli/scpi/SCPISend.java +++ b/measure-cli/src/main/java/org/jtmc/cli/scpi/SCPISend.java @@ -3,35 +3,49 @@ import java.io.File; import java.util.List; import java.util.concurrent.Callable; - import org.jtmc.cli.visa.VISASocketOption; - +import org.jtmc.core.device.ISocket; +import org.jtmc.core.scpi.SCPICommand; +import org.jtmc.core.scpi.socket.RawSCPISocket; import picocli.CommandLine.ArgGroup; import picocli.CommandLine.Command; import picocli.CommandLine.Option; +/** + * SCPISend is a command that can be used to send SCPI commands to instruments. + */ @Command(name = "scpi-send") public class SCPISend implements Callable { - - @ArgGroup(exclusive = true) - Input input; - - @ArgGroup(exclusive = true) - VISASocketOption socketOption; - - class Input { - @Option(names = {"--commands", "-c"}, required = false) - List commands; - - @Option(names = {"--script"}, required = false) - File script; - - @Option(names = {"--interactive", "-i"}, required = true) - boolean interactive; - } - - @Override - public Integer call() throws Exception { - return 1; - } -} \ No newline at end of file + + @ArgGroup(exclusive = true) + Input input; + + @ArgGroup(exclusive = true) + VISASocketOption socketOption; + + static class Input { + @Option(names = {"--commands", "-c"}, required = false) + List commands; + + @Option(names = {"--script"}, required = false) + File script; + + @Option(names = {"--interactive", "-i"}, required = true) + boolean interactive; + } + + @Override + public Integer call() throws Exception { + try (ISocket socket = socketOption.getSocket()) { + RawSCPISocket scpiSocket = new RawSCPISocket(socket); + for (String command : input.commands) { + SCPICommand scpiCommand = new SCPICommand(command); + scpiSocket.send(scpiCommand); + if (scpiCommand.isQuery()) { + System.out.println(scpiSocket.receive(1000)); + } + } + return 0; + } + } +} diff --git a/measure-cli/src/main/java/org/jtmc/cli/visa/RawSocketOption.java b/measure-cli/src/main/java/org/jtmc/cli/visa/RawSocketOption.java index 3a0cf73..45aae1a 100644 --- a/measure-cli/src/main/java/org/jtmc/cli/visa/RawSocketOption.java +++ b/measure-cli/src/main/java/org/jtmc/cli/visa/RawSocketOption.java @@ -1,24 +1,25 @@ package org.jtmc.cli.visa; import java.io.IOException; - import org.jtmc.core.lxi.raw.RawSocket; - import picocli.CommandLine.Option; +/** + * RawSocketOptions holds the arguments of a RawSocket. + */ public class RawSocketOption { - @Option(names = { "--raw-host" }, required = true) - String host; + @Option(names = { "--raw-host" }, required = true) + String host; - @Option(names = { "--raw-port" }, required = true) - int port; + @Option(names = { "--raw-port" }, required = true) + int port; - @Option(names = { "--raw-board" }, defaultValue = "0") - int board; + @Option(names = { "--raw-board" }, defaultValue = "0") + int board; - public RawSocket getSocket() throws IOException { - return new RawSocket(host, port, board); - } + public RawSocket getSocket() throws IOException { + return new RawSocket(host, port, board); + } -} \ No newline at end of file +} diff --git a/measure-cli/src/main/java/org/jtmc/cli/visa/SerialSocketOption.java b/measure-cli/src/main/java/org/jtmc/cli/visa/SerialSocketOption.java index 9b5cfe1..49c7ed8 100644 --- a/measure-cli/src/main/java/org/jtmc/cli/visa/SerialSocketOption.java +++ b/measure-cli/src/main/java/org/jtmc/cli/visa/SerialSocketOption.java @@ -1,38 +1,54 @@ package org.jtmc.cli.visa; import java.io.IOException; - import org.jtmc.core.serial.FlowControl; import org.jtmc.core.serial.Parity; import org.jtmc.core.serial.SerialSocket; import org.jtmc.core.serial.StopBits; - import picocli.CommandLine.ArgGroup; import picocli.CommandLine.Option; +/** + * SerialSocketOption holds the arguments of a SerialSocket. + */ public class SerialSocketOption { - @Option(names = { "--serial-port" }) - String port; - - @ArgGroup(exclusive = false) - SerialSocketDetailedConfiguration socket; - - public SerialSocket getSocket() throws IOException { - return new SerialSocket(port, socket.baudrate, socket.databits, socket.parity, socket.stopbits, FlowControl.NONE); - } - - public static class SerialSocketDetailedConfiguration { - @Option(names = {"--serial-baudrate"}, defaultValue = "9600") - int baudrate; - - @Option(names = {"--serial-databits"}, defaultValue = "8") - int databits; - - @Option(names = {"--serial-parity"}, defaultValue = "NONE") - Parity parity; - - @Option(names = {"--serial-stopbits"}, defaultValue = "1") - StopBits stopbits; - - } -} \ No newline at end of file + @Option(names = { "--serial-port" }) + String port; + + @ArgGroup(exclusive = false) + SerialSocketDetailedConfiguration socket; + + /** + * Returns the configured Serial socket. + * @return Serial socket + * @throws IOException if there was an error opening the socket + */ + public SerialSocket getSocket() throws IOException { + return new SerialSocket( + port, + socket.baudrate, + socket.databits, + socket.parity, + socket.stopbits, + FlowControl.NONE); + } + + /** + * SerialSocketDetailedConfiguration is an argument group that allows configuring + * the SerialSocket's options individually. + */ + public static class SerialSocketDetailedConfiguration { + @Option(names = {"--serial-baudrate"}, defaultValue = "9600") + int baudrate; + + @Option(names = {"--serial-databits"}, defaultValue = "8") + int databits; + + @Option(names = {"--serial-parity"}, defaultValue = "NONE") + Parity parity; + + @Option(names = {"--serial-stopbits"}, defaultValue = "1") + StopBits stopbits; + + } +} diff --git a/measure-cli/src/main/java/org/jtmc/cli/visa/VISASocketOption.java b/measure-cli/src/main/java/org/jtmc/cli/visa/VISASocketOption.java index 2752808..a39d83b 100644 --- a/measure-cli/src/main/java/org/jtmc/cli/visa/VISASocketOption.java +++ b/measure-cli/src/main/java/org/jtmc/cli/visa/VISASocketOption.java @@ -1,17 +1,41 @@ package org.jtmc.cli.visa; +import java.io.IOException; +import org.jtmc.core.device.ISocket; import picocli.CommandLine.ArgGroup; import picocli.CommandLine.Option; +/** + * VISASocketOption is a composite argument group that holds the underlying + * socket implementations' options. + */ public class VISASocketOption { - @ArgGroup(exclusive = false) - RawSocketOption rawSocket; - - @ArgGroup(exclusive = false) - SerialSocketOption serialSocket; + @ArgGroup(exclusive = false) + RawSocketOption rawSocket; - @Option(names = {"--resource"}) - String resourceString; - -} \ No newline at end of file + @ArgGroup(exclusive = false) + SerialSocketOption serialSocket; + + @ArgGroup(exclusive = false) + VXI11SocketOption vxi11Socket; + + @Option(names = { "--resource" }) + String resourceString; + + /** + * Returns the underlying socket implementation. + * @return Socket + * @throws IOException if there was an error opening the socket + */ + public ISocket getSocket() throws IOException { + if (rawSocket != null) { + return rawSocket.getSocket(); + } else if (serialSocket != null) { + return serialSocket.getSocket(); + } else if (vxi11Socket != null) { + return vxi11Socket.getSocket(); + } + return null; + } +} diff --git a/measure-cli/src/main/java/org/jtmc/cli/visa/VXI11SocketOption.java b/measure-cli/src/main/java/org/jtmc/cli/visa/VXI11SocketOption.java index 4836e79..81580d6 100644 --- a/measure-cli/src/main/java/org/jtmc/cli/visa/VXI11SocketOption.java +++ b/measure-cli/src/main/java/org/jtmc/cli/visa/VXI11SocketOption.java @@ -2,35 +2,36 @@ import java.io.IOException; import java.net.InetAddress; - import org.jtmc.core.lxi.vxi11.VXI11Socket; - import picocli.CommandLine.Option; +/** + * VXI11SocketOption holds the arguments of a VXI11Socket. + */ public class VXI11SocketOption { - @Option(names = { "--vxi11-host" }, required = true) - InetAddress host; + @Option(names = { "--vxi11-host" }, required = true) + InetAddress host; - @Option(names = { "--vxi11-port" }, defaultValue = "0") - int port; + @Option(names = { "--vxi11-port" }, defaultValue = "0") + int port; - @Option(names = { "--vxi11-name" }, defaultValue = "inst0") - String name; + @Option(names = { "--vxi11-name" }, defaultValue = "inst0") + String name; - @Option(names = {"--vxi11-lock"}, defaultValue = "false") - boolean lock; + @Option(names = {"--vxi11-lock"}, defaultValue = "false") + boolean lock; - @Option(names = {"--vxi1-lock-timeout"}, defaultValue = "0") - int lockTimeout; + @Option(names = {"--vxi1-lock-timeout"}, defaultValue = "0") + int lockTimeout; - @Option(names = {"--vxi1-io-timeout"}, defaultValue = "0") - int ioTimeout; + @Option(names = {"--vxi1-io-timeout"}, defaultValue = "0") + int ioTimeout; - @Option(names = {"--vxi1-write-block-size"}, defaultValue = "8128") - int writeBlockSize; + @Option(names = {"--vxi1-write-block-size"}, defaultValue = "8128") + int writeBlockSize; - public VXI11Socket getSocket() throws IOException { - return new VXI11Socket(host, name, port, lock, lockTimeout, ioTimeout, writeBlockSize); - } -} \ No newline at end of file + public VXI11Socket getSocket() throws IOException { + return new VXI11Socket(host, name, port, lock, lockTimeout, ioTimeout, writeBlockSize); + } +} diff --git a/measure-core/src/main/java/org/jtmc/core/device/Connectable.java b/measure-core/src/main/java/org/jtmc/core/device/Connectable.java index 0f618fa..322ab3c 100644 --- a/measure-core/src/main/java/org/jtmc/core/device/Connectable.java +++ b/measure-core/src/main/java/org/jtmc/core/device/Connectable.java @@ -1,23 +1,25 @@ package org.jtmc.core.device; /** - * Connectable is any device that has a binary state of connection, either connected or disconnected + * Connectable is any device that has a binary state of connection, + * either connected or disconnected. */ public interface Connectable extends AutoCloseable { - /** - * Returns whether or not the device is connected - * - * @return {@code true} if the device is connected - */ - public boolean isConnected(); + /** + * Returns whether or not the device is connected. + * + * @return {@code true} if the device is connected + */ + public boolean isConnected(); - /** - * Disconnects the device - * - *

This operation should be idempotent, meaning that calling it - * multiple times shouldn't affect the state of the connection beyond the first call - */ - @Override - public void close(); -} \ No newline at end of file + /** + * Disconnects the device. + * + *

This operation should be idempotent, meaning that calling it + * multiple times shouldn't affect the state of the connection beyond + * the first call + */ + @Override + public void close(); +} diff --git a/measure-core/src/main/java/org/jtmc/core/device/ISocket.java b/measure-core/src/main/java/org/jtmc/core/device/ISocket.java index 82adbba..10fbbd6 100644 --- a/measure-core/src/main/java/org/jtmc/core/device/ISocket.java +++ b/measure-core/src/main/java/org/jtmc/core/device/ISocket.java @@ -5,47 +5,50 @@ import java.nio.ByteBuffer; /** - * ISocket is a connectable socket that's capable of transmitting and receiving byte streams + * ISocket is a connectable socket that's capable of transmitting and receiving byte streams. */ public interface ISocket extends Connectable { - /** - * Transmits the given message to the device - * - * @param message Message as a byte buffer - * @throws IOException if there was an error sending the message - */ - void send(final ByteBuffer message) throws IOException; + /** + * Transmits the given message to the device. + * + * @param message Message as a byte buffer + * @throws IOException if there was an error sending the message + */ + void send(final ByteBuffer message) throws IOException; - /** - * Receives a message from the device until the given byte count has been reached - * - * @param count Message's length - * @param timeout Maximum time to wait for the given number of bytes to be received - * @return Message as a byte buffer - * @throws IOException If there was an error during reception - * @throws TimeoutException If the timeout has been reached - */ - ByteBuffer receive(final int count, final long timeout) throws IOException, SocketTimeoutException; + /** + * Receives a message from the device until the given byte count has been reached. + * + * @param count Message's length + * @param timeout Maximum time to wait for the given number of bytes to be received + * @return Message as a byte buffer + * @throws IOException If there was an error during reception + * @throws TimeoutException If the timeout has been reached + */ + ByteBuffer receive( + final int count, + final long timeout) throws IOException, SocketTimeoutException; - /** - * Receives a message from the device until the given character has been reached - * - * @param termination Termination character, like '\n' - * @param timeout Maximum time to wait for the given character to be reached - * @return Message as a byte buffer - * @throws IOException If there was an error during reception - * @throws TimeoutException If the timeout has been reached - */ - ByteBuffer receive(final char termination, final long timeout) throws IOException, SocketTimeoutException; - - /** - * Returns the VISA resource string associated with this socket instance - * - *

Using the returned the String the Socket factory must be able to recreate the connection - * (or there should be at least 1 public constructor with a single String parameter) - * - * @return VISA Resource string - */ - public String getResourceString(); -} \ No newline at end of file + /** + * Receives a message from the device until the given character has been reached. + * + * @param termination Termination character, like '\n' + * @param timeout Maximum time to wait for the given character to be reached + * @return Message as a byte buffer + * @throws IOException If there was an error during reception + * @throws TimeoutException If the timeout has been reached + */ + ByteBuffer receive( + final char termination, final long timeout) throws IOException, SocketTimeoutException; + + /** + * Returns the VISA resource string associated with this socket instance. + * + *

Using the returned the String the Socket factory must be able to recreate the connection + * (or there should be at least 1 public constructor with a single String parameter) + * + * @return VISA Resource string + */ + public String getResourceString(); +} diff --git a/measure-core/src/main/java/org/jtmc/core/instrument/DCPowerSupply.java b/measure-core/src/main/java/org/jtmc/core/instrument/DCPowerSupply.java index 80b8c32..07364cf 100644 --- a/measure-core/src/main/java/org/jtmc/core/instrument/DCPowerSupply.java +++ b/measure-core/src/main/java/org/jtmc/core/instrument/DCPowerSupply.java @@ -3,37 +3,85 @@ import java.util.Collection; /** - * A DC power supply is a device capable of outputting a set voltage or a set current + * A DC power supply is a device capable of outputting a set voltage + * or a set current. * * @author Balazs Eszes */ public interface DCPowerSupply { - Collection getPowerOutputs(); + Collection getPowerOutputs(); - default PowerOutput getPowerOutput(int index) { - return this.getPowerOutputs().stream().skip(index).findFirst().get(); - } + /** + * Returns the power output at the given index. + * + * @param index Output index (zero indexed) + * @return Power output + */ + default PowerOutput getPowerOutput(int index) { + return this.getPowerOutputs().stream().skip(index).findFirst().get(); + } - default PowerOutput getPowerOutput(String name) { - return this.getPowerOutputs().stream().filter(channel -> channel.getName().equals(name)).findFirst().get(); - } + /** + * Returns the power output with the given name. + * + * @param name Output name + * @return Power output + */ + default PowerOutput getPowerOutput(String name) { + return this.getPowerOutputs() + .stream() + .filter(channel -> channel.getName().equals(name)) + .findFirst() + .get(); + } - public static interface PowerOutput { - String getName(); + /** + * PowerOutput is used to represent a hardware output capable to sourcing and + * optionally sinking current. + */ + public static interface PowerOutput { + /** + * Returns the human readable name of the output. + * + * @return Power output name + */ + String getName(); - void setEnabled(boolean enabled); + /** + * Enables or disables the power output. + * + * @param enabled if {@code true} the output is enabled + */ + void setEnabled(boolean enabled); - void setMaximumVoltage(double voltage); + /** + * Sets the maximum voltage the channel tries to output also known + * as 'Set voltage'. + * + * @param voltage Set voltage + */ + void setMaximumVoltage(double voltage); - double getMaximumVoltage(); + double getMaximumVoltage(); - void setMaximumCurrent(double current); + void setMaximumCurrent(double current); - double getMaximumCurrent(); + double getMaximumCurrent(); - double getVoltage(); + /** + * Returns the voltage currently output by the power output. + * + * @return Output voltage + */ + double getVoltage(); - double getCurrent(); - } -} \ No newline at end of file + /** + * Return the current (amperes) currently output by the power output. + * + * @return Output amperes + */ + double getCurrent(); + } + +} diff --git a/measure-core/src/main/java/org/jtmc/core/instrument/FunctionGenerator.java b/measure-core/src/main/java/org/jtmc/core/instrument/FunctionGenerator.java index 0d48531..42878aa 100644 --- a/measure-core/src/main/java/org/jtmc/core/instrument/FunctionGenerator.java +++ b/measure-core/src/main/java/org/jtmc/core/instrument/FunctionGenerator.java @@ -1,58 +1,70 @@ package org.jtmc.core.instrument; import java.util.Collection; - import org.jtmc.core.visa.exception.InstrumentException; /** - * A waveform generator is a device capable of outputting a changing voltage signal + * A waveform generator is a device capable of outputting a changing voltage signal. * * @author Balazs Eszes */ public interface FunctionGenerator { - Collection getAnalogOutputs(); + Collection getAnalogOutputs(); - public static interface AnalogOutput { - String getName(); + /** + * AnalogOutput represents a physical output. + */ + public static interface AnalogOutput { + String getName(); - void setEnabled(boolean enabled) throws InstrumentException; + void setEnabled(boolean enabled) throws InstrumentException; - void setOperationMode(OperationMode operationMode) throws InstrumentException; + void setOperationMode(OperationMode operationMode) throws InstrumentException; - void setImpedance(double impedance) throws InstrumentException; + void setImpedance(double impedance) throws InstrumentException; - // Commons + // Commons - void setAmplitude(double amplitude) throws InstrumentException; + void setAmplitude(double amplitude) throws InstrumentException; - void setOffset(double offset) throws InstrumentException; + void setOffset(double offset) throws InstrumentException; - void setFrequency(double frequency) throws InstrumentException; + void setFrequency(double frequency) throws InstrumentException; - void setPhase(double phase) throws InstrumentException; + void setPhase(double phase) throws InstrumentException; - // Builtin + void setWaveformType(WaveformType waveformType) throws InstrumentException; - void setWaveformType(WaveformType waveformType) throws InstrumentException; + default void setWaveform( + WaveformType waveformType, + double amplitude, + double offset, + double frequency, + double phase) throws InstrumentException { + + } - default void setWaveform(WaveformType waveformType, double amplitude, double offset, double frequency, double phase) throws InstrumentException { - - } + // Arbitrary - // Arbitrary + //void setWaveform(); - //void setWaveform(); + //void setSampleRate(); - //void setSampleRate(); + } - } + /** + * Waveform types supported by most Function generators. + */ + public static enum WaveformType { + SINE, SQUARE, TRIANGLE, RAMPUP, RAMPDOWN, DC, NOISE + } - public static enum WaveformType { - SINE, SQUARE, TRIANGLE, RAMPUP, RAMPDOWN, DC, NOISE - } + /** + * Operation modes. + */ + public static enum OperationMode { + CONTINUOUS, BURST + } - public static enum OperationMode { - CONTINUOUS, BURST - } -} \ No newline at end of file +} diff --git a/measure-core/src/main/java/org/jtmc/core/instrument/LogicAnalyzer.java b/measure-core/src/main/java/org/jtmc/core/instrument/LogicAnalyzer.java index 3b253b5..6d31d3d 100644 --- a/measure-core/src/main/java/org/jtmc/core/instrument/LogicAnalyzer.java +++ b/measure-core/src/main/java/org/jtmc/core/instrument/LogicAnalyzer.java @@ -4,35 +4,48 @@ /** * A logic analyzer is a device capable of making binary signal measurements - * over time + * over time. * * @author Balazs Eszes */ public interface LogicAnalyzer extends TimeDomainAnalyzer { - public final static double TTL = 1.5f; + public static final double TTL = 1.5f; - public final static double CMOS = 1.65f; + public static final double CMOS = 1.65f; - public final static double LVCMOS3V3 = 1.65f; + public static final double LVCMOS3V3 = 1.65f; - public final static double LVCMOS2V5 = 1.25f; + public static final double LVCMOS2V5 = 1.25f; - Collection getDigitalInputs(); + Collection getDigitalInputs(); - default DigitalInput getDigitalInput(int index) { - return this.getDigitalInputs().stream().skip(index).findFirst().get(); - } + default DigitalInput getDigitalInput(int index) { + return this.getDigitalInputs().stream().skip(index).findFirst().get(); + } - default DigitalInput getDigitalInput(String name) { - return this.getDigitalInputs().stream().filter(channel -> channel.getName().equals(name)).findFirst().get(); - } + /** + * Returns the digital input with the matching name. + * @param name Input name + * @return Digital Input + */ + default DigitalInput getDigitalInput(String name) { + return this.getDigitalInputs() + .stream() + .filter(channel -> channel.getName() + .equals(name)) + .findFirst() + .get(); + } - public static interface DigitalInput { - String getName(); + /** + * DigitalInput represents a physical input recording logical values. + */ + public static interface DigitalInput { + String getName(); - void setEnabled(boolean enabled); + void setEnabled(boolean enabled); - void setThreshold(double threshold); - } -} \ No newline at end of file + void setThreshold(double threshold); + } +} diff --git a/measure-core/src/main/java/org/jtmc/core/instrument/Oscilloscope.java b/measure-core/src/main/java/org/jtmc/core/instrument/Oscilloscope.java index 46e3bfb..c98917e 100644 --- a/measure-core/src/main/java/org/jtmc/core/instrument/Oscilloscope.java +++ b/measure-core/src/main/java/org/jtmc/core/instrument/Oscilloscope.java @@ -1,51 +1,60 @@ package org.jtmc.core.instrument; +import java.util.Collection; +import java.util.Optional; import org.jtmc.core.instrument.common.Coupling; import org.jtmc.core.signal.analog.AnalogSignal; import org.jtmc.core.visa.exception.InstrumentException; -import java.util.Collection; -import java.util.Optional; - /** - * An oscilloscope is a device capable of making voltage measurements over time + * An oscilloscope is a device capable of making voltage measurements over time. * * @author Balazs Eszes */ public interface Oscilloscope extends TimeDomainAnalyzer { - Collection getAnalogInputs(); - - default AnalogInput getAnalogInput(int index) { - return this.getAnalogInputs().stream().skip(index).findFirst().get(); - } + Collection getAnalogInputs(); - default AnalogInput getAnalogInput(String name) { - return this.getAnalogInputs().stream().filter(channel -> channel.getName().equals(name)).findFirst().get(); - } + default AnalogInput getAnalogInput(int index) { + return this.getAnalogInputs().stream().skip(index).findFirst().get(); + } - public static interface AnalogInput { - String getName(); + /** + * Returns the analog input with the matching name. + * @param name Input name + * @return Analog Input + */ + default AnalogInput getAnalogInput(String name) { + return this.getAnalogInputs() + .stream() + .filter(channel -> channel.getName().equals(name)) + .findFirst() + .get(); + } - void setEnabled(boolean enabled) throws InstrumentException; + /** + * AnalogInput represents a physical input recording numerical values. + */ + public static interface AnalogInput { + String getName(); - void setRange(double range) throws InstrumentException; + void setEnabled(boolean enabled) throws InstrumentException; - void setOffset(double offset) throws InstrumentException; + void setRange(double range) throws InstrumentException; - void setProbeAttenuation(double attenuation) throws InstrumentException; + void setOffset(double offset) throws InstrumentException; - void setProbeSense(boolean enable) throws InstrumentException; + void setProbeAttenuation(double attenuation) throws InstrumentException; - void setImpedance(double impedance) throws InstrumentException; + void setProbeSense(boolean enable) throws InstrumentException; - void setBandwidthLimit(double bandwidthLimit) throws InstrumentException; + void setImpedance(double impedance) throws InstrumentException; - void setCoupling(Coupling coupling) throws InstrumentException; + void setBandwidthLimit(double bandwidthLimit) throws InstrumentException; - Optional getAnalogInputSignal(int channel) throws InstrumentException; - } + void setCoupling(Coupling coupling) throws InstrumentException; - //Supplier<> + Optional getAnalogInputSignal(int channel) throws InstrumentException; + } -} \ No newline at end of file +} diff --git a/measure-core/src/main/java/org/jtmc/core/instrument/TimeDomainAnalyzer.java b/measure-core/src/main/java/org/jtmc/core/instrument/TimeDomainAnalyzer.java index e248dc7..e2929f7 100644 --- a/measure-core/src/main/java/org/jtmc/core/instrument/TimeDomainAnalyzer.java +++ b/measure-core/src/main/java/org/jtmc/core/instrument/TimeDomainAnalyzer.java @@ -3,43 +3,56 @@ import org.jtmc.core.visa.exception.InstrumentException; /** - * TimeDomainAnalyzer is any device capable of time correlated measurements + * TimeDomainAnalyzer is any device capable of time correlated measurements. * * @author Balazs Eszes */ public interface TimeDomainAnalyzer { - //TODO: move to trigger subsystem - void setRunState(RunState state) throws InstrumentException; + //TODO: move to trigger subsystem + void setRunState(RunState state) throws InstrumentException; - public static enum RunState { - AUTO, NORMAL, SINGLE, STOP; - } + /** + * RunState represents the method which is used to trigger an acquisition. + */ + public static enum RunState { + AUTO, NORMAL, SINGLE, STOP; + } - AcquisitionBaseSystem acquisition(); + AcquisitionBaseSystem acquisition(); - public static interface AcquisitionBaseSystem { + /** + * AcquisitionBaseSystem controls how time correlated measurements are made. + */ + public static interface AcquisitionBaseSystem { - void setTimespan(double span) throws InstrumentException; + void setTimespan(double span) throws InstrumentException; - void setSampleCount(long count) throws InstrumentException; + void setSampleCount(long count) throws InstrumentException; - long getSampleCount() throws InstrumentException; + long getSampleCount() throws InstrumentException; - double getSampleRate() throws InstrumentException; + double getSampleRate() throws InstrumentException; - void setTimeOffset(double offset) throws InstrumentException; - - void setMode(AcquisitionMode mode) throws InstrumentException; + void setTimeOffset(double offset) throws InstrumentException; + + void setMode(AcquisitionMode mode) throws InstrumentException; - public static enum AcquisitionMode { - NORMAL, HIGH_RESOLUTION, AVERAGE, PEAK, ENVELOPE - } + /** + * AcquisitionMode controls how multiple measurements are evaluated into one. + */ + public static enum AcquisitionMode { + NORMAL, HIGH_RESOLUTION, AVERAGE, PEAK, ENVELOPE + } - AcquisitionState getState() throws InstrumentException; + AcquisitionState getState() throws InstrumentException; - public static enum AcquisitionState { - COMPLETE, INPROGRESS, UNKNOWN - } - } -} \ No newline at end of file + /** + * AcquisitionState represents the current measurement availability. + */ + public static enum AcquisitionState { + COMPLETE, INPROGRESS, UNKNOWN + } + } + +} diff --git a/measure-core/src/main/java/org/jtmc/core/instrument/common/Coupling.java b/measure-core/src/main/java/org/jtmc/core/instrument/common/Coupling.java index 339d783..b3bb72b 100644 --- a/measure-core/src/main/java/org/jtmc/core/instrument/common/Coupling.java +++ b/measure-core/src/main/java/org/jtmc/core/instrument/common/Coupling.java @@ -1,5 +1,23 @@ package org.jtmc.core.instrument.common; +/** + * Coupling represents how a hardware input is connected to the sensing. + * circuit + */ public enum Coupling { - AC, DC, GND -} \ No newline at end of file + /** + * A capacitor in series is activated on the sense line. + */ + AC, + + /** + * The input is directly connected to the sensing circuit. + */ + DC, + + /** + * The sense circuit's input is grounded instead of being connected to the + * physical input. + */ + GND +} diff --git a/measure-core/src/main/java/org/jtmc/core/instrument/common/Impedance.java b/measure-core/src/main/java/org/jtmc/core/instrument/common/Impedance.java index cbf2b2f..4ae9c34 100644 --- a/measure-core/src/main/java/org/jtmc/core/instrument/common/Impedance.java +++ b/measure-core/src/main/java/org/jtmc/core/instrument/common/Impedance.java @@ -1,8 +1,20 @@ package org.jtmc.core.instrument.common; +/** + * Contains common impedance values used in test and measurement. + * + *

Users and instrument drivers should use these values for a consistent + * experience across devices. + */ public class Impedance { - - public static final double Z50 = 50.0; + + /** + * Represents a 50Ohm load. + */ + public static final double Z50 = 50.0; - public static final double HIZ = Double.MAX_VALUE; -} \ No newline at end of file + /** + * Represents a High Impedance load. + */ + public static final double HIZ = Double.MAX_VALUE; +} diff --git a/measure-core/src/main/java/org/jtmc/core/lxi/LXIDiscovery.java b/measure-core/src/main/java/org/jtmc/core/lxi/LXIDiscovery.java index 859f02b..e4618a4 100644 --- a/measure-core/src/main/java/org/jtmc/core/lxi/LXIDiscovery.java +++ b/measure-core/src/main/java/org/jtmc/core/lxi/LXIDiscovery.java @@ -1,110 +1,52 @@ package org.jtmc.core.lxi; import java.io.IOException; -import java.net.InetAddress; import java.net.NetworkInterface; import java.net.SocketException; import java.util.Enumeration; import java.util.HashSet; -import java.util.Objects; import java.util.Set; - -import org.jtmc.core.visa.DeviceIdentifier; import org.jtmc.core.visa.VisaException; import org.jtmc.core.visa.instrument.InstrumentDiscovery; -import org.jtmc.core.visa.instrument.InstrumentEndpoint; /** - * LXIDiscovery is the interface for discovering LXI devices on the network + * LXIDiscovery is an interface for discovering LXI devices on the network. */ public interface LXIDiscovery extends InstrumentDiscovery { - /** - * Returns LXI devices found through the given network interface - * - * @param networkInterface Network Interface - * @return LXI Instrument Endpoints - * @throws IOException if there was an error during device discovery - */ - Set discover(NetworkInterface networkInterface) throws IOException; - - /** - * Returns LXI devices found through all of the available network interfaces - * @return LXI Instrument Endpoints - * @throws VisaException if there was an error during device discovery - */ - @Override - default Set discover() throws VisaException { - try { - Enumeration interfaces = NetworkInterface.getNetworkInterfaces(); - Set endpoints = new HashSet<>(); - while(interfaces.hasMoreElements()) { - try { - NetworkInterface intf = interfaces.nextElement(); - endpoints.addAll( - discover(intf)); - } catch (IOException e) { - //TODO: log exception - } - } - return endpoints; - } catch(SocketException e) { - throw new VisaException(e); - } - } - - /** - * LXI Instrument Endpoints identify a host on the network and a port through - * which the instrument is accessible - */ - public abstract static class LXIInstrumentEndpoint implements InstrumentEndpoint { - - private final InetAddress host; - - private final int port; - - private final DeviceIdentifier deviceIdentifier; - - public LXIInstrumentEndpoint(InetAddress host, int port, DeviceIdentifier deviceIdentifier) { - this.host = host; - this.port = port; - this.deviceIdentifier = deviceIdentifier; - } - - public InetAddress getHost() { - return host; - } - - public int getPort() { - return port; - } - - /** - * Returns the Device identifier of this instrument, when the device identifier cannot be resolved - * it will return DeviceIdentifier.UNKNOWN - * - * @return Device identifier - */ - public DeviceIdentifier getDeviceIdentifier() { - return deviceIdentifier; - } - - @Override - public boolean equals(Object obj) { - if(this == obj) { - return true; - } - if(obj == null) { - return false; - } - LXIInstrumentEndpoint endpoint = (LXIInstrumentEndpoint) obj; - return Objects.equals(this.host, endpoint.host) && Objects.equals(this.port, endpoint.port); - } - - @Override - public int hashCode() { - return Objects.hash(this.host, this.port); - } - - } -} \ No newline at end of file + /** + * Returns LXI devices found through the given network interface. + * + * @param networkInterface Network Interface + * @return LXI Instrument Endpoints + * @throws IOException if there was an error during device discovery + */ + Set discover( + NetworkInterface networkInterface) throws IOException; + + /** + * Returns LXI devices found through all of the available network interfaces. + * + * @return LXI Instrument Endpoints + * @throws VisaException if there was an error during device discovery + */ + @Override + default Set discover() throws VisaException { + try { + Enumeration interfaces = NetworkInterface.getNetworkInterfaces(); + Set endpoints = new HashSet<>(); + while (interfaces.hasMoreElements()) { + try { + NetworkInterface intf = interfaces.nextElement(); + endpoints.addAll(discover(intf)); + } catch (IOException e) { + //TODO: log exception + } + } + return endpoints; + } catch (SocketException e) { + throw new VisaException(e); + } + } + +} diff --git a/measure-core/src/main/java/org/jtmc/core/lxi/LXIInstrumentEndpoint.java b/measure-core/src/main/java/org/jtmc/core/lxi/LXIInstrumentEndpoint.java new file mode 100644 index 0000000..74b3856 --- /dev/null +++ b/measure-core/src/main/java/org/jtmc/core/lxi/LXIInstrumentEndpoint.java @@ -0,0 +1,70 @@ +package org.jtmc.core.lxi; + +import java.net.InetAddress; +import java.util.Objects; +import org.jtmc.core.visa.DeviceIdentifier; +import org.jtmc.core.visa.instrument.InstrumentEndpoint; + +/** + * LXI Instrument Endpoints identify a host on the network and a port through + * which the instrument is accessible. + */ +public abstract class LXIInstrumentEndpoint implements InstrumentEndpoint { + + private final InetAddress host; + + private final int port; + + private final DeviceIdentifier deviceIdentifier; + + /** + * Constructs a new LXI Instrument Endpoint by specifying it's network address and port. + * + * @param host Instrument's host address + * @param port Port number + * @param deviceIdentifier DeviceIdentifier + */ + public LXIInstrumentEndpoint(InetAddress host, int port, DeviceIdentifier deviceIdentifier) { + this.host = host; + this.port = port; + this.deviceIdentifier = deviceIdentifier; + } + + public InetAddress getHost() { + return host; + } + + public int getPort() { + return port; + } + + /** + * Returns the Device identifier of this instrument, + * if the device identifier cannot be resolved it returns + * DeviceIdentifier.UNKNOWN + * + * @return Device identifier + */ + public DeviceIdentifier getDeviceIdentifier() { + return deviceIdentifier; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + LXIInstrumentEndpoint endpoint = (LXIInstrumentEndpoint) obj; + return Objects.equals(this.host, endpoint.host) + && Objects.equals(this.port, endpoint.port); + } + + @Override + public int hashCode() { + return Objects.hash(this.host, this.port); + } + +} diff --git a/measure-core/src/main/java/org/jtmc/core/lxi/mdns/MDNS.java b/measure-core/src/main/java/org/jtmc/core/lxi/mdns/MDNS.java index 5cf631b..3b5c164 100644 --- a/measure-core/src/main/java/org/jtmc/core/lxi/mdns/MDNS.java +++ b/measure-core/src/main/java/org/jtmc/core/lxi/mdns/MDNS.java @@ -1,32 +1,48 @@ package org.jtmc.core.lxi.mdns; import javax.jmdns.ServiceInfo; - import org.jtmc.core.visa.DeviceIdentifier; +/** + * Contains mDNS related utility functions. + */ public class MDNS { - /** - * Converts MDNS Service Information into DeviceIdentifier according to the keys - * - * @param serviceInfo - * @return - */ - public static DeviceIdentifier resolveDeviceIdentifier(ServiceInfo serviceInfo) { - String manufacturer = serviceInfo.getPropertyString("Manufacturer"); - String model = serviceInfo.getPropertyString("Model"); - String serialNumber = serviceInfo.getPropertyString("SerialNumber"); - String firmwareVersion = serviceInfo.getPropertyString("FirmwareVersion"); - - return DeviceIdentifier.from(manufacturer, model, serialNumber, firmwareVersion); - } - - public static String convertDeviceIdentifierToRecordKeys(DeviceIdentifier identifier) { - StringBuffer buffer = new StringBuffer(); - buffer.append("Manufacturer=" + identifier.getManufacturer() + "\n"); - buffer.append("Model=" + identifier.getModel() + "\n"); - buffer.append("SerialNumber=" + identifier.getSerialNumber() + "\n"); - buffer.append("FirmwareVersion=" + identifier.getFirmwareVersion()); - return buffer.toString(); - } + /** + * Converts MDNS Service Information into DeviceIdentifier according to + * the keys in the mDNS text record. + * + *

For more details see: LXI Specification 1.5.01, Section 10.4.3 + * + * @param serviceInfo mDNS text record + * @return DeviceIdentifier + */ + public static DeviceIdentifier getDeviceIdentifier(ServiceInfo serviceInfo) { + String manufacturer = serviceInfo.getPropertyString("Manufacturer"); + String model = serviceInfo.getPropertyString("Model"); + String serialNumber = serviceInfo.getPropertyString("SerialNumber"); + String firmwareVersion = serviceInfo.getPropertyString("FirmwareVersion"); + + return DeviceIdentifier.from( + manufacturer, model, serialNumber, firmwareVersion); + } + + /** + * Returns the mDNS text record of a DeviceIdentifier according to the LXI + * mDNS discovery specification. + * + *

For more details see: LXI Specification 1.5.01, Section 10.4.3 + * + * @param identifier DeviceIdentifier + * @return mDNS TXT record + */ + public static String convertToTextRecord(DeviceIdentifier identifier) { + StringBuffer buffer = new StringBuffer(); + buffer.append("Manufacturer=" + identifier.getManufacturer() + "\n"); + buffer.append("Model=" + identifier.getModel() + "\n"); + buffer.append("SerialNumber=" + identifier.getSerialNumber() + "\n"); + buffer.append("FirmwareVersion=" + identifier.getFirmwareVersion()); + + return buffer.toString(); + } } diff --git a/measure-core/src/main/java/org/jtmc/core/lxi/mdns/MDNSDiscovery.java b/measure-core/src/main/java/org/jtmc/core/lxi/mdns/MDNSDiscovery.java index 412e856..bd90d2f 100644 --- a/measure-core/src/main/java/org/jtmc/core/lxi/mdns/MDNSDiscovery.java +++ b/measure-core/src/main/java/org/jtmc/core/lxi/mdns/MDNSDiscovery.java @@ -1,84 +1,95 @@ package org.jtmc.core.lxi.mdns; import java.io.IOException; +import java.net.InetAddress; import java.net.NetworkInterface; import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Set; - import javax.jmdns.JmDNS; import javax.jmdns.ServiceEvent; import javax.jmdns.ServiceListener; - import org.jtmc.core.lxi.LXIDiscovery; +import org.jtmc.core.lxi.LXIInstrumentEndpoint; import org.jtmc.core.lxi.raw.RawInstrumentEndpoint; import org.jtmc.core.lxi.vxi11.VXI11InstrumentEndpoint; +import org.jtmc.core.visa.DeviceIdentifier; /** - * MDNSDiscovery + * MDNSDiscovery implements LXI instrument discovery using the mDNS protocol. */ public class MDNSDiscovery implements LXIDiscovery { - private int timeout = 1000; + private int timeout = 1000; + + private List serviceTypes; - private List serviceTypes; + public MDNSDiscovery() { + this(MDNSServiceType.LXI); + } - public MDNSDiscovery() { - this(MDNSServiceType.LXI); - } + public MDNSDiscovery(MDNSServiceType... types) { + this(Arrays.asList(types)); + } - public MDNSDiscovery(MDNSServiceType... types) { - this(Arrays.asList(types)); - } + public MDNSDiscovery(List types) { + this.serviceTypes = new ArrayList<>(types); + } - public MDNSDiscovery(List types) { - this.serviceTypes = new ArrayList<>(types); - } + @Override + public Set discover(NetworkInterface networkInterface) throws IOException { + try (JmDNS jmdns = JmDNS.create(networkInterface.getInterfaceAddresses().get(0).getAddress())) { + Set devices = new HashSet<>(); + ServiceListener callback = new ServiceListener() { + + @Override + public void serviceResolved(ServiceEvent event) { + LXIInstrumentEndpoint endpoint = convertToEndpoint(event); + if (endpoint != null) { + devices.add(endpoint); + } + } + + @Override + public void serviceRemoved(ServiceEvent event) { + + } + + @Override + public void serviceAdded(ServiceEvent event) { - @Override - public Set discover(NetworkInterface networkInterface) throws IOException { - try(JmDNS jmdns = JmDNS.create(networkInterface.getInterfaceAddresses().get(0).getAddress())) { - Set devices = new HashSet<>(); - ServiceListener callback = new ServiceListener(){ - - @Override - public void serviceResolved(ServiceEvent event) { - LXIInstrumentEndpoint endpoint = getEndpoint(event); - if(endpoint != null) { - devices.add(endpoint); - } - } - - @Override - public void serviceRemoved(ServiceEvent event) { - - } - - @Override - public void serviceAdded(ServiceEvent event) { + } + }; + serviceTypes.forEach(type -> jmdns.addServiceListener(type.fqdn(), callback)); + + Thread.sleep(timeout); - } - }; - serviceTypes.forEach(type -> jmdns.addServiceListener(type.fqdn(), callback)); - - Thread.sleep(timeout); + return devices; + } catch (InterruptedException e) { + throw new IOException("MDNS discovery was interrupted."); + } + } - return devices; - } catch (InterruptedException e) { - throw new IOException("MDNS discovery was interrupted."); - } - } + /** + * Converts an mDNS service discovery event into a connectable + * LXI Instrument endpoint. + * + * @param event mDNS discovery event + * @return LXI Instrument endpoint + */ + private static LXIInstrumentEndpoint convertToEndpoint(ServiceEvent event) { + InetAddress host = event.getInfo().getInetAddresses()[0]; + int port = event.getInfo().getPort(); + DeviceIdentifier deviceIdentifier = MDNS.getDeviceIdentifier(event.getInfo()); - private static LXIInstrumentEndpoint getEndpoint(ServiceEvent event) { - if(event.getType().equals(MDNSServiceType.VXI11.fqdn())) { - return new VXI11InstrumentEndpoint(event.getInfo().getInetAddresses()[0], event.getInfo().getPort(), MDNS.resolveDeviceIdentifier(event.getInfo())); - } - else if(event.getType().equals(MDNSServiceType.SCPI_RAW.fqdn())) { - return new RawInstrumentEndpoint(event.getInfo().getInetAddresses()[0], event.getInfo().getPort(), MDNS.resolveDeviceIdentifier(event.getInfo())); - } - return null; - } + if (event.getType().equals(MDNSServiceType.VXI11.fqdn())) { + return new VXI11InstrumentEndpoint(host, port, deviceIdentifier); + } else if (event.getType().equals(MDNSServiceType.SCPI_RAW.fqdn())) { + return new RawInstrumentEndpoint(host, port, deviceIdentifier); + } + return null; + } -} \ No newline at end of file +} diff --git a/measure-core/src/main/java/org/jtmc/core/lxi/mdns/MDNSServiceType.java b/measure-core/src/main/java/org/jtmc/core/lxi/mdns/MDNSServiceType.java index c43939f..ec83850 100644 --- a/measure-core/src/main/java/org/jtmc/core/lxi/mdns/MDNSServiceType.java +++ b/measure-core/src/main/java/org/jtmc/core/lxi/mdns/MDNSServiceType.java @@ -1,31 +1,59 @@ package org.jtmc.core.lxi.mdns; /** - * Contains mDNS service types that are used to discover - * LXI capable devices + * Contains mDNS service types that are used to discover LXI capable devices. + * + *

For more details see: LXI Specification 1.5.01, Section 10.4.3.10 */ public enum MDNSServiceType { - LXI("_lxi._tcp.local"), - HTTP("_http._tcp.local"), - HISLIP("_hislip._tcp.local"), - SCPI_RAW("_scpi-raw._tcp.local"), - SCPI_TELNET("_scpi-telnet._tcp.local"), - VXI11("_vxi11._tcp.local"); - - private String fqdn; - - public final static MDNSServiceType[] ALL = MDNSServiceType.values(); - - private MDNSServiceType(String fqdn) { - this.fqdn = fqdn; - } - - /** - * Returns the fully qualified name of the mDNS service - * @return Fully Qualified mDNS service name - */ - public String fqdn() { - return fqdn; - } - -} \ No newline at end of file + + /** + * LXI Service type denotes an endpoint capable of identification via HTTP + * the service is expected to be on port 80. + */ + LXI("_lxi._tcp.local"), + + /** + * HTTP Service type denotes an endpoint that may be accessed + * from a web browser. + */ + HTTP("_http._tcp.local"), + + /** + * HiSLIP Service type denotes an endpoint hosting a HiSLIP server. + */ + HISLIP("_hislip._tcp.local"), + + /** + * SCPI Raw Service type denotes an endpoint hosting a TCP server + * interpreting raw SCPI commands. + */ + SCPI_RAW("_scpi-raw._tcp.local"), + + /** + * SCPI Telnet Service type denotes an endpoint hosting a Telnet server + * interpreting SCPI commands. + */ + SCPI_TELNET("_scpi-telnet._tcp.local"), + + /** + * VXI11 Service type denotes an endpoint hosting a VXI-11 Server. + */ + VXI11("_vxi11._tcp.local"); + + private String fqdn; + + public static final MDNSServiceType[] ALL = MDNSServiceType.values(); + + private MDNSServiceType(String fqdn) { + this.fqdn = fqdn; + } + + /** + * Returns the fully qualified name of the mDNS service. + * @return Fully Qualified mDNS service name + */ + public String fqdn() { + return fqdn; + } +} diff --git a/measure-core/src/main/java/org/jtmc/core/lxi/mdns/package-info.java b/measure-core/src/main/java/org/jtmc/core/lxi/mdns/package-info.java new file mode 100644 index 0000000..f2871e6 --- /dev/null +++ b/measure-core/src/main/java/org/jtmc/core/lxi/mdns/package-info.java @@ -0,0 +1,9 @@ +/** + * Contains classes related to LXI Instrument discovery using mDNS + * + *

The implementation uses the jMDNS library and provides an abstraction + * optimized for LXI instrument discovery + * + *

For more details see: LXI Specification 1.5.01, Section 10.3 + */ +package org.jtmc.core.lxi.mdns; diff --git a/measure-core/src/main/java/org/jtmc/core/lxi/package-info.java b/measure-core/src/main/java/org/jtmc/core/lxi/package-info.java index 258164b..137a821 100644 --- a/measure-core/src/main/java/org/jtmc/core/lxi/package-info.java +++ b/measure-core/src/main/java/org/jtmc/core/lxi/package-info.java @@ -1,11 +1,14 @@ /** - * Contains packages and classes related to communication with LAN based instruments + * Contains packages and classes related to communication + * with LAN based instruments * - *

The implementation of the protocols and interfaces are while required by the LXI specification, are - * not exclusive to LXI devices. In fact some manufacturers only partially satisfy the standard, for - * example they might have VXI-11, but they don't have a web interface or don't support mDNS. + *

The implementation of the protocols and interfaces are while required by + * the LXI specification, are not exclusive to LXI devices. + * In fact some manufacturers only partially satisfy the standard, for example + * they might have VXI-11, but they don't have a web interface + * or don't support mDNS. * - *

- * {@link https://www.lxistandard.org/Specifications/Specifications.aspx} + *

For more informations see + * LXI Specification. */ -package org.jtmc.core.lxi; \ No newline at end of file +package org.jtmc.core.lxi; diff --git a/measure-core/src/main/java/org/jtmc/core/lxi/raw/RawInstrumentEndpoint.java b/measure-core/src/main/java/org/jtmc/core/lxi/raw/RawInstrumentEndpoint.java index e30abb5..45dff36 100644 --- a/measure-core/src/main/java/org/jtmc/core/lxi/raw/RawInstrumentEndpoint.java +++ b/measure-core/src/main/java/org/jtmc/core/lxi/raw/RawInstrumentEndpoint.java @@ -2,19 +2,22 @@ import java.io.IOException; import java.net.InetAddress; - -import org.jtmc.core.lxi.LXIDiscovery.LXIInstrumentEndpoint; +import org.jtmc.core.lxi.LXIInstrumentEndpoint; import org.jtmc.core.visa.DeviceIdentifier; +/** + * RawInstrumentEndpoint is used to instantiate raw TCP/IP connections + * to instruments. + */ public class RawInstrumentEndpoint extends LXIInstrumentEndpoint { - public RawInstrumentEndpoint(InetAddress host, int port, DeviceIdentifier deviceIdentifier) { - super(host, port, deviceIdentifier); - } + public RawInstrumentEndpoint(InetAddress host, int port, DeviceIdentifier deviceIdentifier) { + super(host, port, deviceIdentifier); + } + + @Override + public RawSocket connect() throws IOException { + return new RawSocket(this.getHost(), this.getPort(), 0); + } - @Override - public RawSocket connect() throws IOException { - return new RawSocket(this.getHost(), this.getPort(), 0); - } - } diff --git a/measure-core/src/main/java/org/jtmc/core/lxi/raw/RawSocket.java b/measure-core/src/main/java/org/jtmc/core/lxi/raw/RawSocket.java index 562ea9e..5fb3a8e 100644 --- a/measure-core/src/main/java/org/jtmc/core/lxi/raw/RawSocket.java +++ b/measure-core/src/main/java/org/jtmc/core/lxi/raw/RawSocket.java @@ -6,127 +6,137 @@ import java.net.SocketTimeoutException; import java.nio.ByteBuffer; import java.util.ArrayList; - import org.jtmc.core.device.ISocket; /** - * RawSocket implements a generic TCP/IP socket for sending and receiving byte streams + * RawSocket implements a generic TCP/IP socket for sending and receiving byte streams. */ public class RawSocket implements ISocket { - private final InetAddress host; - - private final int port; - - private final int board; - - private transient Socket socket; - - public RawSocket(String host, int port) throws IOException { - this(host, port, 0); - } - - public RawSocket(String host, int port, int board) throws IOException { - this(InetAddress.getByName(host), port, board); - } - - public RawSocket(InetAddress host, int port, int board) throws IOException { - if(board < 0) { - throw new IllegalArgumentException("Board number must be positive: " + board); - } - this.host = host; - this.port = port; - this.board = board; - socket = new Socket(host, port); - } - - public int getBoard() { - return board; - } - - public InetAddress getHost() { - return host; - } - - public int getPort() { - return port; - } - - @Override - public String getResourceString() { - return String.format("TCP%d::%s::%d::SOCKET", board, host.getHostAddress(), port); - } - - @Override - public String toString() { - return String.format("RawSocket[host=%s, port=%d, board=%d]", host.getHostAddress(), port, board); - } - - @Override - public void send(ByteBuffer message) throws IOException { - if(socket == null) { - throw new IOException("Socket not connected"); - } - if(!message.hasArray()) { - throw new IOException("Message is empty."); - } - socket.getOutputStream().write(message.array()); - } - - @Override - public ByteBuffer receive(final int count, final long timeout) throws IOException, SocketTimeoutException { - int previousTimeout = socket.getSoTimeout(); - try { - socket.setSoTimeout((int)timeout); - - ByteBuffer buffer = ByteBuffer.allocate(count); - int read = 0; - while(read < count) { - buffer.put((byte)socket.getInputStream().read()); - read++; - } - return buffer; - } finally { - socket.setSoTimeout((int)previousTimeout); - } - } - - @Override - public ByteBuffer receive(final char delimiter, final long timeout) throws IOException, SocketTimeoutException { - int previousTimeout = socket.getSoTimeout(); - try { - socket.setSoTimeout((int)timeout); - - ArrayList input = new ArrayList<>(); - byte in = (byte) socket.getInputStream().read(); - while(in != delimiter) { - input.add(in); - in = (byte) socket.getInputStream().read(); - } - ByteBuffer bytes = ByteBuffer.allocate(input.size()); - for(byte b : input) { - bytes.put(b); - } - return bytes; - } finally { - socket.setSoTimeout((int)previousTimeout); - } - } - - @Override - public void close() { - try { - socket.close(); - } catch (IOException | NullPointerException e) { - //TODO: warn - } finally { - socket = null; - } - } - - @Override - public boolean isConnected() { - return socket != null && !socket.isClosed(); - } - -} \ No newline at end of file + private final InetAddress host; + + private final int port; + + private final int board; + + private transient Socket socket; + + public RawSocket(String host, int port) throws IOException { + this(host, port, 0); + } + + public RawSocket(String host, int port, int board) throws IOException { + this(InetAddress.getByName(host), port, board); + } + + /** + * Creates a new RawSocket using the IP address, port and board number. + * + * @param host IP Address of the instrument + * @param port Port number of the instrument + * @param board Board number (not used) + * @throws IOException if there was an error when connecting + */ + public RawSocket(InetAddress host, int port, int board) throws IOException { + if (board < 0) { + throw new IllegalArgumentException("Board number must be positive: " + board); + } + this.host = host; + this.port = port; + this.board = board; + socket = new Socket(host, port); + } + + public int getBoard() { + return board; + } + + public InetAddress getHost() { + return host; + } + + public int getPort() { + return port; + } + + @Override + public String getResourceString() { + return String.format("TCP%d::%s::%d::SOCKET", board, host.getHostAddress(), port); + } + + @Override + public String toString() { + return String.format("RawSocket[host=%s, port=%d, board=%d]", + host.getHostAddress(), port, board); + } + + @Override + public void send(ByteBuffer message) throws IOException { + if (socket == null) { + throw new IOException("Socket not connected"); + } + if (!message.hasArray()) { + throw new IOException("Message is empty."); + } + socket.getOutputStream().write(message.array()); + } + + @Override + public ByteBuffer receive(final int count, final long timeout) + throws IOException, SocketTimeoutException { + int previousTimeout = socket.getSoTimeout(); + try { + socket.setSoTimeout((int) timeout); + + ByteBuffer buffer = ByteBuffer.allocate(count); + int read = 0; + while (read < count) { + buffer.put((byte) socket.getInputStream().read()); + read++; + } + return buffer; + } finally { + socket.setSoTimeout((int) previousTimeout); + } + } + + @Override + public ByteBuffer receive(final char delimiter, final long timeout) + throws IOException, SocketTimeoutException { + int previousTimeout = socket.getSoTimeout(); + try { + socket.setSoTimeout((int) timeout); + + ArrayList input = new ArrayList<>(); + byte in = (byte) socket.getInputStream().read(); + while (in != delimiter) { + input.add(in); + in = (byte) socket.getInputStream().read(); + } + ByteBuffer bytes = ByteBuffer.allocate(input.size()); + for (byte b : input) { + bytes.put(b); + } + return bytes; + } finally { + socket.setSoTimeout((int) previousTimeout); + } + } + + @Override + public void close() { + try { + socket.close(); + } catch (IOException | NullPointerException e) { + //TODO: warn + } finally { + socket = null; + } + } + + @Override + public boolean isConnected() { + return socket != null && !socket.isClosed(); + } + +} diff --git a/measure-core/src/main/java/org/jtmc/core/lxi/raw/RawSocketFactory.java b/measure-core/src/main/java/org/jtmc/core/lxi/raw/RawSocketFactory.java index 4458712..8966233 100644 --- a/measure-core/src/main/java/org/jtmc/core/lxi/raw/RawSocketFactory.java +++ b/measure-core/src/main/java/org/jtmc/core/lxi/raw/RawSocketFactory.java @@ -3,41 +3,42 @@ import java.io.IOException; import java.util.regex.Matcher; import java.util.regex.Pattern; - import org.jtmc.core.visa.exception.UnsupportedSocketException; import org.jtmc.core.visa.factory.ISocketFactory; /** * This factory class is capable of creating Raw TCP sockets for SCPI - * communication + * communication. + * + *

It supports the VISA resource string syntax for raw tcp sockets. * - *

- * It supports the VISA resource string syntax for raw tcp sockets. - *

- * Format: TCPIP[board]::[host address]::[port]::SOCKET + *

Format: TCPIP[board]::[host address]::[port]::SOCKET */ public class RawSocketFactory implements ISocketFactory { - private Pattern pattern = Pattern.compile("TCPIP(?[0-9]*)::(?.{1,})::(?[0-9]{1,5})::SOCKET"); + private static final Pattern pattern = Pattern.compile( + "TCPIP(?[0-9]*)::(?.{1,})::(?[0-9]{1,5})::SOCKET"); + + @Override + public boolean supports(String connectionInfo) { + return pattern.matcher(connectionInfo).matches(); + } + + @Override + public RawSocket create(String resourceString) + throws IOException, UnsupportedSocketException { - @Override - public boolean supports(String connectionInfo) { - return pattern.matcher(connectionInfo).matches(); - } + Matcher matcher = pattern.matcher(resourceString); + if (!matcher.matches()) { + throw new UnsupportedSocketException(); + } + String host = matcher.group("host"); + int port = Integer.parseInt(matcher.group("port")); + int board = 0; + if (!matcher.group("board").trim().isEmpty()) { + board = Integer.parseInt(matcher.group("board")); + } + return new RawSocket(host, port, board); + } - @Override - public RawSocket create(String resourceString) throws IOException, UnsupportedSocketException { - Matcher matcher = pattern.matcher(resourceString); - if(!matcher.matches()) { - throw new UnsupportedSocketException(); - } - String host = matcher.group("host"); - int port = Integer.parseInt(matcher.group("port")); - int board = 0; - if(!matcher.group("board").trim().isEmpty()) { - board = Integer.parseInt(matcher.group("board")); - } - return new RawSocket(host, port, board); - } - -} \ No newline at end of file +} diff --git a/measure-core/src/main/java/org/jtmc/core/lxi/vxi11/VXI11.java b/measure-core/src/main/java/org/jtmc/core/lxi/vxi11/VXI11.java index b0ebd28..735bb6f 100644 --- a/measure-core/src/main/java/org/jtmc/core/lxi/vxi11/VXI11.java +++ b/measure-core/src/main/java/org/jtmc/core/lxi/vxi11/VXI11.java @@ -3,7 +3,9 @@ import java.lang.reflect.Field; /** - * VXI-11 contains constants specified in the VXI-11 standard, these include: + * VXI-11 contains constants specified in the VXI-11 standard. + * + *

List of contents: *

    *
  • * Program identifiers @@ -11,101 +13,103 @@ * Procedure numbers *
  • * Error codes + *
*/ public class VXI11 { - /** - * Device core functionality - */ - public static class DeviceCore { - public final static int PROGRAM = 0x0607AF; - public final static int VERSION = 1; + /** + * Device core functionality. + */ + public static class DeviceCore { + public static final int PROGRAM = 0x0607AF; + public static final int VERSION = 1; - public final static int CREATE_LINK = 10; - public final static int DEVICE_WRITE = 11; - public final static int DEVICE_READ = 12; - public final static int DEVICE_READSTB = 13; - public final static int DEVICE_TRIGGER = 14; - public final static int DEVICE_CLEAR = 15; - public final static int DEVICE_REMOTE = 16; - public final static int DEVICE_LOCAL = 17; - public final static int DEVICE_LOCK = 18; - public final static int DEVICE_UNLOCK = 19; - public final static int DEVICE_ENABLE_SRQ = 20; - public final static int DEVICE_DOCMD = 22; - public final static int DESTROY_LINK = 23; - public final static int CREATE_INTR_CHANNEL = 25; - public final static int DESTROY_INTR_CHANNEL = 26; - } + public static final int CREATE_LINK = 10; + public static final int DEVICE_WRITE = 11; + public static final int DEVICE_READ = 12; + public static final int DEVICE_READSTB = 13; + public static final int DEVICE_TRIGGER = 14; + public static final int DEVICE_CLEAR = 15; + public static final int DEVICE_REMOTE = 16; + public static final int DEVICE_LOCAL = 17; + public static final int DEVICE_LOCK = 18; + public static final int DEVICE_UNLOCK = 19; + public static final int DEVICE_ENABLE_SRQ = 20; + public static final int DEVICE_DOCMD = 22; + public static final int DESTROY_LINK = 23; + public static final int CREATE_INTR_CHANNEL = 25; + public static final int DESTROY_INTR_CHANNEL = 26; + } - /** - * Interrupt functionality (optional) - */ - public static class DeviceInterrupt { - public final static int PROGRAM = 0x0607B1; - public final static int VERSION = 1; + /** + * Interrupt functionality (optional). + */ + public static class DeviceInterrupt { + public static final int PROGRAM = 0x0607B1; + public static final int VERSION = 1; - public final static int DEVICE_INTR_SRQ = 30; - } + public static final int DEVICE_INTR_SRQ = 30; + } - /** - * Asynchronous functionality (optional) - */ - public static class DeviceAsync { - public final static int PROGRAM = 0x0607B0; - public final static int VERSION = 1; + /** + * Asynchronous functionality (optional). + */ + public static class DeviceAsync { + public static final int PROGRAM = 0x0607B0; + public static final int VERSION = 1; - public final static int DEVICE_ABORT = 1; - } + public static final int DEVICE_ABORT = 1; + } + + /** + * Error codes. + * + *

Found in VXI-11 Rev 1.0 specification Table B.2 + */ + public static class ErrorCode { + public static final int NO_ERROR = 0; + public static final int SYNTAX_ERROR = 2; + public static final int DEVICE_NOT_ACCESSIBLE = 3; + public static final int INVALID_LINK_IDENTIFIER = 4; + public static final int PARAMETER_ERROR = 5; + public static final int CHANNEL_NOT_ESTABLISHED = 6; + public static final int OPERATION_NOT_SUPPORTED = 8; + public static final int OUT_OF_RESOURCES = 9; + public static final int DEVICE_LOCKED_BY_ANOTHER_LINK = 11; + public static final int NO_LOCK_HELD_BY_THIS_LINK = 12; + public static final int IO_TIMEOUT = 15; + public static final int IO_ERROR = 17; + public static final int INVALID_ADDRESS = 21; + public static final int ABORT = 23; + public static final int CHANNEL_ALREADY_ESTABLISHED = 29; /** - * Error codes + * Returns the name of the error for the given code. * - *

- * Found in VXI-11 Rev 1.0 specification Table B.2 + * @param code Error code + * @return Error name */ - public static class ErrorCode { - public final static int NO_ERROR = 0; - public final static int SYNTAX_ERROR = 2; - public final static int DEVICE_NOT_ACCESSIBLE = 3; - public final static int INVALID_LINK_IDENTIFIER = 4; - public final static int PARAMETER_ERROR = 5; - public final static int CHANNEL_NOT_ESTABLISHED = 6; - public final static int OPERATION_NOT_SUPPORTED = 8; - public final static int OUT_OF_RESOURCES = 9; - public final static int DEVICE_LOCKED_BY_ANOTHER_LINK = 11; - public final static int NO_LOCK_HELD_BY_THIS_LINK = 12; - public final static int IO_TIMEOUT = 15; - public final static int IO_ERROR = 17; - public final static int INVALID_ADDRESS = 21; - public final static int ABORT = 23; - public final static int CHANNEL_ALREADY_ESTABLISHED = 29; - - /** - * Returns the name of the error for the given code - * @param code Error code - * @return Error name - */ - public static String getErrorName(int code) { - try { - for(Field field : ErrorCode.class.getFields()) { - if(field.getType() == int.class && field.getInt(null) == code) { - return field.getName(); - } - } - return "UNKNOWN"; - } catch(IllegalAccessException e) { - return "UNKNOWN"; - } + public static String getErrorName(int code) { + try { + for (Field field : ErrorCode.class.getFields()) { + if (field.getType() == int.class && field.getInt(null) == code) { + return field.getName(); + } } + return "UNKNOWN"; + } catch (IllegalAccessException e) { + return "UNKNOWN"; + } + } - /** - * Returns an error string consisting of the error name and error code - * @param code Error code - * @return Error string in the format 'NAME (CODE)' - */ - public static String getErrorString(int code) { - return getErrorName(code) + " (" + code + ")"; - } + /** + * Returns an error string consisting of the error name and error code. + * + * @param code Error code + * @return Error string in the format 'NAME (CODE)' + */ + public static String getErrorString(int code) { + return getErrorName(code) + " (" + code + ")"; } -} \ No newline at end of file + } +} diff --git a/measure-core/src/main/java/org/jtmc/core/lxi/vxi11/VXI11Discovery.java b/measure-core/src/main/java/org/jtmc/core/lxi/vxi11/VXI11Discovery.java index 5dffd4f..735da07 100644 --- a/measure-core/src/main/java/org/jtmc/core/lxi/vxi11/VXI11Discovery.java +++ b/measure-core/src/main/java/org/jtmc/core/lxi/vxi11/VXI11Discovery.java @@ -9,7 +9,6 @@ import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; - import org.acplt.oncrpc.OncRpcBroadcastEvent; import org.acplt.oncrpc.OncRpcBroadcastListener; import org.acplt.oncrpc.OncRpcException; @@ -25,141 +24,147 @@ /** * VXI11Discovery implements RPC based instrument discovery * - *

- * This implementation broadcasts RPC Portmap queries over UDP. The portmap + *

This implementation broadcasts RPC Portmap queries over UDP. The portmap * parameters specifically look for the existence of the VXI-11 Device Core * program over TCP. */ public class VXI11Discovery implements LXIDiscovery { - /** - * Default timeout, 1 second as specified in the LXI standard - */ - public final static int DEFAULT_TIMEOUT = 1000; - - private int timeout; - - private boolean resolveDeviceInfo; - - private int retransmissions; - - /** - * Constructs a new VXI-11 discovery tool using the default parameters - * - *

    - *
  • 1 second timeout - *
  • Don't resolve device identifiers - *
  • Retransmit 1 time - */ - public VXI11Discovery() { - this(false); - } - - /** - * Constructs a new VXI-11 discovery tool - * - * @param resolveDeviceInfo If {@code true} then after discovering devices it - * will attempt to do an identification query - */ - public VXI11Discovery(boolean resolveDeviceInfo) { - this(resolveDeviceInfo, 1); - } - - /** - * Constructs a new VXI-11 discovery tool - * - * @param resolveDeviceInfo If {@code true} then after discovering devices it - * will attempt to do an identification query - * @param retransmissions The number of retransmissions when broadcasting the - * portmap message, {@code 0} specifies no - * retranmissions so it will do a total of {@code 1} - * tranmission - */ - public VXI11Discovery(boolean resolveDeviceInfo, int retransmissions) { - this(resolveDeviceInfo, retransmissions, DEFAULT_TIMEOUT); - } - - /** - * Constructs a new VXI-11 discovery tool - * - * @param resolveDeviceInfo If {@code true} then after discovering devices it - * will attempt to do an identification query - * @param retransmissions The number of retransmissions when broadcasting the - * portmap message, {@code 0} specifies no - * retranmissions so it will do a total of {@code 1} - * tranmission - * @param timeout Time to wait for portmap responses in milliseconds - */ - public VXI11Discovery(boolean resolveDeviceInfo, int retransmissions, int timeout) { - this.timeout = timeout; - this.resolveDeviceInfo = resolveDeviceInfo; - this.retransmissions = retransmissions; - } - - @Override - public Set discover(NetworkInterface networkInterface) throws IOException { - try { - InetAddress bcast = getBroadcastAddress(networkInterface); - OncRpcUdpClient rpcClient = (OncRpcUdpClient) OncRpcUdpClient.newOncRpcClient(bcast, Portmap.PROGRAM, - Portmap.VERSION, Portmap.PORT, OncRpcProtocols.ONCRPC_UDP); - rpcClient.setTimeout(timeout); - - Set devices = new HashSet<>(); - - PortmapResponse response = new PortmapResponse(); - OncRpcBroadcastListener callback = new OncRpcBroadcastListener() { - @Override - public void replyReceived(OncRpcBroadcastEvent evt) { - VXI11InstrumentEndpoint endpoint = new VXI11InstrumentEndpoint(evt.getReplyAddress(), - response.getPort(), DeviceIdentifier.UNKNOWN); - devices.add(endpoint); - } - }; - - for (int i = 0; i < retransmissions + 1; i++) { - rpcClient.broadcastCall(Portmap.PROC_GETPORT, new PortmapParams(VXI11.DeviceCore.PROGRAM, - VXI11.DeviceCore.VERSION, OncRpcProtocols.ONCRPC_TCP, 4), response, callback); - } - - if (resolveDeviceInfo) { - Set resolved = devices.stream().map(endpoint -> { - try (VXI11Socket socket = endpoint.connect()) { - RawSCPISocket scpiSocket = new RawSCPISocket(socket); - return new VXI11InstrumentEndpoint(endpoint.getHost(), endpoint.getPort(), - scpiSocket.getDeviceIdentifier()); - } catch (IOException e) { - return endpoint; - } - }).collect(Collectors.toSet()); - return resolved; - } - - return devices; - } catch (OncRpcException e) { - throw new IOException(e); - } catch (IllegalArgumentException e) { - throw new IOException(e); - } - } - - /** - * Returns a broadcast address for the given network interface - * - * @param networkInterface Network interface - * @return Broadcast IP Address - * @throws SocketException If the network interface is unavailable - */ - private static InetAddress getBroadcastAddress(NetworkInterface networkInterface) throws SocketException { - if (!networkInterface.isUp() || networkInterface.isVirtual()) { - throw new IllegalArgumentException( - "Network interface '" + networkInterface.getName() + "' unavailable or is virtual."); - } - Optional address = networkInterface.getInterfaceAddresses().stream() - .filter(addr -> addr.getBroadcast() != null).findFirst(); - return address - .orElseThrow(() -> new IllegalArgumentException( - "Network interface '" + networkInterface.getName() + "' has no broadcast address")) - .getBroadcast(); - } - -} \ No newline at end of file + /** + * Default timeout, 1 second as specified in the LXI standard. + */ + public static final int DEFAULT_TIMEOUT = 1000; + + private int timeout; + + private boolean resolveDeviceInfo; + + private int retransmissions; + + /** + * Constructs a new VXI-11 discovery tool using the default parameters. + * + *
      + *
    • 1 second timeout + *
    • Don't resolve device identifiers + *
    • Retransmit 1 time + *
    + */ + public VXI11Discovery() { + this(false); + } + + /** + * Constructs a new VXI-11 discovery tool. + * + * @param resolveDeviceInfo If {@code true} then after discovering devices it + * will attempt to do an identification query + */ + public VXI11Discovery(boolean resolveDeviceInfo) { + this(resolveDeviceInfo, 1); + } + + /** + * Constructs a new VXI-11 discovery tool. + * + * @param resolveDeviceInfo If {@code true} then after discovering devices it + * will attempt to do an identification query + * @param retransmissions The number of retransmissions when broadcasting the + * portmap message, {@code 0} specifies no + * retranmissions so it will do a total of {@code 1} + * tranmission + */ + public VXI11Discovery(boolean resolveDeviceInfo, int retransmissions) { + this(resolveDeviceInfo, retransmissions, DEFAULT_TIMEOUT); + } + + /** + * Constructs a new VXI-11 discovery tool. + * + * @param resolveDeviceInfo If {@code true} then after discovering devices it + * will attempt to do an identification query + * @param retransmissions The number of retransmissions when broadcasting the + * portmap message, {@code 0} specifies no + * retranmissions so it will do a total of {@code 1} + * tranmission + * @param timeout Time to wait for portmap responses in milliseconds + */ + public VXI11Discovery(boolean resolveDeviceInfo, int retransmissions, int timeout) { + this.timeout = timeout; + this.resolveDeviceInfo = resolveDeviceInfo; + this.retransmissions = retransmissions; + } + + @Override + public Set discover( + NetworkInterface networkInterface) throws IOException { + try { + InetAddress bcast = getBroadcastAddress(networkInterface); + OncRpcUdpClient rpcClient = (OncRpcUdpClient) OncRpcUdpClient.newOncRpcClient(bcast, + Portmap.PROGRAM, Portmap.VERSION, Portmap.PORT, OncRpcProtocols.ONCRPC_UDP); + rpcClient.setTimeout(timeout); + + Set devices = new HashSet<>(); + + PortmapResponse response = new PortmapResponse(); + OncRpcBroadcastListener callback = new OncRpcBroadcastListener() { + @Override + public void replyReceived(OncRpcBroadcastEvent evt) { + VXI11InstrumentEndpoint endpoint = new VXI11InstrumentEndpoint(evt.getReplyAddress(), + response.getPort(), DeviceIdentifier.UNKNOWN); + devices.add(endpoint); + } + }; + + //TODO: find out where the 4 comes from + PortmapParams portmapRequest = new PortmapParams( + VXI11.DeviceCore.PROGRAM, VXI11.DeviceCore.VERSION, OncRpcProtocols.ONCRPC_TCP, 4); + + for (int i = 0; i < retransmissions + 1; i++) { + rpcClient.broadcastCall(Portmap.PROC_GETPORT, portmapRequest, response, callback); + } + + if (resolveDeviceInfo) { + Set resolved = devices.stream().map(endpoint -> { + try (VXI11Socket socket = endpoint.connect()) { + RawSCPISocket scpiSocket = new RawSCPISocket(socket); + return new VXI11InstrumentEndpoint(endpoint.getHost(), endpoint.getPort(), + scpiSocket.getDeviceIdentifier()); + } catch (IOException e) { + return endpoint; + } + }).collect(Collectors.toSet()); + return resolved; + } + + return devices; + } catch (OncRpcException e) { + throw new IOException(e); + } catch (IllegalArgumentException e) { + throw new IOException(e); + } + } + + /** + * Returns a broadcast address for the given network interface. + * + * @param networkInterface Network interface + * @return Broadcast IP Address + * @throws SocketException If the network interface is unavailable + */ + private static InetAddress getBroadcastAddress( + NetworkInterface networkInterface) throws SocketException { + + if (!networkInterface.isUp() || networkInterface.isVirtual()) { + throw new IllegalArgumentException( + "Network interface '" + networkInterface.getName() + "' unavailable or is virtual."); + } + Optional address = networkInterface.getInterfaceAddresses().stream() + .filter(addr -> addr.getBroadcast() != null).findFirst(); + return address + .orElseThrow(() -> new IllegalArgumentException( + "Network interface '" + networkInterface.getName() + "' has no broadcast address")) + .getBroadcast(); + } + +} diff --git a/measure-core/src/main/java/org/jtmc/core/lxi/vxi11/VXI11Exception.java b/measure-core/src/main/java/org/jtmc/core/lxi/vxi11/VXI11Exception.java index 8ad611d..e0e3217 100644 --- a/measure-core/src/main/java/org/jtmc/core/lxi/vxi11/VXI11Exception.java +++ b/measure-core/src/main/java/org/jtmc/core/lxi/vxi11/VXI11Exception.java @@ -1,34 +1,35 @@ package org.jtmc.core.lxi.vxi11; import java.io.IOException; - import org.jtmc.core.lxi.vxi11.VXI11.ErrorCode; /** - * VXI11Exception is a convenience exception for indicating an exception while using the VXI-11 Protocol + * VXI11Exception is used for indicating an exception while using + * the VXI-11 Protocol. */ public class VXI11Exception extends IOException { - private static final long serialVersionUID = 9011406167109584636L; + private static final long serialVersionUID = 9011406167109584636L; - /** - * Constructs a VXI11Exception based on the error code - * @param code VXI-11 Error code - */ - public VXI11Exception(int code) { - super("VXI11 Error: " + ErrorCode.getErrorString(code)); - } + /** + * Constructs a VXI11Exception based on the error code. + * + * @param code VXI-11 Error code + */ + public VXI11Exception(int code) { + super("VXI-11 Error: " + ErrorCode.getErrorString(code)); + } - public VXI11Exception(String message) { - super(message); - } + public VXI11Exception(String message) { + super(message); + } - public VXI11Exception(Throwable cause) { - super(cause); - } + public VXI11Exception(Throwable cause) { + super(cause); + } - public VXI11Exception(String message, Throwable cause) { - super(message, cause); - } + public VXI11Exception(String message, Throwable cause) { + super(message, cause); + } -} \ No newline at end of file +} diff --git a/measure-core/src/main/java/org/jtmc/core/lxi/vxi11/VXI11InstrumentEndpoint.java b/measure-core/src/main/java/org/jtmc/core/lxi/vxi11/VXI11InstrumentEndpoint.java index 0191ec7..70d74a8 100644 --- a/measure-core/src/main/java/org/jtmc/core/lxi/vxi11/VXI11InstrumentEndpoint.java +++ b/measure-core/src/main/java/org/jtmc/core/lxi/vxi11/VXI11InstrumentEndpoint.java @@ -2,19 +2,25 @@ import java.io.IOException; import java.net.InetAddress; - -import org.jtmc.core.lxi.LXIDiscovery.LXIInstrumentEndpoint; +import org.jtmc.core.lxi.LXIInstrumentEndpoint; import org.jtmc.core.visa.DeviceIdentifier; +/** + * VXI11InstrumentEndpoint is used to instantiate VXI-11 connections + * to instruments. + */ public class VXI11InstrumentEndpoint extends LXIInstrumentEndpoint { - public VXI11InstrumentEndpoint(InetAddress host, int port, DeviceIdentifier deviceIdentifier) { - super(host, port, deviceIdentifier); - } + public VXI11InstrumentEndpoint( + InetAddress host, + int port, + DeviceIdentifier deviceIdentifier) { + super(host, port, deviceIdentifier); + } - @Override - public VXI11Socket connect() throws IOException { - return new VXI11Socket(this.getHost(), this.getPort()); - } + @Override + public VXI11Socket connect() throws IOException { + return new VXI11Socket(this.getHost(), this.getPort()); + } -} \ No newline at end of file +} diff --git a/measure-core/src/main/java/org/jtmc/core/lxi/vxi11/VXI11Socket.java b/measure-core/src/main/java/org/jtmc/core/lxi/vxi11/VXI11Socket.java index 5853d9e..a924aa8 100644 --- a/measure-core/src/main/java/org/jtmc/core/lxi/vxi11/VXI11Socket.java +++ b/measure-core/src/main/java/org/jtmc/core/lxi/vxi11/VXI11Socket.java @@ -6,7 +6,6 @@ import java.net.SocketTimeoutException; import java.nio.ByteBuffer; import java.util.ArrayList; - import org.acplt.oncrpc.OncRpcClient; import org.acplt.oncrpc.OncRpcException; import org.acplt.oncrpc.OncRpcProtocols; @@ -22,172 +21,237 @@ import org.jtmc.core.lxi.vxi11.rpc.DeviceWriteResponse; /** - * VXI11Socket + * VXI11Socket implements the VXI11 RPC interface. */ public class VXI11Socket implements ISocket { - public final static int DEFAULT_IO_TIMEOUT = 2000; - - public final static int DEFAULT_WRITE_BLOCK_SIZE = 8128; - - //TODO: update value - public final static int CLIENT_ID = 1234567; - - private final InetAddress host; - - private final int port; - - private final String name; - - private int lockTimeout; - - private int ioTimeout; - - private int writeBlockSize; - - private transient OncRpcClient client; - - private transient CreateLinkResponse link; - - public VXI11Socket(final InetAddress host) throws IOException { - this(host, 0, "inst0"); - } - - public VXI11Socket(final InetAddress host, final int port) throws IOException { - this(host, port, "inst0"); - } - - public VXI11Socket(final InetAddress host, final int port, final String instrumentName) throws IOException { - this(host, instrumentName, port, false, 0, DEFAULT_IO_TIMEOUT, DEFAULT_WRITE_BLOCK_SIZE); - } - - public VXI11Socket(final InetAddress host, final String name, int port, boolean lock, int lockTimeout, int ioTimeout, int writeBlockSize) throws IOException { - this.host = host; - this.port = port; - this.name = name; - this.lockTimeout = lockTimeout; - this.ioTimeout = ioTimeout; - this.writeBlockSize = writeBlockSize; - - try { - client = OncRpcClient.newOncRpcClient(host, VXI11.DeviceCore.PROGRAM, VXI11.DeviceCore.VERSION, port, OncRpcProtocols.ONCRPC_TCP); - link = createLink(CLIENT_ID, lock, lockTimeout, name); - } catch (OncRpcException e) { - client = null; - throw new IOException(e); - } - } - - private CreateLinkResponse createLink(int clientId, boolean lockDevice, int lockTimeout, String device) throws OncRpcException { - CreateLinkResponse response = new CreateLinkResponse(); - client.call(VXI11.DeviceCore.CREATE_LINK, VXI11.DeviceCore.VERSION, new CreateLinkParams(clientId, lockDevice, lockTimeout, device), response); - return response; - } - - private DeviceWriteResponse write(int linkId, int ioTimeout, int lockTimeout, DeviceFlags flags, byte... data) throws OncRpcException { - DeviceWriteParams params = new DeviceWriteParams(new DeviceLink(linkId), ioTimeout, lockTimeout, flags, data); - DeviceWriteResponse response = new DeviceWriteResponse(); - client.call(VXI11.DeviceCore.DEVICE_WRITE, VXI11.DeviceCore.VERSION, params, response); - return response; - } - - private DeviceReadResponse read(int linkId, int size, int ioTimeout, int lockTimeout, DeviceFlags flags, byte termination) throws OncRpcException { - DeviceReadParams params = new DeviceReadParams(new DeviceLink(linkId), size, ioTimeout, lockTimeout, flags, termination); - DeviceReadResponse response = new DeviceReadResponse(); - client.call(VXI11.DeviceCore.DEVICE_READ, VXI11.DeviceCore.VERSION, params, response); - return response; - } - - private DeviceError destroyLink(int linkId) throws OncRpcException { - DeviceError error = new DeviceError(); - client.call(VXI11.DeviceCore.DESTROY_LINK, VXI11.DeviceCore.VERSION, new DeviceLink(linkId), error); - return error; - } - - public int getPort() { - return port; - } - - @Override - public void close() { - try { - destroyLink(link.getLinkId()); - client.close(); - } catch(OncRpcException e) { - //log warning - } finally { - client = null; - } - } - - @Override - public boolean isConnected() { - return client != null; - } - - @Override - public String getResourceString() { - return "TCPIP::" + this.host + "::" + this.name + "::INSTR"; - } - - @Override - public void send(ByteBuffer message) throws IOException { - if(!message.hasArray()) { - throw new IOException("Message empty."); - } - try { - int offset = 0; - int size = message.capacity(); - byte[] block = new byte[Math.min(size, writeBlockSize)]; - while(offset < size) { - message.get(block, offset, size - offset > writeBlockSize ? writeBlockSize : size); - DeviceFlags flags = new DeviceFlags(false, offset + writeBlockSize >= size, false); - DeviceWriteResponse response = this.write(link.getLinkId(), ioTimeout, lockTimeout, flags, block); - if(response.getError() != VXI11.ErrorCode.NO_ERROR) { - throw new VXI11Exception(response.getError()); - } - offset += writeBlockSize; - } - } catch(OncRpcException e) { - throw new IOException(e); - } - } - - @Override - public ByteBuffer receive(int count, long timeout) throws IOException, SocketTimeoutException { - try { - DeviceReadResponse response = this.read(link.getLinkId(), count, (int)timeout, lockTimeout, new DeviceFlags(false, false, false), (byte)0); - if(response.getError() != VXI11.ErrorCode.NO_ERROR) { - throw new VXI11Exception(response.getError()); - } - return ByteBuffer.wrap(response.data); - } catch(OncRpcException e) { - throw new IOException(e); - } - } - - @Override - public ByteBuffer receive(char delimiter, long timeout) throws IOException, SocketTimeoutException { - try { - ArrayList buffer = new ArrayList<>(); - DeviceReadResponse response = null; - while(response == null || !response.isEOISet()) { - response = this.read(link.getLinkId(), SocketOptions.SO_RCVBUF, (int)timeout, lockTimeout, new DeviceFlags(false, false, false), (byte)delimiter); - if(response.getError() != VXI11.ErrorCode.NO_ERROR) { - throw new VXI11Exception(response.getError()); - } - } - - buffer.add(response.data); - int length = buffer.stream().mapToInt(array -> array.length).sum(); - ByteBuffer data = ByteBuffer.allocate(length); - buffer.stream().forEach(data::put); - - return data; - } - catch(OncRpcException e) { - throw new IOException(e); - } - } - -} \ No newline at end of file + public static final int DEFAULT_IO_TIMEOUT = 2000; + + public static final int DEFAULT_WRITE_BLOCK_SIZE = 8128; + + //TODO: update value + public static final int CLIENT_ID = 1234567; + + private final InetAddress host; + + private final int port; + + private final String name; + + private int lockTimeout; + + private int ioTimeout; + + private int writeBlockSize; + + private transient OncRpcClient client; + + private transient CreateLinkResponse link; + + public VXI11Socket(final InetAddress host) throws IOException { + this(host, 0, "inst0"); + } + + public VXI11Socket(final InetAddress host, final int port) throws IOException { + this(host, port, "inst0"); + } + + public VXI11Socket( + final InetAddress host, + final int port, + final String instrumentName) throws IOException { + this(host, instrumentName, port, false, 0, DEFAULT_IO_TIMEOUT, DEFAULT_WRITE_BLOCK_SIZE); + } + + /** + * Creates a new VXI11Socket with the given parameters. + * @param host IP Address of the intrument + * @param name Name to be given to the instrument + * @param port Port number of the instrument, use 0 to use Portmap interface for + * @param lock when {@code true} the instrument will be locked for {@code lockTimeout} + * @param lockTimeout Maximum time to lock the device for + * @param ioTimeout Maximum time to wait for IO operations to complete. + * @param writeBlockSize Number of bytes to write in a single write operation + * @throws IOException when + */ + public VXI11Socket( + final InetAddress host, + final String name, + int port, + boolean lock, + int lockTimeout, + int ioTimeout, + int writeBlockSize) throws IOException { + this.host = host; + this.port = port; + this.name = name; + this.lockTimeout = lockTimeout; + this.ioTimeout = ioTimeout; + this.writeBlockSize = writeBlockSize; + + try { + client = OncRpcClient.newOncRpcClient(host, + VXI11.DeviceCore.PROGRAM, + VXI11.DeviceCore.VERSION, + port, + OncRpcProtocols.ONCRPC_TCP); + link = createLink(CLIENT_ID, lock, lockTimeout, name); + } catch (OncRpcException e) { + client = null; + throw new IOException(e); + } + } + + private CreateLinkResponse createLink( + int clientId, + boolean lockDevice, + int lockTimeout, + String device) throws OncRpcException { + CreateLinkResponse response = new CreateLinkResponse(); + CreateLinkParams params = new CreateLinkParams(clientId, lockDevice, lockTimeout, device); + client.call(VXI11.DeviceCore.CREATE_LINK, VXI11.DeviceCore.VERSION, params, response); + return response; + } + + private DeviceWriteResponse write( + int linkId, + int ioTimeout, + int lockTimeout, + DeviceFlags flags, + byte... data) throws OncRpcException { + DeviceLink link = new DeviceLink(linkId); + DeviceWriteParams params = new DeviceWriteParams(link, ioTimeout, lockTimeout, flags, data); + DeviceWriteResponse response = new DeviceWriteResponse(); + client.call(VXI11.DeviceCore.DEVICE_WRITE, VXI11.DeviceCore.VERSION, params, response); + return response; + } + + private DeviceReadResponse read( + int linkId, + int size, + int ioTimeout, + int lockTimeout, + DeviceFlags flags, + byte termination) throws OncRpcException { + DeviceLink link = new DeviceLink(linkId); + DeviceReadParams params = new DeviceReadParams(link, + size, + ioTimeout, + lockTimeout, + flags, + termination); + DeviceReadResponse response = new DeviceReadResponse(); + client.call(VXI11.DeviceCore.DEVICE_READ, VXI11.DeviceCore.VERSION, params, response); + return response; + } + + private DeviceError destroyLink(int linkId) throws OncRpcException { + DeviceError error = new DeviceError(); + DeviceLink link = new DeviceLink(linkId); + client.call(VXI11.DeviceCore.DESTROY_LINK, VXI11.DeviceCore.VERSION, link, error); + return error; + } + + public int getPort() { + return port; + } + + @Override + public void close() { + try { + destroyLink(link.getLinkId()); + client.close(); + } catch (OncRpcException e) { + //log warning + } finally { + client = null; + } + } + + @Override + public boolean isConnected() { + return client != null; + } + + @Override + public String getResourceString() { + return "TCPIP::" + this.host + "::" + this.name + "::INSTR"; + } + + @Override + public void send(ByteBuffer message) throws IOException { + if (!message.hasArray()) { + throw new IOException("Message empty."); + } + try { + int offset = 0; + int size = message.capacity(); + byte[] block = new byte[Math.min(size, writeBlockSize)]; + while (offset < size) { + message.get(block, offset, size - offset > writeBlockSize ? writeBlockSize : size); + DeviceFlags flags = new DeviceFlags(false, offset + writeBlockSize >= size, false); + DeviceWriteResponse response = this.write(link.getLinkId(), + ioTimeout, + lockTimeout, + flags, + block); + if (response.getError() != VXI11.ErrorCode.NO_ERROR) { + throw new VXI11Exception(response.getError()); + } + offset += writeBlockSize; + } + } catch (OncRpcException e) { + throw new IOException(e); + } + } + + @Override + public ByteBuffer receive(int count, long timeout) throws IOException, SocketTimeoutException { + try { + DeviceFlags flags = new DeviceFlags(false, false, false); + DeviceReadResponse response = this.read(link.getLinkId(), + count, + (int) timeout, + lockTimeout, + flags, + (byte) 0); + if (response.getError() != VXI11.ErrorCode.NO_ERROR) { + throw new VXI11Exception(response.getError()); + } + return ByteBuffer.wrap(response.data); + } catch (OncRpcException e) { + throw new IOException(e); + } + } + + @Override + public ByteBuffer receive(char delimiter, long timeout) + throws IOException, SocketTimeoutException { + try { + ArrayList buffer = new ArrayList<>(); + DeviceReadResponse response = null; + DeviceFlags flags = new DeviceFlags(false, false, false); + while (response == null || !response.isEOISet()) { + response = this.read(link.getLinkId(), + SocketOptions.SO_RCVBUF, + (int) timeout, + lockTimeout, + flags, + (byte) delimiter); + if (response.getError() != VXI11.ErrorCode.NO_ERROR) { + throw new VXI11Exception(response.getError()); + } + } + + buffer.add(response.data); + int length = buffer.stream().mapToInt(array -> array.length).sum(); + ByteBuffer data = ByteBuffer.allocate(length); + buffer.stream().forEach(data::put); + + return data; + } catch (OncRpcException e) { + throw new IOException(e); + } + } + +} diff --git a/measure-core/src/main/java/org/jtmc/core/lxi/vxi11/VXI11SocketFactory.java b/measure-core/src/main/java/org/jtmc/core/lxi/vxi11/VXI11SocketFactory.java index b2617f6..5505dcc 100644 --- a/measure-core/src/main/java/org/jtmc/core/lxi/vxi11/VXI11SocketFactory.java +++ b/measure-core/src/main/java/org/jtmc/core/lxi/vxi11/VXI11SocketFactory.java @@ -4,40 +4,35 @@ import java.net.InetAddress; import java.util.regex.Matcher; import java.util.regex.Pattern; - import org.jtmc.core.visa.exception.UnsupportedSocketException; import org.jtmc.core.visa.factory.ISocketFactory; /** - * This factory class is capable of creating VXI-11 sockets for SCPI - * communication - * - *

    - * It supports the VISA resource string syntax for VXI-11 sockets. - *

    - * Format: TCPIP[board]::[host address][::Instrument Name][::INSTR] + * This factory class is capable of creating VXI-11 sockets from VISA + * resource strings. + * + *

    Format: TCPIP[board]::[host address][::Instrument Name][::INSTR] */ public class VXI11SocketFactory implements ISocketFactory { - private Pattern pattern = Pattern.compile("TCPIP(?[0-9]*)::(?[^:]{1,})(::?[^:]{1,})?(::INSTR)?"); - - @Override - public boolean supports(String resourceString) { - return pattern.matcher(resourceString).matches(); - } - - @Override - public VXI11Socket create(String resourceString) throws IOException, UnsupportedSocketException { - Matcher matcher = pattern.matcher(resourceString); - if(!matcher.matches()) { - throw new UnsupportedSocketException(); - } - //int board = Integer.parseInt(matcher.group("board")); - String host = matcher.group("host"); - String instrument = matcher.group("instrument"); - - return new VXI11Socket(InetAddress.getByName(host), 0, instrument); - } - - -} \ No newline at end of file + private static final Pattern pattern = Pattern.compile( + "TCPIP(?[0-9]*)::(?[^:]{1,})(::?[^:]{1,})?(::INSTR)?"); + + @Override + public boolean supports(String resourceString) { + return pattern.matcher(resourceString).matches(); + } + + @Override + public VXI11Socket create(String resourceString) throws IOException, UnsupportedSocketException { + Matcher matcher = pattern.matcher(resourceString); + if (!matcher.matches()) { + throw new UnsupportedSocketException(); + } + //int board = Integer.parseInt(matcher.group("board")); + String host = matcher.group("host"); + String instrument = matcher.group("instrument"); + + return new VXI11Socket(InetAddress.getByName(host), 0, instrument); + } +} diff --git a/measure-core/src/main/java/org/jtmc/core/lxi/vxi11/package-info.java b/measure-core/src/main/java/org/jtmc/core/lxi/vxi11/package-info.java new file mode 100644 index 0000000..e991d32 --- /dev/null +++ b/measure-core/src/main/java/org/jtmc/core/lxi/vxi11/package-info.java @@ -0,0 +1,4 @@ +/** + * Contains classes for interaction with VXI11 based instruments. + */ +package org.jtmc.core.lxi.vxi11; diff --git a/measure-core/src/main/java/org/jtmc/core/lxi/vxi11/portmap/Portmap.java b/measure-core/src/main/java/org/jtmc/core/lxi/vxi11/portmap/Portmap.java index fc15e64..bbfef9d 100644 --- a/measure-core/src/main/java/org/jtmc/core/lxi/vxi11/portmap/Portmap.java +++ b/measure-core/src/main/java/org/jtmc/core/lxi/vxi11/portmap/Portmap.java @@ -1,17 +1,19 @@ package org.jtmc.core.lxi.vxi11.portmap; /** - * Portmap + * Portmap is a service of ONC RPC for finding + * RPC endpoints on a network. This class contains constants + * used by the protocol. * - * https://tools.ietf.org/html/rfc1833 + *

    More information: https://tools.ietf.org/html/rfc1833 */ public class Portmap { - public final static int PROGRAM = 100000; + public static final int PROGRAM = 100000; - public final static int VERSION = 2; + public static final int VERSION = 2; - public final static int PORT = 111; + public static final int PORT = 111; - public final static int PROC_GETPORT = 3; -} \ No newline at end of file + public static final int PROC_GETPORT = 3; +} diff --git a/measure-core/src/main/java/org/jtmc/core/lxi/vxi11/portmap/PortmapParams.java b/measure-core/src/main/java/org/jtmc/core/lxi/vxi11/portmap/PortmapParams.java index cb9f16d..143302b 100644 --- a/measure-core/src/main/java/org/jtmc/core/lxi/vxi11/portmap/PortmapParams.java +++ b/measure-core/src/main/java/org/jtmc/core/lxi/vxi11/portmap/PortmapParams.java @@ -7,41 +7,52 @@ import org.acplt.oncrpc.XdrDecodingStream; import org.acplt.oncrpc.XdrEncodingStream; +/** + * PortmapParams contains request information for Portmap requests. + */ public class PortmapParams implements XdrAble { - private int program; - - private int version; - - private int protocol; - - private int port; - - private int data = 0; - - public PortmapParams(int program, int version, int protocol, int port) { - this.program = program; - this.version = version; - this.protocol = protocol; - this.port = port; - } - - @Override - public void xdrEncode(XdrEncodingStream xdr) throws OncRpcException, IOException { - xdr.xdrEncodeInt(program); - xdr.xdrEncodeInt(version); - xdr.xdrEncodeInt(protocol); - xdr.xdrEncodeInt(port); - xdr.xdrEncodeInt(data); - } - - @Override - public void xdrDecode(XdrDecodingStream xdr) throws OncRpcException, IOException { - program = xdr.xdrDecodeInt(); - version = xdr.xdrDecodeInt(); - protocol = xdr.xdrDecodeInt(); - port = xdr.xdrDecodeInt(); - data = xdr.xdrDecodeInt(); - } - -} \ No newline at end of file + private int program; + + private int version; + + private int protocol; + + private int port; + + private int data = 0; + + /** + * Constructs a new PortmapParam request container. + * + * @param program Program ID to discover + * @param version Program Version number + * @param protocol Underlying protocol (TCP or UDP) + * @param port Port number + */ + public PortmapParams(int program, int version, int protocol, int port) { + this.program = program; + this.version = version; + this.protocol = protocol; + this.port = port; + } + + @Override + public void xdrEncode(XdrEncodingStream xdr) throws OncRpcException, IOException { + xdr.xdrEncodeInt(program); + xdr.xdrEncodeInt(version); + xdr.xdrEncodeInt(protocol); + xdr.xdrEncodeInt(port); + xdr.xdrEncodeInt(data); + } + + @Override + public void xdrDecode(XdrDecodingStream xdr) throws OncRpcException, IOException { + program = xdr.xdrDecodeInt(); + version = xdr.xdrDecodeInt(); + protocol = xdr.xdrDecodeInt(); + port = xdr.xdrDecodeInt(); + data = xdr.xdrDecodeInt(); + } + +} diff --git a/measure-core/src/main/java/org/jtmc/core/lxi/vxi11/portmap/PortmapResponse.java b/measure-core/src/main/java/org/jtmc/core/lxi/vxi11/portmap/PortmapResponse.java index d618c83..07fd3c1 100644 --- a/measure-core/src/main/java/org/jtmc/core/lxi/vxi11/portmap/PortmapResponse.java +++ b/measure-core/src/main/java/org/jtmc/core/lxi/vxi11/portmap/PortmapResponse.java @@ -1,28 +1,32 @@ package org.jtmc.core.lxi.vxi11.portmap; import java.io.IOException; - import org.acplt.oncrpc.OncRpcException; import org.acplt.oncrpc.XdrAble; import org.acplt.oncrpc.XdrDecodingStream; import org.acplt.oncrpc.XdrEncodingStream; /** - * PortmapResponse + * PortmapResponse contains response information for Portmap responses. */ public class PortmapResponse implements XdrAble { - private int port; + private int port; - public void xdrEncode(XdrEncodingStream xdr) throws OncRpcException, IOException { - xdr.xdrEncodeInt(port); - } + public void xdrEncode(XdrEncodingStream xdr) throws OncRpcException, IOException { + xdr.xdrEncodeInt(port); + } - public void xdrDecode(XdrDecodingStream xdr) throws OncRpcException, IOException { - port = xdr.xdrDecodeInt(); - } - - public int getPort() { - return port; - } -} \ No newline at end of file + public void xdrDecode(XdrDecodingStream xdr) throws OncRpcException, IOException { + port = xdr.xdrDecodeInt(); + } + + /** + * Returns the port number for the given response. + * + * @return Port number + */ + public int getPort() { + return port; + } +} diff --git a/measure-core/src/main/java/org/jtmc/core/scpi/ISCPISocket.java b/measure-core/src/main/java/org/jtmc/core/scpi/ISCPISocket.java index dca822d..155e9ae 100644 --- a/measure-core/src/main/java/org/jtmc/core/scpi/ISCPISocket.java +++ b/measure-core/src/main/java/org/jtmc/core/scpi/ISCPISocket.java @@ -2,90 +2,113 @@ import java.io.IOException; import java.net.SocketTimeoutException; - import org.jtmc.core.visa.DeviceIdentifier; /** - * ISCPISocket is the standard interface for sending SCPI commands and receiving responses + * ISCPISocket is the standard interface for sending SCPI commands + * and receiving responses. */ public interface ISCPISocket { - public final static long DEFAULT_TIMEOUT = 1000; + public static final long DEFAULT_TIMEOUT = 1000; + + /** + * Sends SCPI commands to the device, how they're being sent is up to + * the socket implementation. + * + *

    The operation should be atomic, so either all commands should succeed + * or the device's state shall not change + * + * @param commands SCPI commands to send + * @throws IOException if there was an error communication with the device + */ + public void send(SCPICommand... commands) throws IOException; - /** - * Sends SCPI commands to the device, how they're being sent is up to the socket implementation - *

    - * The operation should be atomic, so either all commands should succeed or the device's state shall not change - * - * @param commands SCPI commands to send - * @throws IOException if - */ - public void send(SCPICommand... commands) throws IOException; + /** + * Receives a SCPI response from the device. + * + *

    The operation has to return {@code Optional.empty()} if the method + * timed out with no response. + * + * @param timeout Time to wait for a response, if 0 then wait indefinitely + * @return Device response as string + * @throws IOException if there was an error communication with the device + */ + public String receive(long timeout) + throws SocketTimeoutException, IOException; - /** - * Receives a SCPI response from the device - *

    - * The operation has to return {@code Optional.empty()} if the method timed out with no response - * - * @param timeout Time to wait for a response, if 0 then wait indefinitely - * @return Device response as string - * @throws IOException If the - */ - public String receive(long timeout) throws SocketTimeoutException, IOException; + /** + * Receives part of the response from the device, up until the + * given character count has been reached. + * + * @param count Number of characters to receive + * @param timeout Maximum time to wait for a response + * @return Device response as string + * @throws IOException if there was an error communication with the device + */ + String receive(int count, long timeout) + throws SocketTimeoutException, IOException; - /** - * Receives part of the response from the device, up until the given character count has been reached - * @param count - * @param timeout - * @return - * @throws IOException - */ - String receive(int count, long timeout) throws SocketTimeoutException, IOException; + /** + * Sends a SCPI command to the device and then waits for a response. + * + * @param command SCPI command to send + * @param timeout Maximum time to wait for a response + * @return Response to the SCPI command + * @throws IOException if there was an error communication with the device + */ + default String query(SCPICommand command, long timeout) + throws SocketTimeoutException, IOException { + this.send(command); + return this.receive(timeout); + } - /** - * Sends a SCPI command to the device and then waits for a response - * @param command - * @param timeout - * @return - * @throws IOException - */ - default String query(SCPICommand command, long timeout) throws SocketTimeoutException, IOException { - this.send(command); - return this.receive(timeout); - } + /** + * Returns the device identifier of the SCPI device this socket is connected + * to by sending an {@code *IDN?} query to the device. + * + * @return Device Identifier + * @throws SocketTimeoutException if the device didn't respond within + * the default timeout + * @throws IOException if there was an error communicating with the device + */ + default DeviceIdentifier getDeviceIdentifier() + throws SocketTimeoutException, IOException { + try { + return DeviceIdentifier.from(this.query(SCPI.idnQuery, DEFAULT_TIMEOUT)); + } catch (IllegalArgumentException e) { + throw new IOException("Cannot resolve device identifier", e); + } + } - /** - * Returns the device identifier of the SCPI device this socket is connected to by sending - * an {@code *IDN?} query to the device - * - * @return Device Identifier - * @throws SocketTimeoutException if the device didn't respond within the default timeout - * @throws IOException if there was an error communicating with the device - */ - default DeviceIdentifier getDeviceIdentifier() throws SocketTimeoutException, IOException { - try { - return DeviceIdentifier.from(this.query(SCPI.idnQuery, DEFAULT_TIMEOUT)); - } catch(IllegalArgumentException e) { - throw new IOException("Cannot resolve device identifier", e); - } - } + /** + * Resets the device by sending the standard SCPI reset command. + * + * @see SCPI.resetDevice + * @throws IOException if there was an error communication with the device + */ + default void reset() throws IOException { + this.send(SCPI.resetDevice); + } - /** - * Resets the device by sending the standard SCPI reset command - * - * @see SCPI.resetDevice - * @throws IOException - */ - default void reset() throws IOException { - this.send(SCPI.resetDevice); - } + /** + * Clears the device status by sending the standard SCPI status clear command. + * + * @throws IOException if there was an error communication with the device + */ + default void clearStatus() throws IOException { + this.send(SCPI.clearStatus); + } - default void clearStatus() throws IOException { - this.send(SCPI.clearStatus); - } + /** + * Sends an operation complete query to the device then waits for a response. + * + * @param timeout Maximum time to wait for the operation to complete + * @throws IOException if there was an error communication with the device + */ + default void waitForOperation(long timeout) throws IOException { + this.send(SCPI.opcQuery); + this.receive(timeout); + } - default void waitForOperation(long timeout) throws IOException { - this.send(SCPI.opcQuery); - this.receive(timeout); - } -} \ No newline at end of file +} diff --git a/measure-core/src/main/java/org/jtmc/core/scpi/SCPI.java b/measure-core/src/main/java/org/jtmc/core/scpi/SCPI.java index 8590f5d..640044a 100644 --- a/measure-core/src/main/java/org/jtmc/core/scpi/SCPI.java +++ b/measure-core/src/main/java/org/jtmc/core/scpi/SCPI.java @@ -1,32 +1,35 @@ package org.jtmc.core.scpi; /** - * Includes standard SCPI commands, such as resetting the device, waiting for an operation + * Includes standard SCPI commands, such as resetting the device, + * waiting for an operation. */ public final class SCPI { - /** - * The standard command to query a device's identifier - *

    - * The response is four comma-separated fields 'Manufacturer,Model,SerialNumber,FirmwareVersion' - */ - public final static SCPICommand idnQuery = SCPICommand.builder().query("*IDN").build(); + /** + * The standard command to query a device's identifier. + * + *

    The response is four comma-separated fields + * 'Manufacturer,Model,SerialNumber,FirmwareVersion' + */ + public static final SCPICommand idnQuery = SCPICommand.builder().query("*IDN").build(); - /** - * The standard command to query whether the device has finished it's last operation - *

    - * In sequential mode the response is a single '1' character - */ - public final static SCPICommand opcQuery = SCPICommand.builder().query("*OPC").build(); + /** + * The standard command to query whether the device has finished + * it's last operation. + * + *

    In sequential mode the response is a single '1' character + */ + public static final SCPICommand opcQuery = SCPICommand.builder().query("*OPC").build(); - /** - * The standard command to reset the device into a known state - *

    - * The reset state is dependent on the particular device - */ - public final static SCPICommand resetDevice = SCPICommand.builder().command("*RST").build(); + /** + * The standard command to reset the device into a known state. + * + *

    The reset state is dependent on the particular device + */ + public static final SCPICommand resetDevice = SCPICommand.builder().command("*RST").build(); - public final static SCPICommand clearStatus = SCPICommand.builder().query("*CLS").build(); + public static final SCPICommand clearStatus = SCPICommand.builder().query("*CLS").build(); - public final static SCPICommand testDevice = SCPICommand.builder().query("*TST").build(); -} \ No newline at end of file + public static final SCPICommand testDevice = SCPICommand.builder().query("*TST").build(); +} diff --git a/measure-core/src/main/java/org/jtmc/core/scpi/SCPICommand.java b/measure-core/src/main/java/org/jtmc/core/scpi/SCPICommand.java index 3755082..512fde7 100644 --- a/measure-core/src/main/java/org/jtmc/core/scpi/SCPICommand.java +++ b/measure-core/src/main/java/org/jtmc/core/scpi/SCPICommand.java @@ -8,261 +8,231 @@ import java.util.Optional; /** - * SCPICommand is a message understood by SCPI devices - * - *

    - * In general the command format: - *

    - * ROOT:SUB:CMD PARAM1,PARAM2,... - * + * SCPICommand is a message understood by SCPI devices. * + *

    In general the command format: "ROOT:SUB:CMD PARAM1,PARAM2,..." */ public final class SCPICommand { - private String raw; - - private String command; - - private List parameters; - - /** - * Constructs a SCPICommand using the command and a list of parameters - * @param command Command - * @param params List of parameters - */ - public SCPICommand(String command, List params) { - if(command.contains(" ") || command.contains(",")) { - throw new IllegalArgumentException("Invalid command"); - } - this.command = command; - this.parameters = Collections.unmodifiableList(params); - this.raw = this.toString(); - } - - /** - * Constructs a SCPICommand using the command and an array of parameters - * - * @param command Command - * @param params Parameters - */ - public SCPICommand(String command, String... params) { - this(command, Arrays.asList(params)); - } - - /** - * Constructs a SCPICommand from it's raw string representation - * @param raw Raw string (CMD:SUBCMD PARAM1,PARAM2) - */ - public SCPICommand(String raw) { - this.raw = raw; - - int firstSpace = this.raw.indexOf(" "); - if(firstSpace == -1 || firstSpace == raw.length()) { - this.command = this.raw; - this.parameters = Collections.unmodifiableList(Collections.emptyList()); - } - else { - this.command = raw.substring(0, firstSpace); - String[] params = raw.substring(firstSpace+1, raw.length()).split(","); - this.parameters = Collections.unmodifiableList(Arrays.asList(params)); - } - } - - /** - * @return the command - */ - public String getCommand() { - return command; - } - - /** - * @return the raw - */ - public String getRaw() { - return raw; - } - - /** - * @return the parameters - */ - public List getParameters() { - return parameters; - } - - /** - * Returns the SCPI command in it's string format, equivalent to {@see getRaw()} - * - * @return SCPI command string - */ - @Override - public String toString() { - if(raw != null) { - return raw; - } - StringBuilder builder = new StringBuilder(); - builder.append(this.getCommand()); - if(this.getParameters().size() != 0) { - builder.append(" "); - } - Iterator it = this.getParameters().iterator(); - while(it.hasNext()) { - String param = it.next(); - builder.append(param); - if(it.hasNext()) { - builder.append(","); - } - } - return builder.toString(); - } - - /** - * Returns the given parameter's value - * - * E.g: for {@code CMD PARAM1,VALUE1}, {@code getParameter("PARAM1")} would return VALUE1 - * - * @param name Parameter's name - * @return Parameter's value, empty if parameter doesn't exist or doesn't have a value associated to it - */ - public Optional getParameter(String name) { - Iterator it = parameters.iterator(); - while(it.hasNext()) { - if(it.next().equals(name)) { - if(it.hasNext()) { - return Optional.of(it.next()); - } - else { - return Optional.empty(); - } - } - } - return Optional.empty(); - } - - /** - * Returns the given parameter's value as float - * - * @param name Parameter's name - * @return Parameter's value - * - * @throws NumberFormatException if the value can't be parsed as float - */ - public Optional getFloat(String name) { - return this.getParameter(name) - .map(value -> value != null ? Float.parseFloat(value) : null); - } - - /** - * Returns the given parameter's value as int - * - * @param name Parameter's name - * @return Parameter's value - * - * @throws NumberFormatException if the value can't be parsed as integer - */ - public Optional getInt(String name) { - return this.getParameter(name) - .map(value -> value != null ? Integer.parseInt(value) : null); - } - - /** - * Returns whether or not the given parameter exists - * - * @param param Parameter's name - * @return {@code true} if there's such parameter - */ - public boolean hasParameter(String param) { - return this.parameters.contains(param); - } - - /** - * Returns a mutable command builder - * - * @return Mutable builder instance - */ - public static Builder builder() { - return new Builder(); - } - - /** - * A mutable class for building SCPI commands - * - *

    - * Method calls can be chained for compact usage - * - *

    - * Create command - * {@code SCPICommand.builder().command("SET50").build()} - * - * - *

    - * Create query - * {@code SCPICommand.builder().query("C1:BSWV").build()} - * - *

    - * Create command with parameters - * {@code SCPICommand.builder().command("C1:BSWV").with("TYPE", "SINE").with("FREQ", 10000).build()} - */ - public final static class Builder { - - private String command; - - private List parameters = new LinkedList<>(); - - /** - * Sets the command - * - * @param command - * @return - */ - public Builder command(String command) { - this.command = command; - return this; - } - - /** - * Sets the command to - * - * @param cmds - * @return - */ - public Builder command(String... cmds) { - this.command = String.join(":", cmds); - return this; - } - - /** - * Sets the command to a query - * - * @param command - * @return {@code this} Builder - */ - public Builder query(String command) { - this.command = command + "?"; - return this; - } - - public Builder with(String param, String value) { - this.parameters.add(param); - this.parameters.add(value); - return this; - } - - public Builder with(String param, int value) { - return this.with(param, String.valueOf(value)); - } - - public Builder with(String param, float value) { - return this.with(param, String.valueOf(value)); - } - - public Builder with(String param) { - this.parameters.add(param); - return this; - } - - public SCPICommand build() { - return new SCPICommand(command, parameters); - } - } - -} \ No newline at end of file + private String raw; + + private String command; + + private List parameters; + + /** + * Constructs a SCPICommand using the command and a list of parameters. + * @param command Command + * @param params List of parameters + */ + public SCPICommand(String command, List params) { + if (command.contains(" ") || command.contains(",")) { + throw new IllegalArgumentException("Invalid command"); + } + this.command = command; + this.parameters = Collections.unmodifiableList(params); + this.raw = this.toString(); + } + + /** + * Constructs a SCPICommand using the command and an array of parameters. + * @param command Command + * @param params Parameters + */ + public SCPICommand(String command, String... params) { + this(command, Arrays.asList(params)); + } + + /** + * Constructs a SCPICommand from it's raw string representation. + * @param raw Raw string (CMD:SUBCMD PARAM1,PARAM2) + */ + public SCPICommand(String raw) { + this.raw = raw; + + int firstSpace = this.raw.indexOf(" "); + if (firstSpace == -1 || firstSpace == raw.length()) { + this.command = this.raw; + this.parameters = Collections.unmodifiableList(Collections.emptyList()); + } else { + this.command = raw.substring(0, firstSpace); + String[] params = raw.substring(firstSpace + 1, raw.length()).split(","); + this.parameters = Collections.unmodifiableList(Arrays.asList(params)); + } + } + + public String getCommand() { + return command; + } + + public boolean isQuery() { + return command.endsWith("?"); + } + + public String getRaw() { + return raw; + } + + public List getParameters() { + return parameters; + } + + /** + * Returns the SCPI command in it's string format, equivalent to {@see getRaw()}. + * + * @return SCPI command string + */ + @Override + public String toString() { + if (raw != null) { + return raw; + } + StringBuilder builder = new StringBuilder(); + builder.append(this.getCommand()); + if (this.getParameters().size() != 0) { + builder.append(" "); + } + Iterator it = this.getParameters().iterator(); + while (it.hasNext()) { + String param = it.next(); + builder.append(param); + if (it.hasNext()) { + builder.append(","); + } + } + return builder.toString(); + } + + /** + * Returns the given parameter's value. + * E.g: for {@code CMD PARAM1,VALUE1}, {@code getParameter("PARAM1")} would return VALUE1 + * + * @param name Parameter's name + * @return Parameter's value, empty if parameter doesn't exist or doesn't have a value + */ + public Optional getParameter(String name) { + Iterator it = parameters.iterator(); + while (it.hasNext()) { + if (it.next().equals(name)) { + if (it.hasNext()) { + return Optional.of(it.next()); + } else { + return Optional.empty(); + } + } + } + return Optional.empty(); + } + + /** + * Returns the given parameter's value as float. + * + * @param name Parameter's name + * @return Parameter's value + * @throws NumberFormatException if the value can't be parsed as float + */ + public Optional getFloat(String name) { + return this.getParameter(name) + .map(value -> value != null ? Float.parseFloat(value) : null); + } + + /** + * Returns the given parameter's value as int. + * + * @param name Parameter's name + * @return Parameter's value + * @throws NumberFormatException if the value can't be parsed as integer + */ + public Optional getInt(String name) { + return this.getParameter(name) + .map(value -> value != null ? Integer.parseInt(value) : null); + } + + /** + * Returns whether or not the given parameter exists. + * + * @param param Parameter's name + * @return {@code true} if there's such parameter + */ + public boolean hasParameter(String param) { + return this.parameters.contains(param); + } + + /** + * Returns a mutable command builder. + * + * @return Mutable builder instance + */ + public static Builder builder() { + return new Builder(); + } + + /** + * A mutable class for building SCPI commands. + * + *

    Method calls can be chained for compact usage + * + *

    Create command + * {@code SCPICommand.builder().command("SET50").build()} + * + *

    Create query + * {@code SCPICommand.builder().query("C1:BSWV").build()} + * + *

    Create command with parameters + * {@code + * SCPICommand.builder().command("C1:BSWV").with("TYPE", "SINE").with("FREQ", 10000).build() + * } + */ + public static final class Builder { + + private String command; + + private List parameters = new LinkedList<>(); + + public Builder command(String command) { + this.command = command; + return this; + } + + public Builder command(String... cmds) { + this.command = String.join(":", cmds); + return this; + } + + public Builder query(String command) { + this.command = command + "?"; + return this; + } + + /** + * Appends a parameter with a value to the command. + * + * @param param Parameter name + * @param value Parameter value + * @return Builder + */ + public Builder with(String param, String value) { + this.parameters.add(param); + this.parameters.add(value); + return this; + } + + public Builder with(String param, int value) { + return this.with(param, String.valueOf(value)); + } + + public Builder with(String param, float value) { + return this.with(param, String.valueOf(value)); + } + + public Builder with(String param) { + this.parameters.add(param); + return this; + } + + public SCPICommand build() { + return new SCPICommand(command, parameters); + } + } + +} diff --git a/measure-core/src/main/java/org/jtmc/core/scpi/SCPISocketAdapter.java b/measure-core/src/main/java/org/jtmc/core/scpi/SCPISocketAdapter.java index 4ff089c..4d6e69a 100644 --- a/measure-core/src/main/java/org/jtmc/core/scpi/SCPISocketAdapter.java +++ b/measure-core/src/main/java/org/jtmc/core/scpi/SCPISocketAdapter.java @@ -1,60 +1,61 @@ package org.jtmc.core.scpi; import java.io.IOException; - import org.jtmc.core.visa.DeviceIdentifier; /** - * SCPISocketAdapter is a proxy class for the SCPISocket interface + * SCPISocketAdapter is a proxy class for the SCPISocket interface. * *

    The only additional functionality it provides, is that it caches the Device identifier. */ public class SCPISocketAdapter implements ISCPISocket { - private final ISCPISocket adapter; - - private DeviceIdentifier deviceIdentifier; - - public SCPISocketAdapter(final ISCPISocket adapter) { - this(adapter, null); - } - - public SCPISocketAdapter(final ISCPISocket adapter, DeviceIdentifier deviceIdentifier) { - this.adapter = adapter; - this.deviceIdentifier = deviceIdentifier; - } - - @Override - public DeviceIdentifier getDeviceIdentifier() throws IOException { - return this.deviceIdentifier == null ? (this.deviceIdentifier = ISCPISocket.super.getDeviceIdentifier()) : this.deviceIdentifier; - } - - /** - * Allows subclasses to manually override the cached device identifier - * - * @param deviceIdentifier New device identifier - */ - protected void setDeviceIdentifier(DeviceIdentifier deviceIdentifier) { - this.deviceIdentifier = deviceIdentifier; - } - - @Override - public String receive(long timeout) throws IOException { - return adapter.receive(timeout); - } - - @Override - public String receive(int count, long timeout) throws IOException { - return adapter.receive(count, timeout); - } - - @Override - public void send(SCPICommand... commands) throws IOException { - adapter.send(commands); - } - - public SCPISocketAdapter clone(ISCPISocket socket) { - return new SCPISocketAdapter(socket); - } - -} \ No newline at end of file + private final ISCPISocket adapter; + + private DeviceIdentifier deviceIdentifier; + + public SCPISocketAdapter(final ISCPISocket adapter) { + this(adapter, null); + } + + public SCPISocketAdapter(final ISCPISocket adapter, DeviceIdentifier deviceIdentifier) { + this.adapter = adapter; + this.deviceIdentifier = deviceIdentifier; + } + + @Override + public DeviceIdentifier getDeviceIdentifier() throws IOException { + return this.deviceIdentifier == null + ? (this.deviceIdentifier = ISCPISocket.super.getDeviceIdentifier()) + : this.deviceIdentifier; + } + + /** + * Allows subclasses to manually override the cached device identifier. + * + * @param deviceIdentifier New device identifier + */ + protected void setDeviceIdentifier(DeviceIdentifier deviceIdentifier) { + this.deviceIdentifier = deviceIdentifier; + } + + @Override + public String receive(long timeout) throws IOException { + return adapter.receive(timeout); + } + + @Override + public String receive(int count, long timeout) throws IOException { + return adapter.receive(count, timeout); + } + + @Override + public void send(SCPICommand... commands) throws IOException { + adapter.send(commands); + } + + public SCPISocketAdapter clone(ISCPISocket socket) { + return new SCPISocketAdapter(socket); + } + +} diff --git a/measure-core/src/main/java/org/jtmc/core/scpi/adapter/SCPIFilter.java b/measure-core/src/main/java/org/jtmc/core/scpi/adapter/SCPIFilter.java index 6bd5bf9..8bd04eb 100644 --- a/measure-core/src/main/java/org/jtmc/core/scpi/adapter/SCPIFilter.java +++ b/measure-core/src/main/java/org/jtmc/core/scpi/adapter/SCPIFilter.java @@ -4,40 +4,40 @@ import java.util.ArrayList; import java.util.function.Predicate; import java.util.stream.Stream; - import org.jtmc.core.scpi.ISCPISocket; import org.jtmc.core.scpi.SCPICommand; import org.jtmc.core.scpi.SCPISocketAdapter; /** - * SCPIFilter is used to block certain commands from being sent to the device + * SCPIFilter is used to block certain commands from being sent to the device. */ public class SCPIFilter extends SCPISocketAdapter { - private final Predicate allow; + private final Predicate allow; + + /** + * Constructs a new SCPI filter using a socket and a filter predicate. + * + * @param adapter SCPI socket to pass call to + * @param allow A predicate for allowing commands, returns {@code true} if the command is allowed + */ + public SCPIFilter(ISCPISocket adapter, Predicate allow) { + super(adapter); + this.allow = allow; + } - /** - * Constructs a new SCPI filter using a socket and a filter predicate - * @param adapter SCPI socket to pass call to - * @param allow A predicate for allowing commands, returns {@code true} if the command is allowed - */ - public SCPIFilter(ISCPISocket adapter, Predicate allow) { - super(adapter); - this.allow = allow; - } + @Override + public void send(SCPICommand... commands) throws IOException { + ArrayList filtered = new ArrayList<>(); + Stream.of(commands).filter(allow).forEach(filtered::add); + SCPICommand[] out = new SCPICommand[filtered.size()]; + out = filtered.toArray(out); + super.send(out); + } - @Override - public void send(SCPICommand... commands) throws IOException { - ArrayList filtered = new ArrayList<>(); - Stream.of(commands).filter(allow).forEach(filtered::add); - SCPICommand[] out = new SCPICommand[filtered.size()]; - out = filtered.toArray(out); - super.send(out); - } + @Override + public SCPIFilter clone(ISCPISocket socket) { + return new SCPIFilter(socket, this.allow); + } - @Override - public SCPIFilter clone(ISCPISocket socket) { - return new SCPIFilter(socket, this.allow); - } - -} \ No newline at end of file +} diff --git a/measure-core/src/main/java/org/jtmc/core/scpi/adapter/SCPIReducer.java b/measure-core/src/main/java/org/jtmc/core/scpi/adapter/SCPIReducer.java deleted file mode 100644 index 767ada8..0000000 --- a/measure-core/src/main/java/org/jtmc/core/scpi/adapter/SCPIReducer.java +++ /dev/null @@ -1,23 +0,0 @@ -package org.jtmc.core.scpi.adapter; - -import java.io.IOException; - -import org.jtmc.core.scpi.ISCPISocket; -import org.jtmc.core.scpi.SCPICommand; -import org.jtmc.core.scpi.SCPISocketAdapter; - -/** - * SCPIReducer - */ -public class SCPIReducer extends SCPISocketAdapter { - - public SCPIReducer(ISCPISocket adapter) { - super(adapter); - } - - @Override - public void send(SCPICommand... commands) throws IOException { - //TODO: reduce - } - -} \ No newline at end of file diff --git a/measure-core/src/main/java/org/jtmc/core/scpi/adapter/SCPITracer.java b/measure-core/src/main/java/org/jtmc/core/scpi/adapter/SCPITracer.java index c67ae80..b57180c 100644 --- a/measure-core/src/main/java/org/jtmc/core/scpi/adapter/SCPITracer.java +++ b/measure-core/src/main/java/org/jtmc/core/scpi/adapter/SCPITracer.java @@ -2,65 +2,69 @@ import java.io.IOException; import java.io.OutputStream; - import org.jtmc.core.scpi.ISCPISocket; import org.jtmc.core.scpi.SCPICommand; import org.jtmc.core.scpi.SCPISocketAdapter; /** - * SCPITracer is an adapter class for any SCPISocket - * - *

    - * It captures all SCPICommands and can log outbound messages so they can be replayed later + * SCPITracer is an adapter class that can be used to record SCPI communication. */ public class SCPITracer extends SCPISocketAdapter { - private OutputStream outbound; + private OutputStream outbound; + + private OutputStream inbound; - private OutputStream inbound; + /** + * Constructs a new SCPI tracer given a SCPI socket and streams for recording + * inbound and outbound activity. + * + * @param adapter SCPI socket + * @param inbound Output stream for inbound communication + * @param outbound Output stream for outbound communication + */ + public SCPITracer(ISCPISocket adapter, OutputStream inbound, OutputStream outbound) { + super(adapter); + this.outbound = outbound; + this.inbound = inbound; + } - public SCPITracer(ISCPISocket adapter, OutputStream inbound, OutputStream outbound) { - super(adapter); - this.outbound = outbound; - this.inbound = inbound; - } + private void outbound(SCPICommand... commands) { + try { + //String out = ISCPISocket.concat(';', ' ', commands); + String out = ""; + outbound.write(out.getBytes()); + } catch (IOException e) { + //TODO: log + //log.warn("Error writing outbound message.", e); + } + } - private void outbound(SCPICommand... commands) { - try { - //String out = ISCPISocket.concat(';', ' ', commands); - String out = ""; - outbound.write(out.getBytes()); - } catch(IOException e) { - //TODO: log - //log.warn("Error writing outbound message.", e); - } - } + private void inbound(String command) { + try { + inbound.write(command.getBytes()); + } catch (IOException e) { + //TODO: log + //log.warn("Error writing inbound message.", e); + } + } - private void inbound(String command) { - try { - inbound.write(command.getBytes()); - } catch (IOException e) { - //TODO: log - //log.warn("Error writing inbound message.", e); - } - } + @Override + public void send(SCPICommand... commands) throws IOException { + super.send(commands); + this.outbound(commands); + } - @Override - public void send(SCPICommand... commands) throws IOException { - super.send(commands); - this.outbound(commands); - } + @Override + public String receive(long timeout) throws IOException { + String response = super.receive(timeout); + this.inbound(response); + return response; + } - @Override - public String receive(long timeout) throws IOException { - String response = super.receive(timeout); - this.inbound(response); - return response; - } + @Override + public SCPITracer clone(ISCPISocket socket) { + return new SCPITracer(socket, this.inbound, this.outbound); + } - @Override - public SCPITracer clone(ISCPISocket socket) { - return new SCPITracer(socket, this.inbound, this.outbound); - } - -} \ No newline at end of file +} diff --git a/measure-core/src/main/java/org/jtmc/core/scpi/mock/MockSCPIClass.java b/measure-core/src/main/java/org/jtmc/core/scpi/mock/MockSCPIClass.java index b9de8a9..789e8ab 100644 --- a/measure-core/src/main/java/org/jtmc/core/scpi/mock/MockSCPIClass.java +++ b/measure-core/src/main/java/org/jtmc/core/scpi/mock/MockSCPIClass.java @@ -6,16 +6,16 @@ import java.lang.annotation.Target; /** - * Marks a class as a SCPI mocking candidate + * Marks a class as a SCPI device mocking candidate. */ @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface MockSCPIClass { - /** - * The name used to reference this mock class, should be Vendor-Model, like Keysight-34460A - * - * @return Name of this mock class - */ - String value(); -} \ No newline at end of file + /** + * The name used to reference this mock class, should be Vendor-Model, like Keysight-34460A. + * + * @return Name of this mock class + */ + String value(); +} diff --git a/measure-core/src/main/java/org/jtmc/core/scpi/mock/MockSCPISocket.java b/measure-core/src/main/java/org/jtmc/core/scpi/mock/MockSCPISocket.java index 27320e4..f8e55c7 100644 --- a/measure-core/src/main/java/org/jtmc/core/scpi/mock/MockSCPISocket.java +++ b/measure-core/src/main/java/org/jtmc/core/scpi/mock/MockSCPISocket.java @@ -10,206 +10,208 @@ import java.util.concurrent.TimeUnit; import java.util.regex.Matcher; import java.util.regex.Pattern; - import org.jtmc.core.scpi.ISCPISocket; import org.jtmc.core.scpi.SCPICommand; import org.jtmc.core.visa.DeviceIdentifier; import org.jtmc.core.visa.mock.MockSocket; /** - * MockSCPISocket is used to emulate a SCPI interpreting device - * - * + * MockSCPISocket is used to emulate a SCPI interpreting device. */ public abstract class MockSCPISocket implements ISCPISocket { - private BlockingQueue rxStream = new LinkedBlockingQueue<>(10); - - private final MockSocket socket; - - private final DeviceIdentifier idn; - - public MockSCPISocket(MockSocket socket, DeviceIdentifier idn) { - this.socket = socket; - this.idn = idn; - } - - @Override - public String receive(int count, long timeout) throws IOException { - throw new UnsupportedOperationException("Invalid operation"); - } - - @Override - public String toString() { - return "MockSCPISocket[" + this.getClass().getSimpleName() + "]"; - } - - /** - * Matches the given command path to the incoming command - * - * @param path - * @param command - * @return - */ - private Matcher matcher(String path, String command) { - Pattern regex = Pattern.compile(path); - return regex.matcher(command); - } - - /** - * Resolves method parameters using the given SCPI command and matcher - * - * - * - * @param matcher Matcher object, has to match before - * @param method - * @param command SCPICommand - * @return - */ - private Object[] resolve(Matcher matcher, Method method, SCPICommand command) { - Object[] arguments = new Object[method.getParameterCount()]; - for (int i = 0; i < arguments.length; i++) { - Parameter param = method.getParameters()[i]; - if (param.getType().equals(SCPICommand.class)) { - arguments[i] = command; - } else if (param.isNamePresent()) { - String value = matcher.group(param.getName()); - arguments[i] = resolve(value, param.getType()); - } - } - return arguments; - } - - private Object resolve(String value, Class type) { - if (type.equals(String.class)) { - return value; - } else if (type.equals(Integer.class) || type.equals(int.class)) { - return Integer.parseInt(value); - } - throw new IllegalArgumentException("Couldn't convert '" + value + "' to " + type.getSimpleName()); - } - - /** - * - * @param object - * @return - */ - private String resolveReturnType(Object object) { - if (object instanceof SCPICommand) { - return ((SCPICommand) object).getRaw(); - } else if (object instanceof String) { - return (String) object; - } - return null; - } - - private void handle(Matcher matcher, Method method, SCPICommand command) throws InvocationTargetException { - try { - Object[] arguments = resolve(matcher, method, command); - Object ret = method.invoke(this, arguments); - - String response = resolveReturnType(ret); - if (response != null) { - this.pushResponse(response); - } - } catch (IllegalAccessException | IllegalArgumentException e) { - //TODO: log - e.printStackTrace(); - } - } - - /** - * Called when the socket has received a command - * - * @param in Received SCPI command - * @throws Exception - */ - protected void onReceive(SCPICommand in) throws Exception { - for (Method method : this.getClass().getMethods()) { - if (method.isAnnotationPresent(OnCommand.class)) { - OnCommand path = method.getAnnotation(OnCommand.class); - Matcher matcher = matcher(path.value(), in.getCommand()); - if (matcher.matches()) { - handle(matcher, method, in); - return; - } - } - } - this.onNotMapped(in); - } - - /** - * Pushes the SCPI command to the response stream - * - * @param response SCPI response - */ - protected final void pushResponse(String response) { - rxStream.offer(response); - } - - /** - * Called on identification query commands - * - * @return Response including the device's identifier - */ - @OnCommand("\\*IDN\\?") - public final SCPICommand onIDN() { - return new SCPICommand(idn.value()); - } - - @OnCommand("\\*OPC\\?") - public final SCPICommand onOPC() { - return new SCPICommand("1"); - } - - /** - * Called on reset command - */ - @OnCommand("\\*RST") - public final void onRST() { - this.onReset(); - } - - protected abstract void onReset(); - - /** - * Called when no mapping was found for the inbound command - * - * @param command Inbound SCPI command - */ - protected abstract void onNotMapped(SCPICommand command); - - @Override - public final void send(SCPICommand... commands) throws IOException { - if (!socket.isConnected()) { - throw new IOException("Device not connected."); - } - //TODO: parallel calls, or randomize order - //Stream.of(commands).parallel().peek(command -> { - for(SCPICommand command: commands) { - try { - onReceive(command); - } catch (Exception e) { - throw new IOException(e); - } - } - //}); - } - - @Override - public final String receive(long timeout) throws SocketTimeoutException, IOException { - try { - if(!socket.isConnected()) { - throw new IOException("Device not connected."); - } - String response = timeout == 0 ? rxStream.take() : rxStream.poll(timeout, TimeUnit.MILLISECONDS); - if(response == null) { - throw new SocketTimeoutException("Response timed out."); - } - return response; - } catch(InterruptedException e) { - throw new IOException(); - } - } - -} \ No newline at end of file + private BlockingQueue rxStream = new LinkedBlockingQueue<>(10); + + private final MockSocket socket; + + private final DeviceIdentifier idn; + + public MockSCPISocket(MockSocket socket, DeviceIdentifier idn) { + this.socket = socket; + this.idn = idn; + } + + @Override + public String toString() { + return "MockSCPISocket[" + this.getClass().getSimpleName() + "]"; + } + + /** + * Matches the given command path to the incoming command. + * + * @param path Command pattern + * @param command Command + * @return Matcher + */ + private Matcher matcher(String path, String command) { + Pattern regex = Pattern.compile(path); + return regex.matcher(command); + } + + /** + * Resolves method parameters using the given SCPI command and matcher. + * + * @param matcher Matcher object, has to match before + * @param method Method to call + * @param command SCPICommand + * @return Arguments + */ + private Object[] resolve(Matcher matcher, Method method, SCPICommand command) { + Object[] arguments = new Object[method.getParameterCount()]; + for (int i = 0; i < arguments.length; i++) { + Parameter param = method.getParameters()[i]; + if (param.getType().equals(SCPICommand.class)) { + arguments[i] = command; + } else if (param.isNamePresent()) { + String value = matcher.group(param.getName()); + arguments[i] = resolve(value, param.getType()); + } + } + return arguments; + } + + private Object resolve(String value, Class type) { + if (type.equals(String.class)) { + return value; + } else if (type.equals(Integer.class) || type.equals(int.class)) { + return Integer.parseInt(value); + } + throw new IllegalArgumentException("Can't convert '" + value + "' to " + type.getSimpleName()); + } + + /** + * Converts command handler return values to response strings. + * + * @param object Command return value + * @return Response string + */ + private String resolveReturnType(Object object) { + if (object instanceof SCPICommand) { + return ((SCPICommand) object).getRaw(); + } else if (object instanceof String) { + return (String) object; + } + return null; + } + + private void handle( + Matcher matcher, + Method method, + SCPICommand command) throws InvocationTargetException { + try { + Object[] arguments = resolve(matcher, method, command); + Object ret = method.invoke(this, arguments); + + String response = resolveReturnType(ret); + if (response != null) { + this.pushResponse(response); + } + } catch (IllegalAccessException | IllegalArgumentException e) { + //TODO: log + e.printStackTrace(); + } + } + + /** + * Called when the socket has received a command. + * + * @param in Received SCPI command + * @throws Exception when handling the command failed + */ + protected void onReceive(SCPICommand in) throws Exception { + for (Method method : this.getClass().getMethods()) { + if (method.isAnnotationPresent(OnCommand.class)) { + OnCommand path = method.getAnnotation(OnCommand.class); + Matcher matcher = matcher(path.value(), in.getCommand()); + if (matcher.matches()) { + handle(matcher, method, in); + return; + } + } + } + this.onNotMapped(in); + } + + /** + * Pushes the SCPI command to the response stream. + * + * @param response SCPI response + */ + protected final void pushResponse(String response) { + rxStream.offer(response); + } + + /** + * Called when receiving identification query command. + * + * @return Response including the device's identifier + */ + @OnCommand("\\*IDN\\?") + public final String onIdn() { + return idn.value(); + } + + /** + * Called when receiving procedure complete query. + * + * @return 1 + */ + @OnCommand("\\*OPC\\?") + public final String onOpc() { + return "1"; + } + + /** + * Called when receiving reset command. + */ + @OnCommand("\\*RST") + public final void onRst() { + this.onReset(); + } + + protected abstract void onReset(); + + /** + * Called when no mapping was found for the inbound command. + * + * @param command Inbound SCPI command + */ + protected abstract void onNotMapped(SCPICommand command); + + @Override + public final void send(SCPICommand... commands) throws IOException { + if (!socket.isConnected()) { + throw new IOException("Device not connected."); + } + for (SCPICommand command : commands) { + try { + onReceive(command); + } catch (Exception e) { + throw new IOException(e); + } + } + } + + @Override + public String receive(int count, long timeout) throws IOException { + throw new UnsupportedOperationException("Invalid operation"); + } + + @Override + public final String receive(long timeout) throws SocketTimeoutException, IOException { + try { + if (!socket.isConnected()) { + throw new IOException("Device not connected."); + } + String response = timeout == 0 + ? rxStream.take() : rxStream.poll(timeout, TimeUnit.MILLISECONDS); + if (response == null) { + throw new SocketTimeoutException("Response timed out."); + } + return response; + } catch (InterruptedException e) { + throw new IOException(); + } + } + +} diff --git a/measure-core/src/main/java/org/jtmc/core/scpi/mock/OnCommand.java b/measure-core/src/main/java/org/jtmc/core/scpi/mock/OnCommand.java index 33cf4d5..5547769 100644 --- a/measure-core/src/main/java/org/jtmc/core/scpi/mock/OnCommand.java +++ b/measure-core/src/main/java/org/jtmc/core/scpi/mock/OnCommand.java @@ -6,20 +6,18 @@ import java.lang.annotation.Target; /** - * OnCommand is used to annotate methods to be invoked when the given command is received - * - *

    - * + * OnCommand is used to annotate methods to be invoked when + * the given command is received. */ @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface OnCommand { - /** - * Regular expression, the annotated method will be invoked when - * this expression matches the incoming command - * - * @return Command's path - */ - String value(); -} \ No newline at end of file + /** + * Regular expression, the annotated method will be invoked when + * this expression matches the incoming command. + * + * @return Command's path + */ + String value(); +} diff --git a/measure-core/src/main/java/org/jtmc/core/scpi/mock/TestSCPISocket.java b/measure-core/src/main/java/org/jtmc/core/scpi/mock/TestSCPISocket.java index 6ef2174..c16dadd 100644 --- a/measure-core/src/main/java/org/jtmc/core/scpi/mock/TestSCPISocket.java +++ b/measure-core/src/main/java/org/jtmc/core/scpi/mock/TestSCPISocket.java @@ -4,19 +4,22 @@ import org.jtmc.core.visa.DeviceIdentifier; import org.jtmc.core.visa.mock.MockSocket; +/** + * TestSCPISocket can be used a base class for testing SCPI functionality. + */ public class TestSCPISocket extends MockSCPISocket { - public TestSCPISocket(DeviceIdentifier idn) { - super(new MockSocket("TestSocket", "inst0"), idn); - } + public TestSCPISocket(DeviceIdentifier idn) { + super(new MockSocket("TestSocket", "inst0"), idn); + } - @Override - protected void onReset() { - - } + @Override + protected void onReset() { - @Override - protected void onNotMapped(SCPICommand command) { - - } -} \ No newline at end of file + } + + @Override + protected void onNotMapped(SCPICommand command) { + + } +} diff --git a/measure-core/src/main/java/org/jtmc/core/scpi/package-info.java b/measure-core/src/main/java/org/jtmc/core/scpi/package-info.java index 4e77f6b..7bd186e 100644 --- a/measure-core/src/main/java/org/jtmc/core/scpi/package-info.java +++ b/measure-core/src/main/java/org/jtmc/core/scpi/package-info.java @@ -5,7 +5,7 @@ *

    The implementation of SCPI varies between instruments, this package * only covers parts of the standard that are required for compliance. * - *

    - * {@link https://www.ivifoundation.org/docs/scpi-99.pdf} + *

    For more information see + * SCPI-99 Standard */ -package org.jtmc.core.scpi; \ No newline at end of file +package org.jtmc.core.scpi; diff --git a/measure-core/src/main/java/org/jtmc/core/scpi/socket/RawSCPISocket.java b/measure-core/src/main/java/org/jtmc/core/scpi/socket/RawSCPISocket.java index 7bc6af3..1983720 100644 --- a/measure-core/src/main/java/org/jtmc/core/scpi/socket/RawSCPISocket.java +++ b/measure-core/src/main/java/org/jtmc/core/scpi/socket/RawSCPISocket.java @@ -5,14 +5,13 @@ import java.nio.ByteBuffer; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; - import org.jtmc.core.device.ISocket; import org.jtmc.core.scpi.ISCPISocket; import org.jtmc.core.scpi.SCPICommand; /** * RawSCPISocket implements the most common way of sending SCPI commands - * to instruments + * to instruments. * *

    By default it will send SCPI commands as is with newline terminations appended * after each command @@ -21,83 +20,91 @@ */ public class RawSCPISocket implements ISCPISocket { - public final static char DEFAULT_TERMINATION = '\n'; + public static final char DEFAULT_TERMINATION = '\n'; - public final static char DEFAULT_SEPARATOR = ';'; + public static final char DEFAULT_SEPARATOR = ';'; - private final char termination; + private final char termination; - private final char separator; + private final char separator; - private final ISocket socket; + private final ISocket socket; - private final Charset charset; + private final Charset charset; - /** - * Constructs a RawSCPISocket with the default termination and separator characters - * - * @param socket Socket to the instrument - */ - public RawSCPISocket(final ISocket socket) { - this(socket, DEFAULT_TERMINATION, DEFAULT_SEPARATOR); - } + /** + * Constructs a RawSCPISocket with the default termination and + * separator characters. + * + * @param socket Socket to the instrument + */ + public RawSCPISocket(final ISocket socket) { + this(socket, DEFAULT_TERMINATION, DEFAULT_SEPARATOR); + } - /** - * Constructs a RawSCPISocket with the provided termination and separator characters - * - * @param socket Socket to the instrument - * @param termination Character used to terminate all commands - * @param separator Character used to separate commands - */ - public RawSCPISocket(final ISocket socket, final char termination, final char separator) { - this(socket, termination, separator, StandardCharsets.ISO_8859_1); - } + /** + * Constructs a RawSCPISocket with the provided termination and + * separator characters. + * + * @param socket Socket to the instrument + * @param termination Character used to terminate all commands + * @param separator Character used to separate commands + */ + public RawSCPISocket(final ISocket socket, final char termination, final char separator) { + this(socket, termination, separator, StandardCharsets.ISO_8859_1); + } - /** - * Constructs a RawSCPISocket with the default termination and separator characters - * - * @param socket Socket to the instrument - * @param termination Character used to terminate all commands - * @param separator Character used to separate commands - * @param charset Character encoding to use - */ - public RawSCPISocket(final ISocket socket, final char termination, final char separator, final Charset charset) { - this.socket = socket; - this.termination = termination; - this.separator = separator; - this.charset = charset; - } + /** + * Constructs a RawSCPISocket with the default termination and + * separator characters. + * + * @param socket Socket to the instrument + * @param termination Character used to terminate all commands + * @param separator Character used to separate commands + * @param charset Character encoding to use + */ + public RawSCPISocket( + final ISocket socket, + final char termination, + final char separator, + final Charset charset) { + this.socket = socket; + this.termination = termination; + this.separator = separator; + this.charset = charset; + } - @Override - public void send(SCPICommand... commands) throws IOException { - if(commands.length == 0) { - return; - } - ByteBuffer output = ByteBuffer.wrap(concat(this.termination, this.separator, commands).getBytes(charset)); - socket.send(output); - } + @Override + public void send(SCPICommand... commands) throws IOException { + if (commands.length == 0) { + return; + } + ByteBuffer output = ByteBuffer.wrap( + concat(this.termination, this.separator, commands).getBytes(charset)); + socket.send(output); + } - @Override - public String receive(long timeout) throws SocketTimeoutException, IOException { - ByteBuffer response = socket.receive(this.termination, timeout); - return new String(response.array(), charset); - } + @Override + public String receive(long timeout) throws SocketTimeoutException, IOException { + ByteBuffer response = socket.receive(this.termination, timeout); + return new String(response.array(), charset); + } - @Override - public String receive(int count, long timeout) throws SocketTimeoutException, IOException { - ByteBuffer response = socket.receive(count, timeout); - return new String(response.array(), charset); - } - - static String concat(char termination, char separator, SCPICommand... commands) { - StringBuilder builder = new StringBuilder(); - for(int i=0;i[0-9]+)/(?[0-9])/(?[NOEMS])/(?[12])"); - - /** - * Constructs a SerialSocket using a string configuration - * - *

    Format: Baudrate/Databits/Parity/Stopbits - * - *

    Baudrate: any integer - *

    Databits: integer, 5-8 - *

    Parity: N (None), O (Odd), E (Even), M (Mark), S (Space) - * - *

    Example: 9600/8/N/1 - * - * @param port Port identifier, on Windows COMn, on Unix /dev/ttySn - * @param params Configuration as string - * @return Serial socket - * @throws IOException - */ - public static SerialSocket create(String port, String params) throws IOException { - Matcher matcher = CONFIG_PATTERN.matcher(params); - if(!matcher.matches()) { - throw new IllegalArgumentException("Invalid serial socket configuration: " + params); - } - int baudrate = Integer.parseInt(matcher.group("baudrate")); - int databits = Integer.parseInt(matcher.group("databits")); - String parity = matcher.group("parity"); - int stopbits = Integer.parseInt(matcher.group("stopbits")); - - return new SerialSocket(port, baudrate, databits, Parity.valueOf(parity.charAt(0)), StopBits.valueOf(stopbits)); - } - - @Override - public void close() { - socket.closePort(); - } - - @Override - public boolean isConnected() { - return this.socket != null && this.socket.isOpen(); - } - - @Override - public String getResourceString() { - return "ASRL" + SerialSocketFactory.getPortNumber(this.socket.getSystemPortName()); - } - - @Override - public void send(ByteBuffer message) throws IOException, SerialPortIOException, SerialPortTimeoutException { - if(socket == null) { - throw new IOException("Socket is closed."); - } - if(!message.hasArray()) { - throw new IllegalArgumentException("Message empty."); - } - socket.getOutputStream().write(message.array()); - } - - @Override - public ByteBuffer receive(int count, long timeout) throws IOException, SocketTimeoutException { - //int previousTimeout = socket.getSerialPortTimeout(); - //socket.setSerialPortTimeout((int)timeout); - //socket.setComPortTimeouts(newTimeoutMode, newReadTimeout, newWriteTimeout); - - byte[] input = new byte[count]; - socket.getInputStream().read(input); - - //socket.setSerialPortTimeout(previousTimeout); - - return ByteBuffer.wrap(input); - } - - @Override - public ByteBuffer receive(char delimiter, long timeout) throws IOException, SocketTimeoutException { - try { - //int previousTimeout = socket.getSerialPortTimeout(); - //socket.setSerialPortTimeout((int)timeout); - - ArrayList input = new ArrayList<>(); - byte in = (byte) socket.getInputStream().read(); - while(in != delimiter) { - input.add(in); - in = (byte) socket.getInputStream().read(); - } - //socket.setSerialPortTimeout(previousTimeout); - ByteBuffer bytes = ByteBuffer.allocate(input.size()); - for(byte b : input) { - bytes.put(b); - } - return bytes; - } catch(SerialPortTimeoutException e) { - throw new SocketTimeoutException(e.getMessage()); - } - } - -} \ No newline at end of file + private SerialPort socket; + + /** + * Constructs a Serial socket with the provided baudrate and default 8 bit data length, + * no parity and a single stopbit. Flow control is disabled. + * + * @param port Port identifier, on Windows COMn, on Unix /dev/ttySn + * @param baudrate Baudrate + * @throws IOException if there was an error opening the socket + */ + public SerialSocket(String port, int baudrate) throws IOException { + this(port, baudrate, 8, Parity.NONE, StopBits.ONE); + } + + /** + * Constructs a Serial socket with the provided parameters. Flow control is disabled. + * + * @param port Port identifier, on Windows COMn, on Unix /dev/ttySn + * @param baudrate Baudrate + * @param databits Number of databits, valid values are {@code 5-8} + * @param parity Parity bit (None, Even, Odd, Mark, Space) + * @param stopbits Stop bit count + * @throws IOException if there was an error opening the socket + */ + public SerialSocket( + String port, + int baudrate, + int databits, + Parity parity, + StopBits stopbits) throws IOException { + this(port, baudrate, databits, parity, stopbits, FlowControl.NONE); + } + + /** + * Constructs a Serial socket with the provided parameters. + * + * @param port Port identifier, on Windows COMn, on Unix /dev/ttySn + * @param baudrate Baudrate + * @param databits Number of databits, valid values are {@code 5-8} + * @param parity Parity bit (None, Even, Odd, Mark, Space) + * @param stopbits Stop bit count + * @param flowControl Flow control mode + * @throws IOException if there was an error opening the socket + */ + public SerialSocket( + String port, + int baudrate, + int databits, + Parity parity, + StopBits stopbits, + FlowControl flowControl) throws IOException { + this.socket = SerialPort.getCommPort(port); + this.socket.openPort(); + this.socket.setBaudRate(baudrate); + this.socket.setNumDataBits(databits); + this.socket.setParity(parity.getId()); + this.socket.setNumStopBits(stopbits.getId()); + this.socket.setFlowControl(flowControl.getValue()); + } + + private static final Pattern CONFIG_PATTERN = Pattern.compile( + "(?[0-9]+)/(?[0-9])/(?[NOEMS])/(?[12])"); + + /** + * Constructs a SerialSocket using a string configuration. + * + *

    Format: Baudrate/Databits/Parity/Stopbits + * + *

    Baudrate: any integer + * + *

    Databits: integer, 5-8 + * + *

    Parity: N (None), O (Odd), E (Even), M (Mark), S (Space) + * + *

    Example: 9600/8/N/1 + * + * @param port Port identifier, on Windows COMn, on Unix /dev/ttySn + * @param params Configuration as string + * @return Serial socket + * @throws IOException if there was an error opening the socket + */ + public static SerialSocket create(String port, String params) throws IOException { + Matcher matcher = CONFIG_PATTERN.matcher(params); + if (!matcher.matches()) { + throw new IllegalArgumentException("Invalid serial socket configuration: " + params); + } + int baudrate = Integer.parseInt(matcher.group("baudrate")); + int databits = Integer.parseInt(matcher.group("databits")); + String parity = matcher.group("parity"); + String stopbits = matcher.group("stopbits"); + + return new SerialSocket( + port, + baudrate, + databits, + Parity.valueOf(parity.charAt(0)), + StopBits.from(stopbits)); + } + + @Override + public void close() { + socket.closePort(); + } + + @Override + public boolean isConnected() { + return this.socket != null && this.socket.isOpen(); + } + + @Override + public String getResourceString() { + return "ASRL" + SerialSocketFactory.getPortNumber(this.socket.getSystemPortName()); + } + + @Override + public void send(ByteBuffer message) + throws IOException, SerialPortIOException, SerialPortTimeoutException { + if (socket == null) { + throw new IOException("Socket is closed."); + } + if (!message.hasArray()) { + throw new IllegalArgumentException("Message empty."); + } + socket.getOutputStream().write(message.array()); + } + + @Override + public ByteBuffer receive(int count, long timeout) + throws IOException, SocketTimeoutException { + //int previousTimeout = socket.getSerialPortTimeout(); + //socket.setSerialPortTimeout((int)timeout); + //socket.setComPortTimeouts(newTimeoutMode, newReadTimeout, newWriteTimeout); + + byte[] input = new byte[count]; + socket.getInputStream().read(input); + + //socket.setSerialPortTimeout(previousTimeout); + + return ByteBuffer.wrap(input); + } + + @Override + public ByteBuffer receive(char delimiter, long timeout) + throws IOException, SocketTimeoutException { + try { + //int previousTimeout = socket.getSerialPortTimeout(); + //socket.setSerialPortTimeout((int)timeout); + + ArrayList input = new ArrayList<>(); + byte in = (byte) socket.getInputStream().read(); + while (in != delimiter) { + input.add(in); + in = (byte) socket.getInputStream().read(); + } + //socket.setSerialPortTimeout(previousTimeout); + ByteBuffer bytes = ByteBuffer.allocate(input.size()); + for (byte b : input) { + bytes.put(b); + } + return bytes; + } catch (SerialPortTimeoutException e) { + throw new SocketTimeoutException(e.getMessage()); + } + } + +} diff --git a/measure-core/src/main/java/org/jtmc/core/serial/SerialSocketFactory.java b/measure-core/src/main/java/org/jtmc/core/serial/SerialSocketFactory.java index 334caac..ef27133 100644 --- a/measure-core/src/main/java/org/jtmc/core/serial/SerialSocketFactory.java +++ b/measure-core/src/main/java/org/jtmc/core/serial/SerialSocketFactory.java @@ -6,44 +6,48 @@ import org.jtmc.core.visa.factory.ISocketFactory; /** - * SerialSocketFactory + * SerialSocketFactory can be used to instantiate Serial Socket using + * a VISA resource string. */ public class SerialSocketFactory implements ISocketFactory { - private Pattern pattern = Pattern.compile("ASRL(?[0-9]*)::INSTR"); - - @Override - public boolean supports(String connectionInfo) { - return pattern.matcher(connectionInfo).matches(); - } - - @Override - public SerialSocket create(String connectionInfo) throws IOException { - Matcher matcher = pattern.matcher(connectionInfo); - if(matcher.matches()) { - int port = Integer.parseInt(matcher.group("port")); - //TODO: serial configuration - return new SerialSocket(getPortPath(port), 9600); - } - throw new IllegalArgumentException(connectionInfo + " doesn't match " + pattern.pattern()); - } - - /** - * - * @param port - * @return - */ - public static String getPortPath(int port) { - String os = System.getProperty("os.name"); - if(os.startsWith("Windows")) { - return "COM" + port; - } else if(os.contains("nix") || os.contains("nux")) { - return "/dev/ttyS" + port; - } - return null; - } - - public static int getPortNumber(String portPath) { - return 0; - } -} \ No newline at end of file + private static final Pattern pattern = Pattern.compile( + "ASRL(?[0-9]*)::INSTR"); + + @Override + public boolean supports(String connectionInfo) { + return pattern.matcher(connectionInfo).matches(); + } + + @Override + public SerialSocket create(String connectionInfo) throws IOException { + Matcher matcher = pattern.matcher(connectionInfo); + if (matcher.matches()) { + int port = Integer.parseInt(matcher.group("port")); + //TODO: serial configuration + return new SerialSocket(getPortPath(port), 9600); + } + throw new IllegalArgumentException(connectionInfo + " doesn't match " + pattern.pattern()); + } + + /** + * Returns the serial port name for the given number. + * + * @param port Port number + * @return Serial port name + */ + public static String getPortPath(int port) { + String os = System.getProperty("os.name"); + if (os.startsWith("Windows")) { + return "COM" + port; + } else if (os.contains("nix") || os.contains("nux")) { + return "/dev/ttyS" + port; + } + return null; + } + + public static int getPortNumber(String portPath) { + return 0; + } + +} diff --git a/measure-core/src/main/java/org/jtmc/core/serial/StopBits.java b/measure-core/src/main/java/org/jtmc/core/serial/StopBits.java index d656313..0200534 100644 --- a/measure-core/src/main/java/org/jtmc/core/serial/StopBits.java +++ b/measure-core/src/main/java/org/jtmc/core/serial/StopBits.java @@ -2,38 +2,61 @@ import com.fazecast.jSerialComm.SerialPort; +/** + * StopBits represents the spacing after each UART byte. + */ public enum StopBits { - ONE(SerialPort.ONE_STOP_BIT, 1.0f), - ONE_AND_HALF(SerialPort.ONE_POINT_FIVE_STOP_BITS, 1.5f), - TWO(SerialPort.TWO_STOP_BITS, 2.0f); - - private int id; - - private float bitcount; - - private StopBits(int id, float bitcount) { - this.id = id; - this.bitcount = bitcount; - } - - public int getId() { - return id; - } - - public float getBitCount() { - return bitcount; - } - - public static StopBits valueOf(float bitcount) { - if(bitcount == 1.0f) { - return StopBits.ONE; - } - else if(bitcount == 1.5f) { - return StopBits.ONE_AND_HALF; - } - else if(bitcount == 2.0f) { - return StopBits.TWO; - } - throw new IllegalArgumentException("Invalid stopbit value: " + bitcount); - } -} \ No newline at end of file + ONE(SerialPort.ONE_STOP_BIT, 1.0f), + ONE_AND_HALF(SerialPort.ONE_POINT_FIVE_STOP_BITS, 1.5f), + TWO(SerialPort.TWO_STOP_BITS, 2.0f); + + private int id; + + private float bitcount; + + private StopBits(int id, float bitcount) { + this.id = id; + this.bitcount = bitcount; + } + + public int getId() { + return id; + } + + public float getBitCount() { + return bitcount; + } + + /** + * Returns the stopbits for the given bitcount. + * + * @param bitcount Bitcount (floating) + * @return Stopbits + */ + public static StopBits from(float bitcount) { + if (bitcount == 1.0f) { + return StopBits.ONE; + } else if (bitcount == 1.5f) { + return StopBits.ONE_AND_HALF; + } else if (bitcount == 2.0f) { + return StopBits.TWO; + } + throw new IllegalArgumentException("Invalid stopbit value: " + bitcount); + } + + /** + * Returns the stopbits for the given bitcount. + * + * @param bitcount Bitcount (string), one of '1', '1.5', '2' + * @return Stopbits + */ + public static StopBits from(String bitcount) { + switch (bitcount) { + case "1": return StopBits.ONE; + case "1.5": return StopBits.ONE_AND_HALF; + case "2": return StopBits.TWO; + default: + throw new IllegalArgumentException("Invalid stopbit value: " + bitcount); + } + } +} diff --git a/measure-core/src/main/java/org/jtmc/core/signal/CompositeSignal.java b/measure-core/src/main/java/org/jtmc/core/signal/CompositeSignal.java index 4adca94..e1b8b90 100644 --- a/measure-core/src/main/java/org/jtmc/core/signal/CompositeSignal.java +++ b/measure-core/src/main/java/org/jtmc/core/signal/CompositeSignal.java @@ -5,37 +5,37 @@ import java.util.Set; /** - * CompositeSignal + * CompositeSignal contains multiple time correlated signals. */ public class CompositeSignal> { - private String id; + private String id; - private Set> signals = new HashSet<>(); + private Set> signals = new HashSet<>(); - public CompositeSignal(String id) { - this.id = id; - } + public CompositeSignal(String id) { + this.id = id; + } - public CompositeSignal(Signal signal) { - this(signal.getId()); - this.signals.add(signal); - } + public CompositeSignal(Signal signal) { + this(signal.getId()); + this.signals.add(signal); + } - protected void add(Signal signal) { - signals.add(signal); - } + protected void add(Signal signal) { + signals.add(signal); + } - public Optional> getSignal(String id) { - return signals.stream().filter(signal -> signal.getId().equals(id)).findFirst(); - } + public Optional> getSignal(String id) { + return signals.stream().filter(signal -> signal.getId().equals(id)).findFirst(); + } - public Set> getSignals() { - return signals; - } + public Set> getSignals() { + return signals; + } - public String getId() { - return id; - } + public String getId() { + return id; + } -} \ No newline at end of file +} diff --git a/measure-core/src/main/java/org/jtmc/core/signal/Signal.java b/measure-core/src/main/java/org/jtmc/core/signal/Signal.java index dd029d6..84d1249 100644 --- a/measure-core/src/main/java/org/jtmc/core/signal/Signal.java +++ b/measure-core/src/main/java/org/jtmc/core/signal/Signal.java @@ -7,81 +7,86 @@ import java.util.stream.Stream; /** + * Signal is a container for time series values. * - * @param + * @param Value type */ public class Signal> { - - private String id; - - private TreeSet> data = new TreeSet>((a, b) -> Double.compare(a.time, b.time)); - - public Signal(String id) { - this.id = id; - } + + private String id; + + private TreeSet> data = new TreeSet>( + (a, b) -> Double.compare(a.time, b.time)); + + public Signal(String id) { + this.id = id; + } - public Signal(Signal signal) { - this(signal.getId()); - this.data.addAll(signal.getData()); - } + public Signal(Signal signal) { + this(signal.getId()); + this.data.addAll(signal.getData()); + } - public > Signal(Signal signal, Function transform) { - this(signal.getId()); - signal.stream().map(p -> new DataPoint(p.time, transform.apply(p.value))).forEach(this::add); - } - - public Set> getData() { - return data; - } + public > Signal(Signal signal, Function transform) { + this(signal.getId()); + signal.stream().map(p -> new DataPoint(p.time, transform.apply(p.value))).forEach(this::add); + } + + public Set> getData() { + return data; + } - public String getId() { - return id; - } - - public void add(DataPoint dp) { - this.data.add(dp); - } - - public void add(double time, T value) { - this.data.add(new DataPoint(time, value)); - } + public String getId() { + return id; + } + + public void add(DataPoint dp) { + this.data.add(dp); + } + + public void add(double time, T value) { + this.data.add(new DataPoint(time, value)); + } - public Stream> stream() { - return this.data.stream(); - } + public Stream> stream() { + return this.data.stream(); + } - public T first() { - return this.data.first().value; - } + public T first() { + return this.data.first().value; + } - public T last() { - return this.data.last().value; - } - - public double period() { - return data.last().time; - } + public T last() { + return this.data.last().value; + } + + public double period() { + return data.last().time; + } - public T min() { - return Collections.min(data).value; - } + public T min() { + return Collections.min(data).value; + } - public T max() { - return Collections.max(data).value; - } + public T max() { + return Collections.max(data).value; + } - public static class DataPoint> implements Comparable> { - public final double time; - public final T value; + /** + * DataPoint is a single measurement at a given time with a given value. + */ + public static class DataPoint> implements Comparable> { + public final double time; + public final T value; - public DataPoint(double time, T value) { - this.time = time; - this.value = value; - } - - @Override - public int compareTo(DataPoint arg0) { - return this.value.compareTo(arg0.value); - } - } + public DataPoint(double time, T value) { + this.time = time; + this.value = value; + } + + @Override + public int compareTo(DataPoint arg0) { + return this.value.compareTo(arg0.value); + } + } } diff --git a/measure-core/src/main/java/org/jtmc/core/signal/Waveform.java b/measure-core/src/main/java/org/jtmc/core/signal/Waveform.java deleted file mode 100644 index a83cb40..0000000 --- a/measure-core/src/main/java/org/jtmc/core/signal/Waveform.java +++ /dev/null @@ -1,17 +0,0 @@ -package org.jtmc.core.signal; - -import org.jtmc.core.util.EnumParameters; - -import org.jtmc.core.signal.Waveform.WaveformParameter; - -public class Waveform extends EnumParameters { - - public static enum WaveformParameter { - PERIOD, FREQUENCY, AMPLITUDE, OFFSET, DUTY, SYMMETRY, PHASE, STDEV, MEAN, WIDTH, RISE, FALL, DELAY; - } - - public static enum WaveformType { - SINE, SQUARE, RAMP, PULSE, NOISE, DC; - } - -} \ No newline at end of file diff --git a/measure-core/src/main/java/org/jtmc/core/signal/WaveformUtil.java b/measure-core/src/main/java/org/jtmc/core/signal/WaveformUtil.java index c865f78..7f89328 100644 --- a/measure-core/src/main/java/org/jtmc/core/signal/WaveformUtil.java +++ b/measure-core/src/main/java/org/jtmc/core/signal/WaveformUtil.java @@ -1,9 +1,8 @@ package org.jtmc.core.signal; /** - * WaveformUtil + * WaveformUtil provides methods for creating waveforms. */ public class WaveformUtil { - -} \ No newline at end of file +} diff --git a/measure-core/src/main/java/org/jtmc/core/signal/analog/AnalogSampler.java b/measure-core/src/main/java/org/jtmc/core/signal/analog/AnalogSampler.java index e56f6f7..dbb9b79 100644 --- a/measure-core/src/main/java/org/jtmc/core/signal/analog/AnalogSampler.java +++ b/measure-core/src/main/java/org/jtmc/core/signal/analog/AnalogSampler.java @@ -3,8 +3,8 @@ import org.jtmc.core.signal.sampler.Sampler; /** - * AnalogSampler takes an analog signal and transforms it into another analog signal + * AnalogSampler takes an analog signal and transforms it into another analog signal. */ public interface AnalogSampler extends Sampler { - -} \ No newline at end of file + +} diff --git a/measure-core/src/main/java/org/jtmc/core/signal/analog/AnalogSignal.java b/measure-core/src/main/java/org/jtmc/core/signal/analog/AnalogSignal.java index 6476c1c..b897641 100644 --- a/measure-core/src/main/java/org/jtmc/core/signal/analog/AnalogSignal.java +++ b/measure-core/src/main/java/org/jtmc/core/signal/analog/AnalogSignal.java @@ -4,24 +4,24 @@ import org.jtmc.core.signal.Signal; /** - * AnalogSignal + * AnalogSignal is a numeric signal consisting of a floating point number stream. */ public class AnalogSignal extends Signal { - public AnalogSignal(String id) { - super(id); - } + public AnalogSignal(String id) { + super(id); + } - public > AnalogSignal(Signal signal, Function transform) { - super(signal, transform); - } + public > AnalogSignal(Signal signal, Function transform) { + super(signal, transform); + } - public double amplitude() { - return max() - min(); - } - - public double offset() { - return (max() + min()) / 2.0; - } - -} \ No newline at end of file + public double amplitude() { + return max() - min(); + } + + public double offset() { + return (max() + min()) / 2.0; + } + +} diff --git a/measure-core/src/main/java/org/jtmc/core/signal/digital/BinarySampler.java b/measure-core/src/main/java/org/jtmc/core/signal/digital/BinarySampler.java index 3d4c036..64188af 100644 --- a/measure-core/src/main/java/org/jtmc/core/signal/digital/BinarySampler.java +++ b/measure-core/src/main/java/org/jtmc/core/signal/digital/BinarySampler.java @@ -3,9 +3,8 @@ import org.jtmc.core.signal.sampler.Sampler; /** - * BinarySampler takes a binary signal and outputs a transformed binary signal + * BinarySampler takes a binary signal and outputs a transformed binary signal. */ public interface BinarySampler extends Sampler { - -} \ No newline at end of file +} diff --git a/measure-core/src/main/java/org/jtmc/core/signal/digital/BinarySignal.java b/measure-core/src/main/java/org/jtmc/core/signal/digital/BinarySignal.java index 59b4da8..b6cbfbd 100644 --- a/measure-core/src/main/java/org/jtmc/core/signal/digital/BinarySignal.java +++ b/measure-core/src/main/java/org/jtmc/core/signal/digital/BinarySignal.java @@ -3,12 +3,12 @@ import org.jtmc.core.signal.Signal; /** - * AnalogSignal + * BinarySignal is a logical signal consisting and 0's and 1's. */ public class BinarySignal extends Signal { - public BinarySignal(String id) { - super(id); - } - -} \ No newline at end of file + public BinarySignal(String id) { + super(id); + } + +} diff --git a/measure-core/src/main/java/org/jtmc/core/signal/sampler/EdgeSampler.java b/measure-core/src/main/java/org/jtmc/core/signal/sampler/EdgeSampler.java index a6dbaa7..661c268 100644 --- a/measure-core/src/main/java/org/jtmc/core/signal/sampler/EdgeSampler.java +++ b/measure-core/src/main/java/org/jtmc/core/signal/sampler/EdgeSampler.java @@ -1,41 +1,41 @@ package org.jtmc.core.signal.sampler; import java.util.Iterator; - import org.jtmc.core.signal.Signal; import org.jtmc.core.signal.Signal.DataPoint; /** - * EdgeSampler takes any signal and outputs a signal which is sampled at the given rate + * EdgeSampler takes any signal and outputs a signal which is sampled at the given rate. */ public class EdgeSampler> implements Sampler> { - private long points; + private long points; - public EdgeSampler(long points) { - this.points = points; - } + public EdgeSampler(long points) { + this.points = points; + } - @Override - public Signal sample(Signal signal) { - Signal output = new Signal(signal.getId()); - double timestep = signal.period() / points; - - Iterator> point = signal.getData().iterator(); - int index = 0; - - while(point.hasNext()) { - DataPoint p = point.next(); - for(;index * timestep < p.time || (!point.hasNext() && index < points);index++) { - output.add(index * timestep, p.value); - } - } - - return output; - } + @Override + public Signal sample(Signal signal) { + Signal output = new Signal(signal.getId()); + double timestep = signal.period() / points; + + Iterator> point = signal.getData().iterator(); + int index = 0; + + while (point.hasNext()) { + DataPoint p = point.next(); + while (index * timestep < p.time || (!point.hasNext() && index < points)) { + output.add(index * timestep, p.value); + index++; + } + } + + return output; + } - public long getPoints() { - return points; - } - -} \ No newline at end of file + public long getPoints() { + return points; + } + +} diff --git a/measure-core/src/main/java/org/jtmc/core/signal/sampler/LinearSampler.java b/measure-core/src/main/java/org/jtmc/core/signal/sampler/LinearSampler.java index dcbe439..eb923b5 100644 --- a/measure-core/src/main/java/org/jtmc/core/signal/sampler/LinearSampler.java +++ b/measure-core/src/main/java/org/jtmc/core/signal/sampler/LinearSampler.java @@ -3,13 +3,14 @@ import org.jtmc.core.signal.Signal; /** - * LinearSampler + * LinearSampler takes any numeric signal and uses linear interpolation between + * values to fill out the missing parts. */ public class LinearSampler> implements Sampler> { - @Override - public Signal sample(Signal signal) { - return null; - } + @Override + public Signal sample(Signal signal) { + return null; + } -} \ No newline at end of file +} diff --git a/measure-core/src/main/java/org/jtmc/core/signal/sampler/Sampler.java b/measure-core/src/main/java/org/jtmc/core/signal/sampler/Sampler.java index f451cfe..36688b2 100644 --- a/measure-core/src/main/java/org/jtmc/core/signal/sampler/Sampler.java +++ b/measure-core/src/main/java/org/jtmc/core/signal/sampler/Sampler.java @@ -3,19 +3,19 @@ import org.jtmc.core.signal.Signal; /** - * A sampler takes in an existing signal and converts it to a different signal + * A sampler takes in an existing signal and converts it to a different signal. * *

    For example it may take an Analog Signal then using some threshold * it could convert it into a digital signal */ public interface Sampler, U extends Signal>> { - /** - * Converts the given signal into a different signal - * - * @param signal Input signal - * @return Transformed signal - */ - public U sample(Signal signal); + /** + * Converts the given signal into a different signal. + * + * @param signal Input signal + * @return Transformed signal + */ + public U sample(Signal signal); -} \ No newline at end of file +} diff --git a/measure-core/src/main/java/org/jtmc/core/signal/sampler/SincFilter.java b/measure-core/src/main/java/org/jtmc/core/signal/sampler/SincFilter.java index c1df05d..e1269b7 100644 --- a/measure-core/src/main/java/org/jtmc/core/signal/sampler/SincFilter.java +++ b/measure-core/src/main/java/org/jtmc/core/signal/sampler/SincFilter.java @@ -1,8 +1,8 @@ package org.jtmc.core.signal.sampler; /** - * SincSampler + * SincFilter takes any numeric signal and uses Sine over X to interpolate the signal. */ public class SincFilter { - -} \ No newline at end of file + //TODO: implement +} diff --git a/measure-core/src/main/java/org/jtmc/core/util/EnumParameters.java b/measure-core/src/main/java/org/jtmc/core/util/EnumParameters.java index b9e7215..9095ac5 100644 --- a/measure-core/src/main/java/org/jtmc/core/util/EnumParameters.java +++ b/measure-core/src/main/java/org/jtmc/core/util/EnumParameters.java @@ -7,94 +7,102 @@ import java.util.Set; /** - * EnumParameters is a convenience class for building prototype objects + * EnumParameters is a convenience class for building prototype objects. */ public abstract class EnumParameters> { - private Map params = new HashMap<>(); + private Map params = new HashMap<>(); - /** - * Returns the parameters that have an assigned value - * @return Set of parameters - */ - public Set getParameters() { - return new HashSet<>(params.keySet()); - } + /** + * Returns the parameters that have an assigned value. + * + * @return Set of parameters + */ + public Set getParameters() { + return new HashSet<>(params.keySet()); + } - /** - * Returns a given parameter as float - * @param param Parameter - * @return Float value - * @throws ClassCastException if the parameter isn't float - */ - public float getFloat(T param) { - return (float) params.get(param); - } + /** + * Returns a given parameter as float. + * + * @param param Parameter + * @return Float value + * @throws ClassCastException if the parameter isn't float + */ + public float getFloat(T param) { + return (float) params.get(param); + } - /** - * Returns a given parameter as int - * @param param Parameter - * @return Integer value - * @throws ClassCastException if the parameter isn't int - */ - public int getInt(T param) { - return (int) params.get(param); - } + /** + * Returns a given parameter as int. + * + * @param param Parameter + * @return Integer value + * @throws ClassCastException if the parameter isn't int + */ + public int getInt(T param) { + return (int) params.get(param); + } - /** - * Returns a given parameter as boolean - * @param param Parameter - * @return Boolean value - * @throws ClassCastException if the parameter isn't boolean - */ - public boolean getBoolean(T param) { - return (boolean) params.get(param); - } + /** + * Returns a given parameter as boolean. + * + * @param param Parameter + * @return Boolean value + * @throws ClassCastException if the parameter isn't boolean + */ + public boolean getBoolean(T param) { + return (boolean) params.get(param); + } - /** - * Assigns the given object to the parameter - * @param param Parameter - * @param value Assigned value - * @return Previously assigned value - */ - protected Object put(T param, Object value) { - return params.put(param, value); - } + /** + * Assigns the given object to the parameter. + * + * @param param Parameter + * @param value Assigned value + * @return Previously assigned value + */ + protected Object put(T param, Object value) { + return params.put(param, value); + } - /** - * Removes the assigned value from given parameter - * @param param Parameter - * @return Assigned value - */ - protected Object remove(T param) { - return params.remove(param); - } + /** + * Removes the assigned value from given parameter. + * + * @param param Parameter + * @return Assigned value + */ + protected Object remove(T param) { + return params.remove(param); + } - /** - * Returns whether the parameter has any value assigned to it - * @param param Parameter - * @return {@code true} if the parameter has an object assigned to it - */ - public boolean has(T param) { - return params.containsKey(param); - } + /** + * Returns whether the parameter has any value assigned to it. + * + * @param param Parameter + * @return {@code true} if the parameter has an object assigned to it + */ + public boolean has(T param) { + return params.containsKey(param); + } - /** - * Optionally returns the value associated to the given parameter - * @param Type of the parameter - * @param param Parameter - * @return Optional of value, {@code null} if the parameter has no value or isn't of type {@code U} - */ - @SuppressWarnings("unchecked") - public Optional get(T param) { - try { - Object o = params.get(param); - if(o == null) { - return Optional.empty(); - } - return Optional.of((U) o); - } catch(ClassCastException e) { - return Optional.empty(); - } - } -} \ No newline at end of file + /** + * Optionally returns the value associated to the given parameter. + * + * @param Type of the parameter + * @param param Parameter + * @return Optional of value, empty if the parameter has no value or isn't of type {@code U} + */ + @SuppressWarnings("unchecked") + public Optional get(T param) { + try { + Object o = params.get(param); + if (o == null) { + return Optional.empty(); + } + return Optional.of((U) o); + } catch (ClassCastException e) { + return Optional.empty(); + } + } +} diff --git a/measure-core/src/main/java/org/jtmc/core/util/Units.java b/measure-core/src/main/java/org/jtmc/core/util/Units.java index dbb5960..11e33a0 100644 --- a/measure-core/src/main/java/org/jtmc/core/util/Units.java +++ b/measure-core/src/main/java/org/jtmc/core/util/Units.java @@ -3,68 +3,65 @@ import java.util.Locale; /** - * Util + * Units provides user friendly unit conversion methods. */ public final class Units { - public static final double MEGA = 1000000.0; - - public static final double KILO = 1000.0; - - public static final double MILLI = 0.001; - - public static final double MICRO = 0.000001; - - public static final double NANO = 0.000000001; - - public static final double PICO = 0.00000000001; - - public static double mega(double value) { - return MEGA * value; - } - - public static double kilo(double value) { - return KILO * value; - } - - public static double milli(double value) { - return MILLI * value; - } - - public static double micro(double value) { - return MICRO * value; - } - - public static double nano(double value) { - return NANO * value; - } - - public static double nV(double nanovolts) { - return nano(nanovolts); - } - - public static double mV(double millivolts) { - return milli(millivolts); - } - - public static String auto(double value, int digits, String unit) { - double abs = Math.abs(value); - if(abs >= 1) { - return toString(value, 1, digits, unit); - } - else if(abs >= Units.milli(1)) { - return toString(value, MILLI, digits, "m" + unit); - } - else if(abs >= Units.micro(1)) { - return toString(value, MICRO, digits, "u" + unit); - } - else if(abs >= Units.nano(1)) { - return toString(value, NANO, digits, "n" + unit); - } - return toString(value, NANO, digits, unit); - } - - private static String toString(double value, double range, int digits, String unit) { - return String.format(Locale.US, "%.0" + digits + "f" + unit, value / range); - } -} \ No newline at end of file + public static final double MEGA = 1000000.0; + + public static final double KILO = 1000.0; + + public static final double MILLI = 0.001; + + public static final double MICRO = 0.000001; + + public static final double NANO = 0.000000001; + + public static final double PICO = 0.00000000001; + + public static double mega(double value) { + return MEGA * value; + } + + public static double kilo(double value) { + return KILO * value; + } + + public static double milli(double value) { + return MILLI * value; + } + + public static double micro(double value) { + return MICRO * value; + } + + public static double nano(double value) { + return NANO * value; + } + + /** + * Converts a floating point number into a string with the SI-prefixum and unit appended. + * + * @param value Input value + * @param digits Number of digits to keep + * @param unit Unit type + * @return Value with SI-prefixum and unit + */ + public static String auto(double value, int digits, String unit) { + double abs = Math.abs(value); + if (abs >= 1) { + return toString(value, 1, digits, unit); + } else if (abs >= Units.milli(1)) { + return toString(value, MILLI, digits, "m" + unit); + } else if (abs >= Units.micro(1)) { + return toString(value, MICRO, digits, "u" + unit); + } else if (abs >= Units.nano(1)) { + return toString(value, NANO, digits, "n" + unit); + } + return toString(value, NANO, digits, unit); + } + + private static String toString(double value, double range, int digits, String unit) { + return String.format(Locale.US, "%.0" + digits + "f" + unit, value / range); + } +} diff --git a/measure-core/src/main/java/org/jtmc/core/visa/DeviceIdentifier.java b/measure-core/src/main/java/org/jtmc/core/visa/DeviceIdentifier.java index 7c4aafa..20116d3 100644 --- a/measure-core/src/main/java/org/jtmc/core/visa/DeviceIdentifier.java +++ b/measure-core/src/main/java/org/jtmc/core/visa/DeviceIdentifier.java @@ -1,91 +1,102 @@ package org.jtmc.core.visa; /** - * Device identifier, specified in the SCPI-99 standard + * DeviceIdentifier holds instrument information as specified in the SCPI-99 standard. * - *

    - * Includes the device vendor, model, serial number and firmware version + *

    Includes the following properties: + *

      + *
    • Device vendor + *
    • Model + *
    • Serial number + *
    • Firmware version + *
    */ public final class DeviceIdentifier { - public final static DeviceIdentifier UNKNOWN = new DeviceIdentifier("-", "-", "-", "-"); + public static final DeviceIdentifier UNKNOWN = new DeviceIdentifier("-", "-", "-", "-"); - private final String manufacturer; - private final String model; - private final String serialNumber; - private final String firmwareVersion; + private final String manufacturer; + private final String model; + private final String serialNumber; + private final String firmwareVersion; - /** - * @param manufacturer - * @param model - * @param serialNumber - * @param firmwareVersion - */ - private DeviceIdentifier(String manufacturer, String model, String serialNumber, String firmwareVersion) { - this.manufacturer = manufacturer; - this.model = model; - this.serialNumber = serialNumber; - this.firmwareVersion = firmwareVersion; - } + private DeviceIdentifier( + String manufacturer, + String model, + String serialNumber, + String firmwareVersion) { + this.manufacturer = manufacturer; + this.model = model; + this.serialNumber = serialNumber; + this.firmwareVersion = firmwareVersion; + } - /** - * Constructs an Identifier from the format seen in the LXI Discovery and Identification specification - * - * @param raw Raw string in the - * @return Device identifier object - */ - public static DeviceIdentifier from(String raw) { - String[] info = raw.trim().split(","); - if(info.length != 4) { - throw new IllegalArgumentException("bad identifier: " + raw + " must be 4 comma-separated fields"); - } - return new DeviceIdentifier(info[0], info[1], info[2], info[3]); - } + /** + * Constructs an Identifier from the format seen in the LXI Discovery + * and Identification specification. + * + * @param raw Raw string in the + * @return Device identifier object + */ + public static DeviceIdentifier from(String raw) { + String[] info = raw.trim().split(","); + if (info.length != 4) { + throw new IllegalArgumentException( + "Bad device identifier: \'" + raw + "\' must be 4 comma-separated fields"); + } + return new DeviceIdentifier(info[0], info[1], info[2], info[3]); + } - /** - * Constructs an Identifier from the fields found in LXI Discovery and Identification specification - * - * @param manufacturer Manufacturer of the device - * @param model Model number - * @param serialNumber Serial number - * @param firmwareVersion Firmware version - * @return Device identifier object - */ - public static DeviceIdentifier from(String manufacturer, String model, String serialNumber, String firmwareVersion) { - return new DeviceIdentifier(manufacturer, model, serialNumber, firmwareVersion); - } + /** + * Constructs an Identifier from the fields found in LXI Discovery + * and Identification specification. + * + * @param manufacturer Manufacturer of the device + * @param model Model number + * @param serialNumber Serial number + * @param firmwareVersion Firmware version + * @return Device identifier object + */ + public static DeviceIdentifier from( + String manufacturer, + String model, + String serialNumber, + String firmwareVersion) { + return new DeviceIdentifier(manufacturer, model, serialNumber, firmwareVersion); + } - public String getManufacturer() { - return manufacturer; - } + public String getManufacturer() { + return manufacturer; + } - public String getModel() { - return model; - } + public String getModel() { + return model; + } - public String getSerialNumber() { - return serialNumber; - } + public String getSerialNumber() { + return serialNumber; + } - public String getFirmwareVersion() { - return firmwareVersion; - } + public String getFirmwareVersion() { + return firmwareVersion; + } - /** - * Returns the string representation of this identifier - * @return Raw string identifier - */ - public String value() { - return manufacturer + "," + model + "," + serialNumber + "," + firmwareVersion; - } + /** + * Returns the string representation of this identifier. + * @return Raw string identifier + */ + public String value() { + return manufacturer + "," + model + "," + serialNumber + "," + firmwareVersion; + } - /** - * Returns a string representing this identifier in a debug friendly way - * @return Debug string - */ - @Override - public String toString() { - //TODO: rework format - return "[mf=" + manufacturer + ";model=" + model + ";sn=" + serialNumber + ";ver=" + firmwareVersion + "]"; - } -} \ No newline at end of file + /** + * Returns a string representing this identifier in a debug friendly way. + * @return Debug string + */ + @Override + public String toString() { + //TODO: rework format + return "[mf=" + manufacturer + ";model=" + model + + ";sn=" + serialNumber + ";ver=" + firmwareVersion + "]"; + } +} diff --git a/measure-core/src/main/java/org/jtmc/core/visa/VISAResourceManager.java b/measure-core/src/main/java/org/jtmc/core/visa/VISAResourceManager.java index 818b7a5..fb9b1cc 100644 --- a/measure-core/src/main/java/org/jtmc/core/visa/VISAResourceManager.java +++ b/measure-core/src/main/java/org/jtmc/core/visa/VISAResourceManager.java @@ -6,68 +6,87 @@ import java.util.LinkedList; import java.util.List; import java.util.NoSuchElementException; - import org.jtmc.core.device.ISocket; import org.jtmc.core.visa.exception.UnsupportedSocketException; import org.jtmc.core.visa.factory.ISocketFactory; + /** - * VISAResourceManager + * VISAResourceManager can be used to connect instruments. */ public class VISAResourceManager implements AutoCloseable { - private ISocketFactory socketFactory; + private ISocketFactory socketFactory; - private List> factories; + private List> factories; - private transient List sockets; + private transient List sockets; - public VISAResourceManager(final ISocketFactory socketFactory, List> factories) { - this.socketFactory = socketFactory; - this.factories = new LinkedList<>(factories); - this.sockets = new LinkedList<>(); - } + /** + * Creates a new VISAResourceManager with the given socket factory and device factories. + * @param socketFactory Factory for creating sockets + * @param factories Device factories + */ + public VISAResourceManager( + final ISocketFactory socketFactory, + List> factories) { + this.socketFactory = socketFactory; + this.factories = new LinkedList<>(factories); + this.sockets = new LinkedList<>(); + } - public ISocket connect(String resourceString) throws IOException, VisaException { - return socketFactory.create(resourceString); - } + public ISocket connect(String resourceString) throws IOException, VisaException { + return socketFactory.create(resourceString); + } - public T connect(String resourceString, Class klass) throws IOException, VisaException { - ISocket socket = this.connect(resourceString); + /** + * Connects a new instrument using the given resource string and casting it to the + * required type. + * + * @param Preferred return type + * @param resourceString VISA Resource string + * @param klass Class of the return type + * @return Device driver + * @throws IOException if there was an error connecting to the device + * @throws VisaException if there was an error creating a driver for the device + */ + public T connect(String resourceString, Class klass) throws IOException, VisaException { + ISocket socket = this.connect(resourceString); - ArrayList candidates = new ArrayList<>(factories.size()); - candidates.add(socket); - - for(VisaDeviceFactory factory : factories) { - try { - if(factory.supports(socket)) { - Object dev = factory.create(socket); - candidates.add(dev); - } - } catch(UnsupportedSocketException e) { - e.printStackTrace(); - } - } - - try { - return findCandidate(candidates, klass); - } catch(NoSuchElementException e) { - throw new VisaException("No candidate found for type " + klass); - } - } + ArrayList candidates = new ArrayList<>(factories.size()); + candidates.add(socket); + + for (VisaDeviceFactory factory : factories) { + try { + if (factory.supports(socket)) { + Object dev = factory.create(socket); + candidates.add(dev); + } + } catch (UnsupportedSocketException e) { + e.printStackTrace(); + } + } + + try { + return findCandidate(candidates, klass); + } catch (NoSuchElementException e) { + throw new VisaException("No candidate found for type " + klass); + } + } - private static T findCandidate(Collection candidates, Class klass) throws NoSuchElementException { - for(Object obj : candidates) { - if(klass.isAssignableFrom(obj.getClass())) { - //TODO: handle ambiguity, prefer equal type - return klass.cast(obj); - } - } - throw new NoSuchElementException(); - } + private static T findCandidate(Collection candidates, Class klass) + throws NoSuchElementException { + for (Object obj : candidates) { + if (klass.isAssignableFrom(obj.getClass())) { + //TODO: handle ambiguity, prefer equal type + return klass.cast(obj); + } + } + throw new NoSuchElementException(); + } - @Override - public void close() { - this.sockets.forEach(ISocket::close); - } + @Override + public void close() { + this.sockets.forEach(ISocket::close); + } -} \ No newline at end of file +} diff --git a/measure-core/src/main/java/org/jtmc/core/visa/VisaDeviceFactory.java b/measure-core/src/main/java/org/jtmc/core/visa/VisaDeviceFactory.java index 16498e5..dafc38e 100644 --- a/measure-core/src/main/java/org/jtmc/core/visa/VisaDeviceFactory.java +++ b/measure-core/src/main/java/org/jtmc/core/visa/VisaDeviceFactory.java @@ -4,28 +4,28 @@ import org.jtmc.core.visa.exception.UnsupportedSocketException; /** - * VisaDeviceFactory + * VisaDeviceFactory is used for creating drivers for sockets. */ public interface VisaDeviceFactory { - /** - * Indicates that this factory is capable of wrapping the given Socket in a driver - * @param socket Socket - * @return {@code true} when the factory may be able to create some driver using the given Socket - */ - boolean supports(ISocket socket); + /** + * Indicates that this factory is capable of wrapping the given Socket in a driver. + * @param socket Socket + * @return {@code true} when the factory may be able to create some driver using the given Socket + */ + boolean supports(ISocket socket); - /** - * Creates and returns a driver object that wraps the given - * - *

    Implementations can assume that create is only called after verifying the return - * value of support - * - * @param socket Socket - * @return Driver - * @throws UnsupportedSocketException if attempting to create the driver failed because - * the socket turned out to be unsuitable - * @throws VisaException if there was an error creating the driver - */ - T create(ISocket socket) throws UnsupportedSocketException, VisaException; -} \ No newline at end of file + /** + * Creates and returns a driver object that wraps the given. + * + *

    Implementations can assume that create is only called after verifying the return + * value of support + * + * @param socket Socket + * @return Driver + * @throws UnsupportedSocketException if attempting to create the driver failed because + * the socket turned out to be unsuitable + * @throws VisaException if there was an error creating the driver + */ + T create(ISocket socket) throws UnsupportedSocketException, VisaException; +} diff --git a/measure-core/src/main/java/org/jtmc/core/visa/VisaException.java b/measure-core/src/main/java/org/jtmc/core/visa/VisaException.java index f23addb..e9d5acd 100644 --- a/measure-core/src/main/java/org/jtmc/core/visa/VisaException.java +++ b/measure-core/src/main/java/org/jtmc/core/visa/VisaException.java @@ -1,29 +1,26 @@ package org.jtmc.core.visa; /** - * VisaException + * VisaException is thrown when there's an error connecting to a device + * using the VISA Resource Manager. */ public class VisaException extends Exception { - private static final long serialVersionUID = 4917695447853517964L; + private static final long serialVersionUID = 4917695447853517964L; - public VisaException() { - } + public VisaException() { + } - public VisaException(String message) { - super(message); - } + public VisaException(String message) { + super(message); + } - public VisaException(Throwable cause) { - super(cause); - } + public VisaException(Throwable cause) { + super(cause); + } - public VisaException(String message, Throwable cause) { - super(message, cause); - } + public VisaException(String message, Throwable cause) { + super(message, cause); + } - public VisaException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { - super(message, cause, enableSuppression, writableStackTrace); - } - -} \ No newline at end of file +} diff --git a/measure-core/src/main/java/org/jtmc/core/visa/exception/InstrumentException.java b/measure-core/src/main/java/org/jtmc/core/visa/exception/InstrumentException.java index 54e39db..944d356 100644 --- a/measure-core/src/main/java/org/jtmc/core/visa/exception/InstrumentException.java +++ b/measure-core/src/main/java/org/jtmc/core/visa/exception/InstrumentException.java @@ -1,26 +1,25 @@ package org.jtmc.core.visa.exception; +/** + * InstrumentException is thrown when there's an error in controlling the instrument. + */ public class InstrumentException extends Exception { - private static final long serialVersionUID = 3954823646769129524L; + private static final long serialVersionUID = 3954823646769129524L; - public InstrumentException() { - } + public InstrumentException() { + } - public InstrumentException(String message) { - super(message); - } + public InstrumentException(String message) { + super(message); + } - public InstrumentException(Throwable cause) { - super(cause); - } + public InstrumentException(Throwable cause) { + super(cause); + } - public InstrumentException(String message, Throwable cause) { - super(message, cause); - } + public InstrumentException(String message, Throwable cause) { + super(message, cause); + } - public InstrumentException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { - super(message, cause, enableSuppression, writableStackTrace); - } - -} \ No newline at end of file +} diff --git a/measure-core/src/main/java/org/jtmc/core/visa/exception/UnsupportedDeviceException.java b/measure-core/src/main/java/org/jtmc/core/visa/exception/UnsupportedDeviceException.java index eb93bc2..cf24c17 100644 --- a/measure-core/src/main/java/org/jtmc/core/visa/exception/UnsupportedDeviceException.java +++ b/measure-core/src/main/java/org/jtmc/core/visa/exception/UnsupportedDeviceException.java @@ -1,25 +1,26 @@ package org.jtmc.core.visa.exception; /** - * UnsupportedDeviceException + * UnsupportedDeviceException is thrown when the device factory is unable to + * find a suitable driver for the given socket. */ public class UnsupportedDeviceException extends Exception { - private static final long serialVersionUID = -7900104889511905613L; + private static final long serialVersionUID = -7900104889511905613L; - public UnsupportedDeviceException() { - } + public UnsupportedDeviceException() { + } - public UnsupportedDeviceException(String message) { - super(message); - } + public UnsupportedDeviceException(String message) { + super(message); + } - public UnsupportedDeviceException(Throwable cause) { - super(cause); - } + public UnsupportedDeviceException(Throwable cause) { + super(cause); + } - public UnsupportedDeviceException(String message, Throwable cause) { - super(message, cause); - } + public UnsupportedDeviceException(String message, Throwable cause) { + super(message, cause); + } -} \ No newline at end of file +} diff --git a/measure-core/src/main/java/org/jtmc/core/visa/exception/UnsupportedSocketException.java b/measure-core/src/main/java/org/jtmc/core/visa/exception/UnsupportedSocketException.java index 81aa15b..161acb4 100644 --- a/measure-core/src/main/java/org/jtmc/core/visa/exception/UnsupportedSocketException.java +++ b/measure-core/src/main/java/org/jtmc/core/visa/exception/UnsupportedSocketException.java @@ -3,25 +3,26 @@ import org.jtmc.core.visa.VisaException; /** - * UnsupportedSocketException + * UnsupportedSocketException is thrown when the socket factory doesn't support + * the creation of the socket given a resource string. */ public class UnsupportedSocketException extends VisaException { - private static final long serialVersionUID = -7900104889511905613L; + private static final long serialVersionUID = -7900104889511905613L; - public UnsupportedSocketException() { - } + public UnsupportedSocketException() { + } - public UnsupportedSocketException(String message) { - super(message); - } + public UnsupportedSocketException(String message) { + super(message); + } - public UnsupportedSocketException(Throwable cause) { - super(cause); - } + public UnsupportedSocketException(Throwable cause) { + super(cause); + } - public UnsupportedSocketException(String message, Throwable cause) { - super(message, cause); - } + public UnsupportedSocketException(String message, Throwable cause) { + super(message, cause); + } -} \ No newline at end of file +} diff --git a/measure-core/src/main/java/org/jtmc/core/visa/factory/ISocketFactory.java b/measure-core/src/main/java/org/jtmc/core/visa/factory/ISocketFactory.java index a86d301..deb4336 100644 --- a/measure-core/src/main/java/org/jtmc/core/visa/factory/ISocketFactory.java +++ b/measure-core/src/main/java/org/jtmc/core/visa/factory/ISocketFactory.java @@ -1,34 +1,33 @@ package org.jtmc.core.visa.factory; import java.io.IOException; - import org.jtmc.core.device.ISocket; import org.jtmc.core.visa.exception.UnsupportedSocketException; /** * SocketFactory is a factory capable of creating Sockets using a VISA resource string * - *

    - * The factory may have internal default configurations, such as baud rates for serial implementations. - * It's recommended to have a no-args constructor with these defaults, setters for certain parameters and an all-args constructor. + *

    The factory may have internal default configurations, such as baud rates + * for serial implementations. It's recommended to have a no-args constructor + * with these defaults, setters for certain parameters and an all-args constructor. */ public interface ISocketFactory { - /** - * Returns whether this factory is capable of creating a socket given the resource string - * - * @param resourceString VISA Resource string - * @return {@code true} if the factory can create a socket from the resource string - */ - boolean supports(String resourceString); + /** + * Returns whether this factory is capable of creating a socket given the resource string. + * + * @param resourceString VISA Resource string + * @return {@code true} if the factory can create a socket from the resource string + */ + boolean supports(String resourceString); - /** - * Creates a socket using the given resource string - * - * @param resourceString VISA Resource string - * @return Socket - * @throws IOException if the creation failed because of a communication error - * @throws UnsupportedSocketException if the socket creation failed because of a bad resource string - */ - ISocket create(String resourceString) throws IOException, UnsupportedSocketException; -} \ No newline at end of file + /** + * Creates a socket using the given resource string. + * + * @param resourceString VISA Resource string + * @return Socket + * @throws IOException if the creation failed because of a communication error + * @throws UnsupportedSocketException if the factory is unable to create the given socket + */ + ISocket create(String resourceString) throws IOException, UnsupportedSocketException; +} diff --git a/measure-core/src/main/java/org/jtmc/core/visa/factory/SocketFactory.java b/measure-core/src/main/java/org/jtmc/core/visa/factory/SocketFactory.java index 19b7ef0..a22d132 100644 --- a/measure-core/src/main/java/org/jtmc/core/visa/factory/SocketFactory.java +++ b/measure-core/src/main/java/org/jtmc/core/visa/factory/SocketFactory.java @@ -4,47 +4,41 @@ import java.util.Arrays; import java.util.LinkedList; import java.util.List; - import org.jtmc.core.device.ISocket; import org.jtmc.core.visa.exception.UnsupportedSocketException; /** - * SocketFactory is a composite ISocketFactory + * SocketFactory is a composite ISocketFactory. */ public class SocketFactory implements ISocketFactory { - private List factories; - - /** - * Constructs a - * @param factories - */ - public SocketFactory(List factories) { - this.factories = new LinkedList<>(factories); - } - - public SocketFactory(ISocketFactory... factories) { - this(Arrays.asList(factories)); - } - - @Override - public boolean supports(String resourceURI) { - return factories.stream().anyMatch(factory -> factory.supports(resourceURI)); - } - - @Override - public ISocket create(String resourceString) throws IOException, UnsupportedSocketException { - for(ISocketFactory factory: factories) { - if(factory.supports(resourceString)) { - try { - return factory.create(resourceString); - } catch(UnsupportedSocketException e) { - //TODO log warning - } - } + private List factories; + + public SocketFactory(List factories) { + this.factories = new LinkedList<>(factories); + } + + public SocketFactory(ISocketFactory... factories) { + this(Arrays.asList(factories)); + } + + @Override + public boolean supports(String resource) { + return factories.stream().anyMatch(factory -> factory.supports(resource)); + } + + @Override + public ISocket create(String resourceString) throws IOException, UnsupportedSocketException { + for (ISocketFactory factory : factories) { + if (factory.supports(resourceString)) { + try { + return factory.create(resourceString); + } catch (UnsupportedSocketException e) { + //TODO log warning } - throw new UnsupportedSocketException(); + } } + throw new UnsupportedSocketException(); + } - -} \ No newline at end of file +} diff --git a/measure-core/src/main/java/org/jtmc/core/visa/instrument/InstrumentDiscovery.java b/measure-core/src/main/java/org/jtmc/core/visa/instrument/InstrumentDiscovery.java index c154819..bf938e8 100644 --- a/measure-core/src/main/java/org/jtmc/core/visa/instrument/InstrumentDiscovery.java +++ b/measure-core/src/main/java/org/jtmc/core/visa/instrument/InstrumentDiscovery.java @@ -1,20 +1,20 @@ package org.jtmc.core.visa.instrument; import java.util.Set; - import org.jtmc.core.visa.VisaException; /** * InstrumentDiscovery can be used to discover instruments accessible - * to the test controller + * to the test controller. */ public interface InstrumentDiscovery { - /** - * Discovers instruments and returns a set of connectable endpoints - * - * @return Instrument Endpoints - * @throws VisaException when an error occured while discovering instruments - */ - Set discover() throws VisaException; + /** + * Discovers instruments and returns a set of connectable endpoints. + * + * @return Instrument Endpoints + * @throws VisaException when an error occured while discovering instruments + */ + Set discover() throws VisaException; + } diff --git a/measure-core/src/main/java/org/jtmc/core/visa/instrument/InstrumentEndpoint.java b/measure-core/src/main/java/org/jtmc/core/visa/instrument/InstrumentEndpoint.java index 899c334..9170718 100644 --- a/measure-core/src/main/java/org/jtmc/core/visa/instrument/InstrumentEndpoint.java +++ b/measure-core/src/main/java/org/jtmc/core/visa/instrument/InstrumentEndpoint.java @@ -1,18 +1,18 @@ package org.jtmc.core.visa.instrument; import java.io.IOException; - import org.jtmc.core.device.ISocket; /** - * Instrument Endpoints allow an instrument to be connected + * Instrument Endpoints allow an instrument to be connected. */ public interface InstrumentEndpoint { - /** - * Returns the socket connecting to the instrument - * @return Socket - * @throws IOException if there was an error connecting the instrument - */ - ISocket connect() throws IOException; + /** + * Returns the socket connecting to the instrument. + * @return Socket + * @throws IOException if there was an error connecting the instrument + */ + ISocket connect() throws IOException; + } diff --git a/measure-core/src/main/java/org/jtmc/core/visa/mock/MockSocket.java b/measure-core/src/main/java/org/jtmc/core/visa/mock/MockSocket.java index dd46b72..c0b6d53 100644 --- a/measure-core/src/main/java/org/jtmc/core/visa/mock/MockSocket.java +++ b/measure-core/src/main/java/org/jtmc/core/visa/mock/MockSocket.java @@ -3,74 +3,79 @@ import java.io.IOException; import java.net.SocketTimeoutException; import java.nio.ByteBuffer; - import org.jtmc.core.device.ISocket; /** - * VirtualSocket + * MockSocket can be used to emulate a connection to an instrument. */ public class MockSocket implements ISocket { - private boolean connected; - - private String className; - - private String instrumentName; - - public MockSocket(String className) { - this(className, null); + private boolean connected; + + private String className; + + private String instrumentName; + + public MockSocket(String className) { + this(className, null); + } + + /** + * Creates a new MockSocket with the given classname and instrument name. + * + * @param className Class name + * @param instrumentName Instrument name + */ + public MockSocket(String className, String instrumentName) { + this.className = className; + this.instrumentName = instrumentName; + this.connected = true; + } + + @Override + public void close() { + this.connected = false; + } + + @Override + public boolean isConnected() { + return this.connected; + } + + @Override + public String getResourceString() { + return "MOCK::" + className + (instrumentName != null ? "::" + instrumentName : "") + "::INSTR"; + } + + public String getClassName() { + return className; + } + + public String getInstrumentName() { + return instrumentName; + } + + @Override + public void send(ByteBuffer message) throws IOException { + if (!this.connected) { + throw new IOException("MockSocket not connected"); } + } - public MockSocket(String className, String instrumentName) { - this.className = className; - this.instrumentName = instrumentName; - this.connected = true; + @Override + public ByteBuffer receive(int count, long timeout) throws IOException, SocketTimeoutException { + if (!this.connected) { + throw new IOException("MockSocket not connected"); } - - @Override - public void close() { - this.connected = false; - } - - @Override - public boolean isConnected() { - return this.connected; - } - - @Override - public String getResourceString() { - return "MOCK::" + className + (instrumentName != null ? "::" + instrumentName: "") + "::INSTR"; - } - - public String getClassName() { - return className; - } - - public String getInstrumentName() { - return instrumentName; - } - - @Override - public void send(ByteBuffer message) throws IOException { - if(!this.connected) { - throw new IOException("MockSocket not connected"); - } - } - - @Override - public ByteBuffer receive(int count, long timeout) throws IOException, SocketTimeoutException { - if(!this.connected) { - throw new IOException("MockSocket not connected"); - } - return ByteBuffer.allocate(0); - } - - @Override - public ByteBuffer receive(char delimiter, long timeout) throws IOException, SocketTimeoutException { - if(!this.connected) { - throw new IOException("MockSocket not connected"); - } - return ByteBuffer.allocate(0); + return ByteBuffer.allocate(0); + } + + @Override + public ByteBuffer receive(char delimiter, long timeout) + throws IOException, SocketTimeoutException { + if (!this.connected) { + throw new IOException("MockSocket not connected"); } - -} \ No newline at end of file + return ByteBuffer.allocate(0); + } +} diff --git a/measure-core/src/main/java/org/jtmc/core/visa/mock/MockSocketFactory.java b/measure-core/src/main/java/org/jtmc/core/visa/mock/MockSocketFactory.java index 069e5a0..639ad24 100644 --- a/measure-core/src/main/java/org/jtmc/core/visa/mock/MockSocketFactory.java +++ b/measure-core/src/main/java/org/jtmc/core/visa/mock/MockSocketFactory.java @@ -2,31 +2,31 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; - import org.jtmc.core.visa.factory.ISocketFactory; /** - * MockSocketFactory + * MockSocketFactory can be used to create MockSockets using VISA resource strings. */ public class MockSocketFactory implements ISocketFactory { - private final static Pattern pattern = Pattern.compile("MOCK::(?[^:]*)(::(?!INSTR)(?[^:]*))?(::INSTR)?"); - - @Override - public boolean supports(String resourceString) { - return pattern.matcher(resourceString).matches(); - } + private static final Pattern pattern = Pattern.compile( + "MOCK::(?[^:]*)(::(?!INSTR)(?[^:]*))?(::INSTR)?"); + + @Override + public boolean supports(String resourceString) { + return pattern.matcher(resourceString).matches(); + } - @Override - public MockSocket create(String resourceString) { - Matcher matcher = pattern.matcher(resourceString); - if(matcher.matches()) { - String className = matcher.group("class"); - String instrumentName = matcher.group("name"); + @Override + public MockSocket create(String resourceString) { + Matcher matcher = pattern.matcher(resourceString); + if (matcher.matches()) { + String className = matcher.group("class"); + String instrumentName = matcher.group("name"); - return new MockSocket(className, instrumentName); - } - throw new IllegalArgumentException(); - } + return new MockSocket(className, instrumentName); + } + throw new IllegalArgumentException(); + } -} \ No newline at end of file +} diff --git a/measure-core/src/test/java/org/jtmc/core/instrument/InstrumentInterfaceTests.java b/measure-core/src/test/java/org/jtmc/core/instrument/InstrumentInterfaceTests.java new file mode 100644 index 0000000..45607bb --- /dev/null +++ b/measure-core/src/test/java/org/jtmc/core/instrument/InstrumentInterfaceTests.java @@ -0,0 +1,24 @@ +package org.jtmc.core.instrument; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.BlockJUnit4ClassRunner; + +@RunWith(BlockJUnit4ClassRunner.class) +public class InstrumentInterfaceTests { + + @Test + public void testDCPowerSupply() { + TestDCPowerSupply psu = new TestDCPowerSupply(); + assertEquals(psu.getPowerOutputs().size(), 2); + + psu.getPowerOutput(0); + psu.getPowerOutput(1); + + psu.getPowerOutput("1"); + psu.getPowerOutput("2"); + } +} diff --git a/measure-core/src/test/java/org/jtmc/core/instrument/TestDCPowerSupply.java b/measure-core/src/test/java/org/jtmc/core/instrument/TestDCPowerSupply.java new file mode 100644 index 0000000..a088dcc --- /dev/null +++ b/measure-core/src/test/java/org/jtmc/core/instrument/TestDCPowerSupply.java @@ -0,0 +1,72 @@ +package org.jtmc.core.instrument; + +import java.util.Collection; +import java.util.LinkedList; +import java.util.List; + +public class TestDCPowerSupply implements DCPowerSupply { + + private List outputs; + + public TestDCPowerSupply() { + this.outputs = new LinkedList<>(); + this.outputs.add(new DummyOutputImpl(0)); + this.outputs.add(new DummyOutputImpl(1)); + } + + @Override + public Collection getPowerOutputs() { + return this.outputs; + } + + public static class DummyOutputImpl implements DCPowerSupply.PowerOutput { + + private int index; + + public DummyOutputImpl(int index) { + this.index = index; + } + + @Override + public String getName() { + return String.valueOf(index + 1); + } + + @Override + public void setEnabled(boolean enabled) { + + } + + @Override + public void setMaximumVoltage(double voltage) { + + } + + @Override + public double getMaximumVoltage() { + return 0; + } + + @Override + public void setMaximumCurrent(double current) { + + } + + @Override + public double getMaximumCurrent() { + return 0; + } + + @Override + public double getVoltage() { + return 0; + } + + @Override + public double getCurrent() { + return 0; + } + + } + +} diff --git a/measure-core/src/test/java/org/jtmc/core/serial/SerialTest.java b/measure-core/src/test/java/org/jtmc/core/serial/SerialTest.java new file mode 100644 index 0000000..e9adc18 --- /dev/null +++ b/measure-core/src/test/java/org/jtmc/core/serial/SerialTest.java @@ -0,0 +1,25 @@ +package org.jtmc.core.serial; + +import static org.junit.Assert.assertEquals; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.BlockJUnit4ClassRunner; + +@RunWith(BlockJUnit4ClassRunner.class) +public class SerialTest { + + @Test + public void testParityValueOfChar() { + assertEquals(Parity.NONE, Parity.valueOf('N')); + assertEquals(Parity.ODD, Parity.valueOf('O')); + assertEquals(Parity.EVEN, Parity.valueOf('E')); + assertEquals(Parity.MARK, Parity.valueOf('M')); + assertEquals(Parity.SPACE, Parity.valueOf('S')); + } + + @Test(expected = IllegalArgumentException.class) + public void testParityValueOfCharInvalid() { + Parity.valueOf('A'); + } +} diff --git a/measure-driver-siglent/src/main/java/org/jtmc/siglent/driver/SDGDriver.java b/measure-driver-siglent/src/main/java/org/jtmc/siglent/driver/SDGDriver.java index bbd376c..33f5c1e 100644 --- a/measure-driver-siglent/src/main/java/org/jtmc/siglent/driver/SDGDriver.java +++ b/measure-driver-siglent/src/main/java/org/jtmc/siglent/driver/SDGDriver.java @@ -2,7 +2,6 @@ import java.io.IOException; import java.util.Collection; - import org.jtmc.core.instrument.FunctionGenerator; import org.jtmc.core.instrument.common.Impedance; import org.jtmc.core.scpi.ISCPISocket; @@ -12,160 +11,179 @@ import org.jtmc.core.visa.exception.InstrumentException; import org.jtmc.siglent.info.SiglentFunctionGeneratorModel; +/** + * Siglent SDG Series function generator driver. + */ public class SDGDriver extends SCPISocketAdapter implements FunctionGenerator { - public SDGDriver(ISCPISocket adapter, DeviceIdentifier deviceIdentifier, SiglentFunctionGeneratorModel fGeneratorModel) { - super(adapter, deviceIdentifier); - // TODO Auto-generated constructor stub - } - - @Override - public Collection getAnalogOutputs() { - // TODO Auto-generated method stub - return null; - } - - private static class AnalogOutputImpl implements FunctionGenerator.AnalogOutput { - - private ISCPISocket socket; - - private int channelId; - - private long memoryDepth; - - private int verticalResolution; - - public AnalogOutputImpl(ISCPISocket socket, int channelId, long memoryDepth, int verticalResolution) { - this.socket = socket; - this.channelId = channelId; - this.memoryDepth = memoryDepth; - this.verticalResolution = verticalResolution; - } - - @Override - public String getName() { - return String.valueOf(this.channelId); - } - - @Override - public void setEnabled(boolean enabled) throws InstrumentException { - try { - this.socket.send(channelCommand("OUTP").with(enabled ? "ON": "OFF").build()); - } catch(IOException e) { - throw new InstrumentException(e); - } - } - - @Override - public void setOperationMode(OperationMode operationMode) throws InstrumentException { - // TODO Auto-generated method stub - - } - - @Override - public void setImpedance(double impedance) throws InstrumentException { - try { - //TODO: validate impedance value (50-10000 Ohms or HiZ) - this.socket.send(channelCommand("OUTP").with("LOAD", impedance == Impedance.HIZ ? "HZ": String.valueOf(impedance)).build()); - } catch(IOException e) { - throw new InstrumentException(e); - } - } - - @Override - public void setAmplitude(double amplitude) throws InstrumentException { - try { - this.socket.send(channelCommand("OUTP").with("AMP", String.valueOf(amplitude)).build()); - } catch(IOException e) { - throw new InstrumentException(e); - } - } - - @Override - public void setOffset(double offset) throws InstrumentException { - try { - this.socket.send(channelCommand("OUTP").with("OFST", String.valueOf(offset)).build()); - } catch(IOException e) { - throw new InstrumentException(e); - } - } - - @Override - public void setFrequency(double frequency) throws InstrumentException { - try { - this.socket.send(channelCommand("OUTP").with("FREQ", String.valueOf(frequency)).build()); - } catch(IOException e) { - throw new InstrumentException(e); - } - } - - @Override - public void setPhase(double phase) throws InstrumentException { - try { - //TODO: validate Phase 0- 360 - this.socket.send(channelCommand("OUTP").with("PHSE", String.valueOf(phase)).build()); - } catch(IOException e) { - throw new InstrumentException(e); - } - } - - @Override - public void setWaveformType(WaveformType waveformType) throws InstrumentException{ - try { - //TODO: RampDown = Triangle, 100% Sym; RampUp = Triangle, 0% Sym; - this.socket.send(channelCommand("OUTP").with("WVTP", String.valueOf(waveformType.toString())).build()); - } catch(IOException e) { - throw new InstrumentException(e); - } - } - - private SCPICommand.Builder channelCommand(String command) { - return SCPICommand.builder().command("C" + channelId, command); - } - - } - - /* - @Override - public synchronized void setAnalogArbitraryWaveform(int channel, Signal signal) throws IOException { - validateChannelNumber(channel); - if (signal.getData().size() != WAVE_LENGTH) { - signal = sampler.sample(signal); - } - - SCPICommand arbMode = channelCommand(channel, "BSWV").with("WVTP", "ARB").build(); - this.send(arbMode); - this.waitForOperation(DEFAULT_TIMEOUT); - - SCPICommand srate = channelCommand(channel, "SRATE").with("MODE", "TARB").build(); - this.send(srate); - this.waitForOperation(DEFAULT_TIMEOUT); - - SCPICommand wdata = channelCommand(channel, "WVDT").with("WVNM", signal.getId()).with("LENGTH", "32KB") - .with("FREQ", 1.0f / signal.period()).with("AMPL", signal.max() - signal.min()) - .with("OFST", (signal.max() + signal.min()) / 2.0f) - .with("WAVEDATA", new String(binary(signal).array(), StandardCharsets.ISO_8859_1)).build(); - this.send(wdata); - this.waitForOperation(DEFAULT_TIMEOUT); - - SCPICommand arbWave = channelCommand(channel, "ARWV").with("NAME", signal.getId()).build(); - this.send(arbWave); - this.waitForOperation(DEFAULT_TIMEOUT); - } - - private static ByteBuffer binary(Signal signal) { - ByteBuffer buffer = ByteBuffer.allocate(16384 * 2); - double min = signal.min(); - double max = signal.max(); - signal.getData().forEach(point -> { - double k = (point.value - min) * 2 / (max - min) - 1; - short value = (short) (k * 65535 / 2); - byte low = (byte) (value & 0xFF); - byte high = (byte) ((value & 0xFF00) >> 8); - buffer.put(low); - buffer.put(high); - }); - return buffer; - } - */ + public SDGDriver( + ISCPISocket adapter, + DeviceIdentifier deviceIdentifier, + SiglentFunctionGeneratorModel functionGeneratorModel) { + super(adapter, deviceIdentifier); + // TODO Auto-generated constructor stub + } + + @Override + public Collection getAnalogOutputs() { + // TODO Auto-generated method stub + return null; + } + + private static class AnalogOutputImpl implements FunctionGenerator.AnalogOutput { + + private ISCPISocket socket; + + private int channelId; + + private long memoryDepth; + + private int verticalResolution; + + public AnalogOutputImpl( + ISCPISocket socket, + int channelId, + long memoryDepth, + int verticalResolution) { + this.socket = socket; + this.channelId = channelId; + this.memoryDepth = memoryDepth; + this.verticalResolution = verticalResolution; + } + + @Override + public String getName() { + return String.valueOf(this.channelId); + } + + @Override + public void setEnabled(boolean enabled) throws InstrumentException { + try { + this.socket.send(channelCommand("OUTP").with(enabled ? "ON" : "OFF").build()); + } catch (IOException e) { + throw new InstrumentException(e); + } + } + + @Override + public void setOperationMode(OperationMode operationMode) throws InstrumentException { + // TODO Auto-generated method stub + + } + + @Override + public void setImpedance(double impedance) throws InstrumentException { + try { + //TODO: validate impedance value (50-10000 Ohms or HiZ) + this.socket.send( + channelCommand("OUTP") + .with("LOAD", impedance == Impedance.HIZ ? "HZ" : String.valueOf(impedance)) + .build()); + } catch (IOException e) { + throw new InstrumentException(e); + } + } + + @Override + public void setAmplitude(double amplitude) throws InstrumentException { + try { + this.socket.send(channelCommand("OUTP").with("AMP", String.valueOf(amplitude)).build()); + } catch (IOException e) { + throw new InstrumentException(e); + } + } + + @Override + public void setOffset(double offset) throws InstrumentException { + try { + this.socket.send(channelCommand("OUTP").with("OFST", String.valueOf(offset)).build()); + } catch (IOException e) { + throw new InstrumentException(e); + } + } + + @Override + public void setFrequency(double frequency) throws InstrumentException { + try { + this.socket.send(channelCommand("OUTP").with("FREQ", String.valueOf(frequency)).build()); + } catch (IOException e) { + throw new InstrumentException(e); + } + } + + @Override + public void setPhase(double phase) throws InstrumentException { + try { + //TODO: validate Phase 0- 360 + this.socket.send(channelCommand("OUTP").with("PHSE", String.valueOf(phase)).build()); + } catch (IOException e) { + throw new InstrumentException(e); + } + } + + @Override + public void setWaveformType(WaveformType waveformType) throws InstrumentException { + try { + //TODO: RampDown = Triangle, 100% Sym; RampUp = Triangle, 0% Sym; + this.socket.send( + channelCommand("OUTP") + .with("WVTP", String.valueOf(waveformType.toString())) + .build()); + } catch (IOException e) { + throw new InstrumentException(e); + } + } + + private SCPICommand.Builder channelCommand(String command) { + return SCPICommand.builder().command("C" + channelId, command); + } + + } + + /* + @Override + public synchronized void setAnalogArbitraryWaveform(int channel, Signal signal) + throws IOException { + validateChannelNumber(channel); + if (signal.getData().size() != WAVE_LENGTH) { + signal = sampler.sample(signal); + } + + SCPICommand arbMode = channelCommand(channel, "BSWV").with("WVTP", "ARB").build(); + this.send(arbMode); + this.waitForOperation(DEFAULT_TIMEOUT); + + SCPICommand srate = channelCommand(channel, "SRATE").with("MODE", "TARB").build(); + this.send(srate); + this.waitForOperation(DEFAULT_TIMEOUT); + + SCPICommand wdata = channelCommand(channel, "WVDT") + .with("WVNM", signal.getId()) + .with("LENGTH", "32KB") + .with("FREQ", 1.0f / signal.period()).with("AMPL", signal.max() - signal.min()) + .with("OFST", (signal.max() + signal.min()) / 2.0f) + .with("WAVEDATA", new String(binary(signal).array(), StandardCharsets.ISO_8859_1)).build(); + this.send(wdata); + this.waitForOperation(DEFAULT_TIMEOUT); + + SCPICommand arbWave = channelCommand(channel, "ARWV").with("NAME", signal.getId()).build(); + this.send(arbWave); + this.waitForOperation(DEFAULT_TIMEOUT); + } + + private static ByteBuffer binary(Signal signal) { + ByteBuffer buffer = ByteBuffer.allocate(16384 * 2); + double min = signal.min(); + double max = signal.max(); + signal.getData().forEach(point -> { + double k = (point.value - min) * 2 / (max - min) - 1; + short value = (short) (k * 65535 / 2); + byte low = (byte) (value & 0xFF); + byte high = (byte) ((value & 0xFF00) >> 8); + buffer.put(low); + buffer.put(high); + }); + return buffer; + } + */ } diff --git a/measure-driver-siglent/src/main/java/org/jtmc/siglent/driver/SDSE11ADriver.java b/measure-driver-siglent/src/main/java/org/jtmc/siglent/driver/SDSE11ADriver.java index 9518da1..5250fcc 100644 --- a/measure-driver-siglent/src/main/java/org/jtmc/siglent/driver/SDSE11ADriver.java +++ b/measure-driver-siglent/src/main/java/org/jtmc/siglent/driver/SDSE11ADriver.java @@ -5,11 +5,17 @@ import org.jtmc.core.visa.DeviceIdentifier; import org.jtmc.siglent.info.SiglentOscilloscopeModel; +/** + * Siglent Oscilloscope driver for the newer scopes using a standard SCPI driver. + */ public class SDSE11ADriver extends SCPISocketAdapter { - public SDSE11ADriver(ISCPISocket adapter, DeviceIdentifier deviceIdentifier, SiglentOscilloscopeModel scopeInfo) { - super(adapter, deviceIdentifier); - //TODO: implement - } - -} \ No newline at end of file + public SDSE11ADriver( + ISCPISocket adapter, + DeviceIdentifier deviceIdentifier, + SiglentOscilloscopeModel scopeInfo) { + super(adapter, deviceIdentifier); + //TODO: implement + } + +} diff --git a/measure-driver-siglent/src/main/java/org/jtmc/siglent/driver/SDSLegacyDriver.java b/measure-driver-siglent/src/main/java/org/jtmc/siglent/driver/SDSLegacyDriver.java index 7118103..0d60834 100644 --- a/measure-driver-siglent/src/main/java/org/jtmc/siglent/driver/SDSLegacyDriver.java +++ b/measure-driver-siglent/src/main/java/org/jtmc/siglent/driver/SDSLegacyDriver.java @@ -6,7 +6,6 @@ import java.util.LinkedList; import java.util.List; import java.util.Optional; - import org.jtmc.core.instrument.Oscilloscope; import org.jtmc.core.instrument.common.Coupling; import org.jtmc.core.scpi.ISCPISocket; @@ -19,252 +18,283 @@ import org.jtmc.siglent.info.SiglentOscilloscopeModel; /** - * Driver for the following Siglent Oscilloscopes: + * Driver for the Siglent Oscilloscopes. * - *

    + *

    Supported models are: + *

      + *
    • * SDS1000CFL - *

      + *

    • * SDS1000A - *

      + *

    • * SDS1000CML+/CNL+/DL+/E+/F+ - *

      + *

    • * SDS1000X - *

      + *

    • * SDS1000X+ - *

      + *

    • * SDS1000X-E/C - *

      + *

    • * SDS2000(X) + *
    */ public class SDSLegacyDriver extends SCPISocketAdapter implements Oscilloscope { - private final static int HORIZONTAL_DIVISIONS = 14; - - private final static int VERTICAL_DIVISIONS = 8; - - private final static double[] TIME_DIV_VALUES = new double[] { Units.nano(1), Units.nano(2), Units.nano(5), - Units.nano(10), Units.nano(20), Units.nano(50), Units.nano(100), Units.nano(200), Units.nano(500), - Units.micro(1), Units.micro(2), Units.micro(5), Units.micro(10), Units.micro(20), Units.micro(50), - Units.micro(100), Units.micro(200), Units.micro(500), Units.milli(1), Units.milli(2), Units.milli(5), - Units.milli(10), Units.milli(20), Units.milli(50), Units.milli(100), Units.milli(200), Units.milli(500), 1f, - 2f, 5f, 10f, 20f, 50f, 100f }; - - private AcquisitionSystemImpl acquisitionSystem; - - private List channels; - - public SDSLegacyDriver(ISCPISocket socket) throws IOException { - this(socket, socket.getDeviceIdentifier()); - } - - public SDSLegacyDriver(ISCPISocket socket, DeviceIdentifier deviceIdentifier) throws IOException { - this(socket, deviceIdentifier, SiglentOscilloscopeModel.create(deviceIdentifier.getModel())); - } - - public SDSLegacyDriver(ISCPISocket socket, DeviceIdentifier deviceIdentifier, SiglentOscilloscopeModel info) throws IOException { - super(socket, deviceIdentifier); - - // Sets the response to the Shortest format, meaning for queries it will only respond - // with the values - socket.send(new SCPICommand("CHDR", "OFF")); - - // Initiate channels and acquisition system - this.acquisitionSystem = new AcquisitionSystemImpl(socket, HORIZONTAL_DIVISIONS, TIME_DIV_VALUES); - this.channels = new LinkedList(); - - for(int i = 0; i < info.getChannels(); i++) { - this.channels.add(new ChannelImpl(socket, i + 1, VERTICAL_DIVISIONS)); - } - } - - @Override - public void setRunState(RunState state) throws InstrumentException { - try { - String mode = state.name(); - if (state == RunState.NORMAL) { - mode = "NORM"; - } - this.send(SCPICommand.builder().command("TRMD").with(mode).build()); - } catch (IOException e) { - throw new InstrumentException(e); - } - } - - @Override - public AcquisitionBaseSystem acquisition() { - return this.acquisitionSystem; - } - - @Override - public Collection getAnalogInputs() { - return this.channels; - } - - private static class ChannelImpl implements AnalogInput { - - private ISCPISocket socket; - - private int channel; - - private int verticalDivisions; - - public ChannelImpl(ISCPISocket socket, int channel, int verticalDivisions) { - this.socket = socket; - this.channel = channel; - this.verticalDivisions = verticalDivisions; - } - - private static SCPICommand.Builder channelCommand(int channel, String cmd) { - return SCPICommand.builder().command("C" + channel, cmd); - } - - @Override - public String getName() { - return String.valueOf(this.channel); - } - - @Override - public void setEnabled(boolean enabled) throws InstrumentException { - try { - socket.send(channelCommand(channel, "TRA").with(enabled ? "ON":"OFF").build()); - } catch(IOException e) { - throw new InstrumentException(e); - } - } - - @Override - public void setRange(double range) throws InstrumentException { - try { - double gain = range / VERTICAL_DIVISIONS; - socket.send(channelCommand(channel, "VDIV").with(Units.auto(gain, 1, "V")).build()); - } catch(IOException e) { - throw new InstrumentException(e); - } - } - - @Override - public void setOffset(double offset) throws InstrumentException { - try { - String ofst = Units.auto(offset, 3, "V").toUpperCase(); - socket.send(channelCommand(channel, "OFST").with(ofst).build()); - } catch(IOException e) { - throw new InstrumentException(e); - } - } - - @Override - public void setProbeAttenuation(double attenuation) throws InstrumentException { - try { - String attn = attenuation < 1 ? String.format("%.01f", attenuation) : String.format("%.00f", attenuation); - socket.send(channelCommand(channel, "ATTN").with(attn).build()); - } catch(IOException e) { - throw new InstrumentException(e); - } - } - - @Override - public void setProbeSense(boolean enable) throws InstrumentException { - //TODO: 2000X series has probe detection ring - throw new InstrumentException("Probe sense is not supported"); - } - - @Override - public void setImpedance(double impedance) throws InstrumentException { - //2 channel variants have 50 Ohm mode - throw new InstrumentException("No implemented"); - } - - @Override - public void setBandwidthLimit(double bandwidthLimit) throws InstrumentException { - double MHZ20 = Units.mega(20); - if(Math.abs(bandwidthLimit - MHZ20) <= 0.01) { - //Set bandwidth - } - throw new InstrumentException(""); - } - - @Override - public void setCoupling(Coupling coupling) { - // TODO Auto-generated method stub - - } - - @Override - public Optional getAnalogInputSignal(int channel) throws InstrumentException { - // TODO Auto-generated method stub - return null; - } - - } - - private static class AcquisitionSystemImpl implements AcquisitionBaseSystem { - - private ISCPISocket socket; - - private int horizontalDivisions; - - private final double[] timeDivisionValues; - - public AcquisitionSystemImpl(ISCPISocket socket, int horizontalDivisions, double[] timeDivisionValues) { - this.socket = socket; - this.horizontalDivisions = horizontalDivisions; - this.timeDivisionValues = timeDivisionValues; - } - - @Override - public void setTimespan(double span) throws InstrumentException { - try { - double secPerDiv = span / horizontalDivisions; - double targetDiv = Arrays.stream(TIME_DIV_VALUES).filter(tdiv -> Math.abs(secPerDiv - tdiv) <= Units.PICO || tdiv >= secPerDiv).findFirst().getAsDouble(); - String tdiv = Units.auto(targetDiv, 0, "s"); - - socket.send(SCPICommand.builder().command("TDIV").with(tdiv).build()); - } catch(IOException e) { - throw new InstrumentException(e); - } - } - - @Override - public void setSampleCount(long count) throws InstrumentException { - // TODO Auto-generated method stub - - } - - @Override - public long getSampleCount() throws InstrumentException { - // TODO Auto-generated method stub - return 0; - } - - @Override - public double getSampleRate() throws InstrumentException { - // TODO Auto-generated method stub - return 0; - } - - @Override - public void setTimeOffset(double offset) throws InstrumentException { - try { - String triggerDelay = Units.auto(-offset, 2, "s"); - - socket.send(SCPICommand.builder().command("TRDL").with(triggerDelay).build()); - } catch(IOException e) { - throw new InstrumentException(e); - } - } - - @Override - public void setMode(AcquisitionMode mode) throws InstrumentException { - // TODO Auto-generated method stub - - } - - @Override - public AcquisitionState getState() throws InstrumentException { - // TODO Auto-generated method stub - return null; - } - - } - -} \ No newline at end of file + private static final int HORIZONTAL_DIVISIONS = 14; + + private static final int VERTICAL_DIVISIONS = 8; + + private static final double[] TIME_DIV_VALUES = new double[] { + Units.nano(1), Units.nano(2), Units.nano(5), Units.nano(10), Units.nano(20), + Units.nano(50), Units.nano(100), Units.nano(200), Units.nano(500), + + Units.micro(1), Units.micro(2), Units.micro(5), Units.micro(10), Units.micro(20), + Units.micro(50), Units.micro(100), Units.micro(200), Units.micro(500), + + Units.milli(1), Units.milli(2), Units.milli(5), Units.milli(10), Units.milli(20), + Units.milli(50), Units.milli(100), Units.milli(200), Units.milli(500), 1f, + 2f, 5f, 10f, 20f, 50f, 100f }; + + private AcquisitionSystemImpl acquisitionSystem; + + private List channels; + + public SDSLegacyDriver(ISCPISocket socket) throws IOException { + this(socket, socket.getDeviceIdentifier()); + } + + public SDSLegacyDriver(ISCPISocket socket, DeviceIdentifier deviceIdentifier) throws IOException { + this(socket, deviceIdentifier, SiglentOscilloscopeModel.create(deviceIdentifier.getModel())); + } + + /** + * Creates a new driver object for Siglent oscilloscopes. + * + * @param socket SCPI Socket + * @param deviceIdentifier Device identifier + * @param info Siglent Oscilloscope model information + * @throws IOException if there was an error communicating with the target + */ + public SDSLegacyDriver( + ISCPISocket socket, + DeviceIdentifier deviceIdentifier, + SiglentOscilloscopeModel info) throws IOException { + super(socket, deviceIdentifier); + + // Sets the response to the Shortest format, meaning for queries it will only respond + // with the values + socket.send(new SCPICommand("CHDR", "OFF")); + + // Initiate channels and acquisition system + this.acquisitionSystem = new AcquisitionSystemImpl(socket, + HORIZONTAL_DIVISIONS, + TIME_DIV_VALUES); + this.channels = new LinkedList(); + + for (int i = 0; i < info.getChannels(); i++) { + this.channels.add(new ChannelImpl(socket, i + 1, VERTICAL_DIVISIONS)); + } + } + + @Override + public void setRunState(RunState state) throws InstrumentException { + try { + String mode = state.name(); + if (state == RunState.NORMAL) { + mode = "NORM"; + } + this.send(SCPICommand.builder().command("TRMD").with(mode).build()); + } catch (IOException e) { + throw new InstrumentException(e); + } + } + + @Override + public AcquisitionBaseSystem acquisition() { + return this.acquisitionSystem; + } + + @Override + public Collection getAnalogInputs() { + return this.channels; + } + + private static class ChannelImpl implements AnalogInput { + + private ISCPISocket socket; + + private int channel; + + private int verticalDivisions; + + public ChannelImpl(ISCPISocket socket, int channel, int verticalDivisions) { + this.socket = socket; + this.channel = channel; + this.verticalDivisions = verticalDivisions; + } + + private static SCPICommand.Builder channelCommand(int channel, String cmd) { + return SCPICommand.builder().command("C" + channel, cmd); + } + + @Override + public String getName() { + return String.valueOf(this.channel); + } + + @Override + public void setEnabled(boolean enabled) throws InstrumentException { + try { + socket.send(channelCommand(channel, "TRA").with(enabled ? "ON" : "OFF").build()); + } catch (IOException e) { + throw new InstrumentException(e); + } + } + + @Override + public void setRange(double range) throws InstrumentException { + try { + double gain = range / verticalDivisions; + socket.send(channelCommand(channel, "VDIV").with(Units.auto(gain, 1, "V")).build()); + } catch (IOException e) { + throw new InstrumentException(e); + } + } + + @Override + public void setOffset(double offset) throws InstrumentException { + try { + String ofst = Units.auto(offset, 3, "V").toUpperCase(); + socket.send(channelCommand(channel, "OFST").with(ofst).build()); + } catch (IOException e) { + throw new InstrumentException(e); + } + } + + @Override + public void setProbeAttenuation(double attenuation) throws InstrumentException { + try { + String attn = attenuation < 1 + ? String.format("%.01f", attenuation) : String.format("%.00f", attenuation); + socket.send(channelCommand(channel, "ATTN").with(attn).build()); + } catch (IOException e) { + throw new InstrumentException(e); + } + } + + @Override + public void setProbeSense(boolean enable) throws InstrumentException { + //TODO: 2000X series has probe detection ring + throw new InstrumentException("Probe sense is not supported"); + } + + @Override + public void setImpedance(double impedance) throws InstrumentException { + //2 channel variants have 50 Ohm mode + throw new InstrumentException("No implemented"); + } + + @Override + public void setBandwidthLimit(double bandwidthLimit) throws InstrumentException { + double mhz20 = Units.mega(20); + if (Math.abs(bandwidthLimit - mhz20) <= 0.01) { + //Set bandwidth + } + throw new InstrumentException(""); + } + + @Override + public void setCoupling(Coupling coupling) { + // TODO Auto-generated method stub + + } + + @Override + public Optional getAnalogInputSignal(int channel) throws InstrumentException { + // TODO Auto-generated method stub + return null; + } + + } + + private static class AcquisitionSystemImpl implements AcquisitionBaseSystem { + + private ISCPISocket socket; + + private int horizontalDivisions; + + private final double[] timeDivisionValues; + + public AcquisitionSystemImpl( + ISCPISocket socket, + int horizontalDivisions, + double[] timeDivisionValues) { + this.socket = socket; + this.horizontalDivisions = horizontalDivisions; + this.timeDivisionValues = timeDivisionValues; + } + + private double getHorizontalScale(double secPerDiv) { + return Arrays.stream(timeDivisionValues) + .filter(tdiv -> Math.abs(secPerDiv - tdiv) <= Units.PICO || tdiv >= secPerDiv) + .findFirst() + .getAsDouble(); + } + + @Override + public void setTimespan(double span) throws InstrumentException { + try { + double secPerDiv = span / horizontalDivisions; + double targetDiv = getHorizontalScale(secPerDiv); + String tdiv = Units.auto(targetDiv, 0, "s"); + + socket.send(SCPICommand.builder().command("TDIV").with(tdiv).build()); + } catch (IOException e) { + throw new InstrumentException(e); + } + } + + @Override + public void setSampleCount(long count) throws InstrumentException { + // TODO Auto-generated method stub + + } + + @Override + public long getSampleCount() throws InstrumentException { + // TODO Auto-generated method stub + return 0; + } + + @Override + public double getSampleRate() throws InstrumentException { + // TODO Auto-generated method stub + return 0; + } + + @Override + public void setTimeOffset(double offset) throws InstrumentException { + try { + String triggerDelay = Units.auto(-offset, 2, "s"); + + socket.send(SCPICommand.builder().command("TRDL").with(triggerDelay).build()); + } catch (IOException e) { + throw new InstrumentException(e); + } + } + + @Override + public void setMode(AcquisitionMode mode) throws InstrumentException { + // TODO Auto-generated method stub + + } + + @Override + public AcquisitionState getState() throws InstrumentException { + // TODO Auto-generated method stub + return null; + } + + } + +} diff --git a/measure-driver-siglent/src/main/java/org/jtmc/siglent/factory/SiglentDeviceFactory.java b/measure-driver-siglent/src/main/java/org/jtmc/siglent/factory/SiglentDeviceFactory.java index f9b9d78..3589fcc 100644 --- a/measure-driver-siglent/src/main/java/org/jtmc/siglent/factory/SiglentDeviceFactory.java +++ b/measure-driver-siglent/src/main/java/org/jtmc/siglent/factory/SiglentDeviceFactory.java @@ -1,7 +1,6 @@ package org.jtmc.siglent.factory; import java.io.IOException; - import org.jtmc.core.device.ISocket; import org.jtmc.core.scpi.ISCPISocket; import org.jtmc.core.scpi.socket.RawSCPISocket; @@ -9,48 +8,57 @@ import org.jtmc.core.visa.VisaException; /** - * SiglentDeviceFactory + * SiglentDeviceFactory is used for instantiating driver objects for Siglent + * devices. */ public class SiglentDeviceFactory implements VisaDeviceFactory { - public final static String SIGLENT_MANUFACTURER_STRING = "Siglent Technologies"; - - private SiglentScopeFactory scopeFactory; - - private SiglentFunctionGeneratorFactory functionGeneratorFactory; - - public SiglentDeviceFactory() { - this.scopeFactory = new SiglentScopeFactory(); - this.functionGeneratorFactory = new SiglentFunctionGeneratorFactory(); - } - - @Override - public boolean supports(ISocket socket) { - return this.supports(new RawSCPISocket(socket)); - } - - @Override - public ISCPISocket create(ISocket socket) throws VisaException { - return this.create(new RawSCPISocket(socket)); - } - - public boolean supports(ISCPISocket scpiSocket) { - try { - return scpiSocket.getDeviceIdentifier() - .getManufacturer() - .equals(SIGLENT_MANUFACTURER_STRING); - } catch(IOException e) { - return false; - } - } - - public ISCPISocket create(ISCPISocket scpiSocket) throws VisaException { - if(scopeFactory.supports(scpiSocket)) { - return scopeFactory.create(scpiSocket); - } - else if(functionGeneratorFactory.supports(scpiSocket)) { - return functionGeneratorFactory.create(scpiSocket); - } - throw new VisaException(scpiSocket + " is not supported by " + this.getClass().getSimpleName()); - } -} \ No newline at end of file + public static final String SIGLENT_MANUFACTURER_STRING = "Siglent Technologies"; + + private SiglentScopeFactory scopeFactory; + + private SiglentFunctionGeneratorFactory functionGeneratorFactory; + + public SiglentDeviceFactory() { + this.scopeFactory = new SiglentScopeFactory(); + this.functionGeneratorFactory = new SiglentFunctionGeneratorFactory(); + } + + @Override + public boolean supports(ISocket socket) { + return this.supports(new RawSCPISocket(socket)); + } + + /** + * Returns whether this socket can be used with Siglent drivers. + * + * @param scpiSocket SCPI socket + * @return {@code true} if the SCPI device is supported + */ + public boolean supports(ISCPISocket scpiSocket) { + try { + return scpiSocket.getDeviceIdentifier() + .getManufacturer() + .equals(SIGLENT_MANUFACTURER_STRING); + } catch (IOException e) { + return false; + } + } + + @Override + public ISCPISocket create(ISocket socket) throws VisaException { + return this.create(new RawSCPISocket(socket)); + } + + /** + * Creates and returns the driver for the given instrument. + */ + public ISCPISocket create(ISCPISocket scpiSocket) throws VisaException { + if (scopeFactory.supports(scpiSocket)) { + return scopeFactory.create(scpiSocket); + } else if (functionGeneratorFactory.supports(scpiSocket)) { + return functionGeneratorFactory.create(scpiSocket); + } + throw new VisaException(scpiSocket + " is not supported by " + this.getClass().getSimpleName()); + } +} diff --git a/measure-driver-siglent/src/main/java/org/jtmc/siglent/factory/SiglentFunctionGeneratorFactory.java b/measure-driver-siglent/src/main/java/org/jtmc/siglent/factory/SiglentFunctionGeneratorFactory.java index 61a3d27..bdcb0b2 100644 --- a/measure-driver-siglent/src/main/java/org/jtmc/siglent/factory/SiglentFunctionGeneratorFactory.java +++ b/measure-driver-siglent/src/main/java/org/jtmc/siglent/factory/SiglentFunctionGeneratorFactory.java @@ -1,34 +1,50 @@ package org.jtmc.siglent.factory; import java.io.IOException; - import org.jtmc.core.scpi.ISCPISocket; import org.jtmc.core.visa.DeviceIdentifier; import org.jtmc.core.visa.VisaException; import org.jtmc.siglent.driver.SDGDriver; import org.jtmc.siglent.info.SiglentFunctionGeneratorModel; +/** + * SiglentFunctionGeneratorFactory is used to instantiate drivers for + * Siglent Function generators. + */ public class SiglentFunctionGeneratorFactory { - public boolean supports(ISCPISocket scpiSocket) { - try { - SiglentFunctionGeneratorModel.create(scpiSocket.getDeviceIdentifier().getModel()); - return true; - } - catch(IllegalArgumentException | IOException e) { - return false; - } - } + /** + * Returns whether this factory is able to initialize a driver for the socket. + * + * @param scpiSocket SCPI Socket + * @return {@code true} if this factory can create a driver + */ + public boolean supports(ISCPISocket scpiSocket) { + try { + SiglentFunctionGeneratorModel.create(scpiSocket.getDeviceIdentifier().getModel()); + return true; + } catch (IllegalArgumentException | IOException e) { + return false; + } + } + + /** + * Creates a driver object for the given SCPI socket. + * + * @param scpiSocket SCPI Socket + * @return SCPI Socket + * @throws VisaException if there was an error creating the driver + */ + public ISCPISocket create(ISCPISocket scpiSocket) throws VisaException { + try { + DeviceIdentifier deviceIdentifier = scpiSocket.getDeviceIdentifier(); + String model = deviceIdentifier.getModel(); + SiglentFunctionGeneratorModel fgenModel = SiglentFunctionGeneratorModel.create(model); - public ISCPISocket create(ISCPISocket scpiSocket) throws VisaException { - try { - DeviceIdentifier deviceIdentifier = scpiSocket.getDeviceIdentifier(); - String model = deviceIdentifier.getModel(); - SiglentFunctionGeneratorModel fgenModel = SiglentFunctionGeneratorModel.create(model); + return new SDGDriver(scpiSocket, deviceIdentifier, fgenModel); + } catch (IOException e) { + throw new VisaException(e); + } + } - return new SDGDriver(scpiSocket, deviceIdentifier, fgenModel); - } catch(IOException e) { - throw new VisaException(e); - } - } -} \ No newline at end of file +} diff --git a/measure-driver-siglent/src/main/java/org/jtmc/siglent/factory/SiglentScopeFactory.java b/measure-driver-siglent/src/main/java/org/jtmc/siglent/factory/SiglentScopeFactory.java index f178a51..4bb7295 100644 --- a/measure-driver-siglent/src/main/java/org/jtmc/siglent/factory/SiglentScopeFactory.java +++ b/measure-driver-siglent/src/main/java/org/jtmc/siglent/factory/SiglentScopeFactory.java @@ -1,7 +1,6 @@ package org.jtmc.siglent.factory; import java.io.IOException; - import org.jtmc.core.scpi.ISCPISocket; import org.jtmc.core.visa.DeviceIdentifier; import org.jtmc.core.visa.VisaException; @@ -9,33 +8,48 @@ import org.jtmc.siglent.driver.SDSLegacyDriver; import org.jtmc.siglent.info.SiglentOscilloscopeModel; - +/** + * SiglentScopeFactory is used to instantiate drivers for Siglent Oscilloscopes. + */ public class SiglentScopeFactory { - public boolean supports(ISCPISocket scpiSocket) { - try { - SiglentOscilloscopeModel.create(scpiSocket.getDeviceIdentifier().getModel()); - return true; - } catch(IllegalArgumentException | IOException e) { - return false; - } - } + /** + * Returns whether this factory is able to initialize a driver for the socket. + * + * @param scpiSocket SCPI Socket + * @return {@code true} if this factory can create a driver + */ + public boolean supports(ISCPISocket scpiSocket) { + try { + SiglentOscilloscopeModel.create(scpiSocket.getDeviceIdentifier().getModel()); + return true; + } catch (IllegalArgumentException | IOException e) { + return false; + } + } - public ISCPISocket create(ISCPISocket scpiSocket) throws VisaException { - try { - DeviceIdentifier deviceIdentifier = scpiSocket.getDeviceIdentifier(); - String model = deviceIdentifier.getModel(); - SiglentOscilloscopeModel scopeInfo = SiglentOscilloscopeModel.create(model); - //TODO: verify IDN response of the SDS2000X Plus series, the postfix may be P or Plus - if( scopeInfo.getSeries() == 5 || - scopeInfo.getSeries() == 6 || - (scopeInfo.getSeries() == 2 && scopeInfo.getPostfix().equals("X+"))) { - - return new SDSE11ADriver(scpiSocket, deviceIdentifier, scopeInfo); - } - return new SDSLegacyDriver(scpiSocket, deviceIdentifier, scopeInfo); - } catch(IOException e) { - throw new VisaException(e); - } - } -} \ No newline at end of file + /** + * Creates a driver object for the given SCPI socket. + * + * @param scpiSocket SCPI Socket + * @return SCPI Socket + * @throws VisaException if there was an error creating the driver + */ + public ISCPISocket create(ISCPISocket scpiSocket) throws VisaException { + try { + DeviceIdentifier deviceIdentifier = scpiSocket.getDeviceIdentifier(); + String model = deviceIdentifier.getModel(); + SiglentOscilloscopeModel scopeInfo = SiglentOscilloscopeModel.create(model); + //TODO: verify IDN response of the SDS2000X Plus series, the postfix may be P or Plus + if (scopeInfo.getSeries() == 5 + || scopeInfo.getSeries() == 6 + || (scopeInfo.getSeries() == 2 && scopeInfo.getPostfix().equals("X+"))) { + + return new SDSE11ADriver(scpiSocket, deviceIdentifier, scopeInfo); + } + return new SDSLegacyDriver(scpiSocket, deviceIdentifier, scopeInfo); + } catch (IOException e) { + throw new VisaException(e); + } + } +} diff --git a/measure-driver-siglent/src/main/java/org/jtmc/siglent/info/SiglentFunctionGeneratorModel.java b/measure-driver-siglent/src/main/java/org/jtmc/siglent/info/SiglentFunctionGeneratorModel.java index 47c6aec..2fa682c 100644 --- a/measure-driver-siglent/src/main/java/org/jtmc/siglent/info/SiglentFunctionGeneratorModel.java +++ b/measure-driver-siglent/src/main/java/org/jtmc/siglent/info/SiglentFunctionGeneratorModel.java @@ -3,29 +3,42 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; +/** + * SiglentFunctionGeneratorModel contains model information about + * Siglent Function generators. + */ public class SiglentFunctionGeneratorModel { - public final static Pattern BENCHTOP_FGEN_REGEX = Pattern.compile("SDG(?[123456])(?[0-9]{2,3})(?X?)"); - - private int series; + public static final Pattern BENCHTOP_FGEN_REGEX = Pattern.compile( + "SDG(?[123456])(?[0-9]{2,3})(?X?)"); + + private int series; - public static SiglentFunctionGeneratorModel create(String model) { - Matcher matcher = BENCHTOP_FGEN_REGEX.matcher(model); - if(!matcher.matches()) { - throw new IllegalArgumentException(); - } - int series = Integer.parseInt(matcher.group("seriesprefix")); - //String postfix = matcher.group("seriespostfix"); - //TODO: add more information - - return new SiglentFunctionGeneratorModel(series); - } + /** + * Creates a Siglent function generator model info container based on + * the model number. + * + * @param model Model number + * @return Model information + */ + public static SiglentFunctionGeneratorModel create(String model) { + Matcher matcher = BENCHTOP_FGEN_REGEX.matcher(model); + if (!matcher.matches()) { + throw new IllegalArgumentException(); + } + int series = Integer.parseInt(matcher.group("seriesprefix")); + //String postfix = matcher.group("seriespostfix"); + //TODO: add more information + + return new SiglentFunctionGeneratorModel(series); + } - private SiglentFunctionGeneratorModel(int series) { - this.series = series; - } + private SiglentFunctionGeneratorModel(int series) { + this.series = series; + } - public int getSeries() { - return series; - } -} \ No newline at end of file + public int getSeries() { + return series; + } + +} diff --git a/measure-driver-siglent/src/main/java/org/jtmc/siglent/info/SiglentOscilloscopeModel.java b/measure-driver-siglent/src/main/java/org/jtmc/siglent/info/SiglentOscilloscopeModel.java index a34022a..ef7a3c9 100644 --- a/measure-driver-siglent/src/main/java/org/jtmc/siglent/info/SiglentOscilloscopeModel.java +++ b/measure-driver-siglent/src/main/java/org/jtmc/siglent/info/SiglentOscilloscopeModel.java @@ -2,93 +2,89 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; - import org.jtmc.core.util.Units; /** - * SiglentOscilloscopeInfo is used to contain meta information about Siglent Oscilloscopes + * SiglentOscilloscopeInfo is used to contain meta information about + * Siglent Oscilloscopes. */ public class SiglentOscilloscopeModel { - - public final static Pattern SCOPE_REGEX = Pattern.compile("SDS(?[123456])(?[0-9]{2})(?[24])(?DL\\+|CML\\+|CFL|X(\\+|-E)?)"); + + public static final Pattern SCOPE_REGEX = Pattern.compile( + "SDS(?[123456])(?[0-9]{2})(?[24])" + + "(?DL\\+|CML\\+|CFL|X(\\+|-E)?)"); + + private int series; - private int series; + private String bandwidth; - private String bandwidth; + private int channels; - private int channels; + private String postfix; - private String postfix; + /** + * Converts a Siglent Oscilloscope model number into a metainfo container object. + * + * @param model Oscilloscope model + * @return Oscilloscope information + * @throws IllegalArgumentException If the model number is invalid + */ + public static SiglentOscilloscopeModel create(String model) throws IllegalArgumentException { + Matcher matcher = SCOPE_REGEX.matcher(model); + if (!matcher.matches()) { + throw new IllegalArgumentException(); + } + int series = Integer.parseInt(matcher.group("seriesprefix")); + String bandwidth = matcher.group("bandwidth"); + int channels = Integer.parseInt(matcher.group("channels")); + String postfix = matcher.group("seriespostfix"); + return new SiglentOscilloscopeModel(series, bandwidth, channels, postfix); + } - /** - * Converts a Siglent Oscilloscope model number into a metainfo container object - * - * @param model Oscilloscope model - * @return Oscilloscope information - * @throws IllegalArgumentException If the model number is invalid - */ - public static SiglentOscilloscopeModel create(String model) throws IllegalArgumentException { - Matcher matcher = SCOPE_REGEX.matcher(model); - if(!matcher.matches()) { - throw new IllegalArgumentException(); - } - int series = Integer.parseInt(matcher.group("seriesprefix")); - String bandwidth = matcher.group("bandwidth"); - int channels = Integer.parseInt(matcher.group("channels")); - String postfix = matcher.group("seriespostfix"); - return new SiglentOscilloscopeModel(series, bandwidth, channels, postfix); - } + private SiglentOscilloscopeModel(int series, String bandwidth, int channels, String postfix) { + this.series = series; + this.channels = channels; + this.postfix = postfix; + } - private SiglentOscilloscopeModel(int series, String bandwidth, int channels, String postfix) { - this.series = series; - this.channels = channels; - this.postfix = postfix; - } + /** + * Returns the analog bandwidth of the oscilloscope model. + * @return Analog bandwidth + */ + public double getBandwidth() { + if (this.series == 5) { + switch (this.bandwidth) { + case "03": return Units.mega(350); + case "05": return Units.mega(500); + case "10": return Units.mega(1000); + default: break; + } + } + switch (this.bandwidth) { + case "03": return Units.mega(350); + case "05": return Units.mega(50); + case "07": return Units.mega(70); + case "10": return Units.mega(100); + case "15": return Units.mega(150); + case "20": return Units.mega(200); + case "30": return Units.mega(300); + case "35": return Units.mega(350); + default: break; + } + throw new IllegalArgumentException("Unable to determine bandwidth from: " + + this.bandwidth + "(series " + this.series + ")"); + } - /** - * Returns the bandwidth of the Oscilloscope model - * @return - */ - public double getBandwidth() { - if(this.series == 5) { - switch(this.bandwidth) { - case "03": return Units.mega(350); - case "05": return Units.mega(500); - case "10": return Units.mega(1000); - } - } - switch(this.bandwidth) { - case "03": return Units.mega(350); - case "05": return Units.mega(50); - case "07": return Units.mega(70); - case "10": return Units.mega(100); - case "15": return Units.mega(150); - case "20": return Units.mega(200); - case "30": return Units.mega(300); - case "35": return Units.mega(350); - } - throw new IllegalArgumentException("Unable to determine bandwidth from: " + this.bandwidth + "(series " + this.series + ")"); - } + public int getSeries() { + return series; + } - /** - * @return the series - */ - public int getSeries() { - return series; - } + public int getChannels() { + return channels; + } - /** - * @return the channels - */ - public int getChannels() { - return channels; - } + public String getPostfix() { + return postfix; + } - /** - * @return the postfix - */ - public String getPostfix() { - return postfix; - } - -} \ No newline at end of file +} diff --git a/measure-driver-siglent/src/test/java/org/jtmc/siglent/driver/SDSLegacyDriverTest.java b/measure-driver-siglent/src/test/java/org/jtmc/siglent/driver/SDSLegacyDriverTest.java index 8057b37..143c11f 100644 --- a/measure-driver-siglent/src/test/java/org/jtmc/siglent/driver/SDSLegacyDriverTest.java +++ b/measure-driver-siglent/src/test/java/org/jtmc/siglent/driver/SDSLegacyDriverTest.java @@ -3,7 +3,6 @@ import static org.junit.Assert.assertEquals; import java.io.IOException; - import org.jtmc.core.scpi.mock.TestSCPISocket; import org.jtmc.core.visa.DeviceIdentifier; import org.jtmc.siglent.factory.SiglentDeviceFactory; @@ -12,32 +11,47 @@ import org.junit.runner.RunWith; import org.junit.runners.BlockJUnit4ClassRunner; +/** + * Tests the Siglent SDSLegacyDriver. + */ @RunWith(BlockJUnit4ClassRunner.class) public class SDSLegacyDriverTest { - @Test - public void testInstanceSDS1202XE() throws IOException { - DeviceIdentifier identifier = DeviceIdentifier.from(SiglentDeviceFactory.SIGLENT_MANUFACTURER_STRING, "SDS1202X-E", "ABC123", "1.0.0"); - SDSLegacyDriver scope = new SDSLegacyDriver(new TestSCPISocket(identifier)); - - assertEquals(scope.getAnalogInputs().size(), 2); - } - - @Test - public void testInstanceSDS1104XE() throws IOException { - DeviceIdentifier identifier = DeviceIdentifier.from(SiglentDeviceFactory.SIGLENT_MANUFACTURER_STRING, "SDS1104X-E", "ABC123", "1.0.0"); - SiglentOscilloscopeModel info = SiglentOscilloscopeModel.create(identifier.getModel()); - SDSLegacyDriver scope = new SDSLegacyDriver(new TestSCPISocket(identifier), identifier, info); - - assertEquals(scope.getAnalogInputs().size(), 4); - } - - @Test - public void testInstanceSDS1204XE() throws IOException { - DeviceIdentifier identifier = DeviceIdentifier.from(SiglentDeviceFactory.SIGLENT_MANUFACTURER_STRING, "SDS1204X-E", "ABC123", "1.0.0"); - SiglentOscilloscopeModel info = SiglentOscilloscopeModel.create(identifier.getModel()); - SDSLegacyDriver scope = new SDSLegacyDriver(new TestSCPISocket(identifier), identifier, info); - - assertEquals(scope.getAnalogInputs().size(), 4); - } -} \ No newline at end of file + @Test + public void testInstanceSDS1202XE() throws IOException { + DeviceIdentifier identifier = DeviceIdentifier.from( + SiglentDeviceFactory.SIGLENT_MANUFACTURER_STRING, + "SDS1202X-E", + "ABC123", + "1.0.0"); + SDSLegacyDriver scope = new SDSLegacyDriver(new TestSCPISocket(identifier)); + + assertEquals(scope.getAnalogInputs().size(), 2); + } + + @Test + public void testInstanceSDS1104XE() throws IOException { + DeviceIdentifier identifier = DeviceIdentifier.from( + SiglentDeviceFactory.SIGLENT_MANUFACTURER_STRING, + "SDS1104X-E", + "ABC123", + "1.0.0"); + SiglentOscilloscopeModel info = SiglentOscilloscopeModel.create(identifier.getModel()); + SDSLegacyDriver scope = new SDSLegacyDriver(new TestSCPISocket(identifier), identifier, info); + + assertEquals(scope.getAnalogInputs().size(), 4); + } + + @Test + public void testInstanceSDS1204XE() throws IOException { + DeviceIdentifier identifier = DeviceIdentifier.from( + SiglentDeviceFactory.SIGLENT_MANUFACTURER_STRING, + "SDS1204X-E", + "ABC123", + "1.0.0"); + SiglentOscilloscopeModel info = SiglentOscilloscopeModel.create(identifier.getModel()); + SDSLegacyDriver scope = new SDSLegacyDriver(new TestSCPISocket(identifier), identifier, info); + + assertEquals(scope.getAnalogInputs().size(), 4); + } +} diff --git a/measure-driver-siglent/src/test/java/org/jtmc/siglent/factory/SiglentFactoryTest.java b/measure-driver-siglent/src/test/java/org/jtmc/siglent/factory/SiglentFactoryTest.java index 2e3eb22..ddd294b 100644 --- a/measure-driver-siglent/src/test/java/org/jtmc/siglent/factory/SiglentFactoryTest.java +++ b/measure-driver-siglent/src/test/java/org/jtmc/siglent/factory/SiglentFactoryTest.java @@ -10,56 +10,59 @@ import org.junit.runner.RunWith; import org.junit.runners.BlockJUnit4ClassRunner; +/** + * Tests Siglent device factory. + */ @RunWith(BlockJUnit4ClassRunner.class) public class SiglentFactoryTest { - private SiglentDeviceFactory factory = new SiglentDeviceFactory(); + private SiglentDeviceFactory factory = new SiglentDeviceFactory(); - @Test - public void testFactorySupportsDevice() { - TestSCPISocket scpiSocket = new TestSCPISocket( - DeviceIdentifier.from( - SiglentDeviceFactory.SIGLENT_MANUFACTURER_STRING, - "ABC123", - "ABC123", - "1.0.0")); + @Test + public void testFactorySupportsDevice() { + TestSCPISocket scpiSocket = new TestSCPISocket( + DeviceIdentifier.from( + SiglentDeviceFactory.SIGLENT_MANUFACTURER_STRING, + "ABC123", + "ABC123", + "1.0.0")); - assertTrue(factory.supports(scpiSocket)); - } + assertTrue(factory.supports(scpiSocket)); + } - @Test - public void testFactoryUnsupportedDevice() { - TestSCPISocket scpiSocket = new TestSCPISocket( - DeviceIdentifier.from( - "Other Manufacturer", - "ABC123", - "ABC123", - "1.0.0")); + @Test + public void testFactoryUnsupportedDevice() { + TestSCPISocket scpiSocket = new TestSCPISocket( + DeviceIdentifier.from( + "Other Manufacturer", + "ABC123", + "ABC123", + "1.0.0")); - assertFalse(factory.supports(scpiSocket)); - } + assertFalse(factory.supports(scpiSocket)); + } - @Test(expected = VisaException.class) - public void testFactoryCreateUnsupportedDevice() throws VisaException { - TestSCPISocket scpiSocket = new TestSCPISocket( - DeviceIdentifier.from( - SiglentDeviceFactory.SIGLENT_MANUFACTURER_STRING, - "ABC123", - "ABC123", - "1.0.0")); - - factory.create(scpiSocket); - } + @Test(expected = VisaException.class) + public void testFactoryCreateUnsupportedDevice() throws VisaException { + TestSCPISocket scpiSocket = new TestSCPISocket( + DeviceIdentifier.from( + SiglentDeviceFactory.SIGLENT_MANUFACTURER_STRING, + "ABC123", + "ABC123", + "1.0.0")); + + factory.create(scpiSocket); + } - @Test(expected = VisaException.class) - public void testFactoryCreateSupportedDevice() throws VisaException { - TestSCPISocket scpiSocket = new TestSCPISocket( - DeviceIdentifier.from( - SiglentDeviceFactory.SIGLENT_MANUFACTURER_STRING, - "SDS1104XE", - "ABC123", - "1.0.0")); - - factory.create(scpiSocket); - } -} \ No newline at end of file + @Test(expected = VisaException.class) + public void testFactoryCreateSupportedDevice() throws VisaException { + TestSCPISocket scpiSocket = new TestSCPISocket( + DeviceIdentifier.from( + SiglentDeviceFactory.SIGLENT_MANUFACTURER_STRING, + "SDS1104XE", + "ABC123", + "1.0.0")); + + factory.create(scpiSocket); + } +} diff --git a/measure-driver-siglent/src/test/java/org/jtmc/siglent/hardware/SDSLegacyDriverHardwareTest.java b/measure-driver-siglent/src/test/java/org/jtmc/siglent/hardware/SDSLegacyDriverHardwareTest.java index bebaf25..3a833b7 100644 --- a/measure-driver-siglent/src/test/java/org/jtmc/siglent/hardware/SDSLegacyDriverHardwareTest.java +++ b/measure-driver-siglent/src/test/java/org/jtmc/siglent/hardware/SDSLegacyDriverHardwareTest.java @@ -2,7 +2,6 @@ import java.io.IOException; import java.net.InetAddress; - import org.jtmc.core.instrument.Oscilloscope; import org.jtmc.core.lxi.vxi11.VXI11Socket; import org.jtmc.core.scpi.socket.RawSCPISocket; @@ -13,23 +12,26 @@ import org.junit.runner.RunWith; import org.junit.runners.BlockJUnit4ClassRunner; +/** + * Hardware test of Siglent SDS scopes that use the SDSLegacy driver. + */ @RunWith(BlockJUnit4ClassRunner.class) public class SDSLegacyDriverHardwareTest { - private final static String IP = "192.168.2.2"; + //TODO: externalize or use hostname based resolution + private static final String IP = "192.168.2.2"; - @Test - public void testScopeDriver() throws IOException, InstrumentException { - - try(VXI11Socket socket = new VXI11Socket(InetAddress.getByName(IP))) { - Oscilloscope scope = new SDSLegacyDriver(new RawSCPISocket(socket)); + @Test + public void testScopeDriver() throws IOException, InstrumentException { + try (VXI11Socket socket = new VXI11Socket(InetAddress.getByName(IP))) { + Oscilloscope scope = new SDSLegacyDriver(new RawSCPISocket(socket)); - scope.acquisition().setTimespan(Units.milli(14)); - scope.acquisition().setTimeOffset(0); - scope.getAnalogInput(2).setEnabled(true); - scope.getAnalogInput(2).setProbeAttenuation(10); - scope.getAnalogInput(2).setRange(3.1); - scope.getAnalogInput(2).setOffset(-1.5); - } - } -} \ No newline at end of file + scope.acquisition().setTimespan(Units.milli(14)); + scope.acquisition().setTimeOffset(0); + scope.getAnalogInput(2).setEnabled(true); + scope.getAnalogInput(2).setProbeAttenuation(10); + scope.getAnalogInput(2).setRange(3.1); + scope.getAnalogInput(2).setOffset(-1.5); + } + } +} diff --git a/measure-signals/src/main/java/org/jtmc/core/signal/analog/Sine.java b/measure-signals/src/main/java/org/jtmc/core/signal/analog/Sine.java index be86aab..4db6193 100644 --- a/measure-signals/src/main/java/org/jtmc/core/signal/analog/Sine.java +++ b/measure-signals/src/main/java/org/jtmc/core/signal/analog/Sine.java @@ -1,15 +1,15 @@ package org.jtmc.core.signal.analog; /** - * Sine + * Sine function as an AnalogSignal. */ public class Sine extends AnalogSignal { - public Sine(double frequency, double amplitude, double offset) { - super(""); - } + public Sine(double frequency, double amplitude, double offset) { + super(""); + } - public static Sine sine(double frequency, double amplitude, double offset) { - return new Sine(frequency, amplitude, offset); - } -} \ No newline at end of file + public static Sine sine(double frequency, double amplitude, double offset) { + return new Sine(frequency, amplitude, offset); + } +} diff --git a/measure-signals/src/main/java/org/jtmc/core/signal/analog/sampler/AnalogInverter.java b/measure-signals/src/main/java/org/jtmc/core/signal/analog/sampler/AnalogInverter.java index 5dc5497..6571f4c 100644 --- a/measure-signals/src/main/java/org/jtmc/core/signal/analog/sampler/AnalogInverter.java +++ b/measure-signals/src/main/java/org/jtmc/core/signal/analog/sampler/AnalogInverter.java @@ -1,42 +1,50 @@ package org.jtmc.core.signal.analog.sampler; -import org.jtmc.core.signal.analog.AnalogSampler; -import org.jtmc.core.signal.analog.AnalogSignal; - import java.util.function.Function; - import org.jtmc.core.signal.Signal; +import org.jtmc.core.signal.analog.AnalogSampler; +import org.jtmc.core.signal.analog.AnalogSignal; /** - * This sampler inverts any analog signal alongside the voltage axis + * This sampler inverts any analog signal alongside the voltage axis. */ public class AnalogInverter implements AnalogSampler, Function { - private final float offset; - - public AnalogInverter() { - this(0.0f); - } - - public AnalogInverter(float offset) { - this.offset = offset; - } - - @Override - public Float apply(Float value) { - return -(value - offset); - } - - @Override - public AnalogSignal sample(Signal signal) { - return new AnalogSignal(signal, this); - } - - /** - * @return the offset - */ - public float getOffset() { - return offset; - } - -} \ No newline at end of file + private final float offset; + + /** + * Constructs an AnalogInverter with an offset of zero. + */ + public AnalogInverter() { + this(0.0f); + } + + /** + * Constructs an AnalogInverter with the given offset. + * + * @param offset Offset + */ + public AnalogInverter(float offset) { + this.offset = offset; + } + + @Override + public Float apply(Float value) { + return -(value - offset); + } + + @Override + public AnalogSignal sample(Signal signal) { + return new AnalogSignal(signal, this); + } + + /** + * Returns the offset of the inverter. + * + * @return Offset + */ + public float getOffset() { + return offset; + } + +} diff --git a/measure-signals/src/main/java/org/jtmc/core/signal/analog/sampler/BinaryToAnalog.java b/measure-signals/src/main/java/org/jtmc/core/signal/analog/sampler/BinaryToAnalog.java index b411db2..efd0b9d 100644 --- a/measure-signals/src/main/java/org/jtmc/core/signal/analog/sampler/BinaryToAnalog.java +++ b/measure-signals/src/main/java/org/jtmc/core/signal/analog/sampler/BinaryToAnalog.java @@ -1,34 +1,34 @@ package org.jtmc.core.signal.analog.sampler; import java.util.function.Function; - import org.jtmc.core.signal.Signal; import org.jtmc.core.signal.analog.AnalogSignal; import org.jtmc.core.signal.sampler.Sampler; /** - * BinaryToAnalog + * BinaryToAnalog converts a binary signal to an analog signal with the given + * low and high associations. */ -public class BinaryToAnalog implements Sampler, Function { +public class BinaryToAnalog implements + Sampler, Function { - private float high; + private float high; - private float low; + private float low; - public BinaryToAnalog(float high, float low) { - this.high = high; - this.low = low; - } + public BinaryToAnalog(float high, float low) { + this.high = high; + this.low = low; + } - @Override - public AnalogSignal sample(Signal signal) { - return new AnalogSignal(signal, this); - } + @Override + public AnalogSignal sample(Signal signal) { + return new AnalogSignal(signal, this); + } - @Override - public Float apply(Boolean value) { - return value ? high : low; - } + @Override + public Float apply(Boolean value) { + return value ? high : low; + } - -} \ No newline at end of file +} diff --git a/measure-signals/src/main/java/org/jtmc/core/signal/analog/sampler/ClippingSampler.java b/measure-signals/src/main/java/org/jtmc/core/signal/analog/sampler/ClippingSampler.java index 6407113..a372d41 100644 --- a/measure-signals/src/main/java/org/jtmc/core/signal/analog/sampler/ClippingSampler.java +++ b/measure-signals/src/main/java/org/jtmc/core/signal/analog/sampler/ClippingSampler.java @@ -1,11 +1,9 @@ package org.jtmc.core.signal.analog.sampler; -import org.jtmc.core.signal.analog.AnalogSampler; -import org.jtmc.core.signal.analog.AnalogSignal; - import java.util.function.Function; - import org.jtmc.core.signal.Signal; +import org.jtmc.core.signal.analog.AnalogSampler; +import org.jtmc.core.signal.analog.AnalogSignal; /** * ClippingSampler takes an analog signal and clips voltages to minimum and @@ -13,29 +11,29 @@ */ public class ClippingSampler implements AnalogSampler, Function { - private float min; - - private float max; - - /** - * Creates a new clipping sampler - * - * @param min Minimum voltage - * @param max Maximum voltage - */ - public ClippingSampler(float min, float max) { - this.min = min; - this.max = max; - } - - @Override - public Float apply(Float value) { - return Math.max(Math.min(value, max), min); - } - - @Override - public AnalogSignal sample(Signal signal) { - return new AnalogSignal(signal, this); - } - -} \ No newline at end of file + private float min; + + private float max; + + /** + * Constructs a new clipping sampler with the given top and bottom thresholds. + * + * @param min Minimum voltage + * @param max Maximum voltage + */ + public ClippingSampler(float min, float max) { + this.min = min; + this.max = max; + } + + @Override + public Float apply(Float value) { + return Math.max(Math.min(value, max), min); + } + + @Override + public AnalogSignal sample(Signal signal) { + return new AnalogSignal(signal, this); + } + +} diff --git a/measure-signals/src/main/java/org/jtmc/core/signal/digital/I2C.java b/measure-signals/src/main/java/org/jtmc/core/signal/digital/I2C.java deleted file mode 100644 index e69de29..0000000 diff --git a/measure-signals/src/main/java/org/jtmc/core/signal/digital/PRBS.java b/measure-signals/src/main/java/org/jtmc/core/signal/digital/PRBS.java deleted file mode 100644 index 84b7125..0000000 --- a/measure-signals/src/main/java/org/jtmc/core/signal/digital/PRBS.java +++ /dev/null @@ -1,14 +0,0 @@ -package org.jtmc.core.signal.digital; - -import org.jtmc.core.signal.Signal; - -/** - * PRBS - */ -public class PRBS extends Signal { - - public PRBS(int bitCount, float bitRate) { - super("PRBS"); - } - -} \ No newline at end of file diff --git a/measure-signals/src/main/java/org/jtmc/core/signal/digital/SPI.java b/measure-signals/src/main/java/org/jtmc/core/signal/digital/SPI.java deleted file mode 100644 index e69de29..0000000 diff --git a/measure-signals/src/main/java/org/jtmc/core/signal/digital/UART.java b/measure-signals/src/main/java/org/jtmc/core/signal/digital/UART.java deleted file mode 100644 index a4ef417..0000000 --- a/measure-signals/src/main/java/org/jtmc/core/signal/digital/UART.java +++ /dev/null @@ -1,9 +0,0 @@ -package org.jtmc.core.signal.digital; - -/** - * UART - */ -public class UART { - - -} \ No newline at end of file diff --git a/measure-signals/src/main/java/org/jtmc/core/signal/digital/Wiegand.java b/measure-signals/src/main/java/org/jtmc/core/signal/digital/Wiegand.java index 3fc4daf..76e506f 100644 --- a/measure-signals/src/main/java/org/jtmc/core/signal/digital/Wiegand.java +++ b/measure-signals/src/main/java/org/jtmc/core/signal/digital/Wiegand.java @@ -1,54 +1,59 @@ package org.jtmc.core.signal.digital; +import static org.jtmc.core.util.Units.micro; +import static org.jtmc.core.util.Units.milli; + import org.jtmc.core.signal.CompositeSignal; import org.jtmc.core.signal.Signal; -import static org.jtmc.core.util.Units.*; - /** - * Wiegand + * Wiegand is used to create waveforms that emulate RFID card reader outputs. */ public class Wiegand extends CompositeSignal { - private int value; - - public Wiegand(int value) { - super("W26_" + String.valueOf(value)); - this.value = value; - Signal d0 = new Signal<>("D0"); - Signal d1 = new Signal<>("D1"); - this.add(d0); - this.add(d1); - - int output = value << 1; - if(Integer.bitCount(value & 0xFFF000) % 2 != 0) { - output |= (1 << 25); - } - if(Integer.bitCount(value & 0x000FFF) % 2 == 0) { - output |= 1; - } - double time = micro(1); - d0.add(0, true); - d1.add(0, true); - for(int i=25;i>=0;i--) { - if((output & (1 << i)) == 0) { - d0.add(time, true); - d0.add(time + micro(100), false); - d0.add(time + micro(200), true); - } - else { - d1.add(time, true); - d1.add(time + micro(100), false); - d1.add(time + micro(200), true); - } - time += milli(1); - } - d0.add(time, true); - d1.add(time, true); - } + private int value; + + /** + * Constructs a Wiegand signal using the given Wiegand26 code. + * + * @param value Card code + */ + public Wiegand(int value) { + super("W26_" + String.valueOf(value)); + this.value = value; + Signal d0 = new Signal<>("D0"); + Signal d1 = new Signal<>("D1"); + this.add(d0); + this.add(d1); + + int output = value << 1; + if (Integer.bitCount(value & 0xFFF000) % 2 != 0) { + output |= (1 << 25); + } + if (Integer.bitCount(value & 0x000FFF) % 2 == 0) { + output |= 1; + } + double time = micro(1); + d0.add(0, true); + d1.add(0, true); + for (int i = 25; i >= 0; i--) { + if ((output & (1 << i)) == 0) { + d0.add(time, true); + d0.add(time + micro(100), false); + d0.add(time + micro(200), true); + } else { + d1.add(time, true); + d1.add(time + micro(100), false); + d1.add(time + micro(200), true); + } + time += milli(1); + } + d0.add(time, true); + d1.add(time, true); + } + + public int getValue() { + return value; + } - public int getValue() { - return value; - } - -} \ No newline at end of file +} diff --git a/measure-signals/src/main/java/org/jtmc/core/signal/digital/sampler/BinaryInverter.java b/measure-signals/src/main/java/org/jtmc/core/signal/digital/sampler/BinaryInverter.java index 8f1f608..25f0e11 100644 --- a/measure-signals/src/main/java/org/jtmc/core/signal/digital/sampler/BinaryInverter.java +++ b/measure-signals/src/main/java/org/jtmc/core/signal/digital/sampler/BinaryInverter.java @@ -1,21 +1,21 @@ package org.jtmc.core.signal.digital.sampler; +import org.jtmc.core.signal.Signal; import org.jtmc.core.signal.digital.BinarySampler; import org.jtmc.core.signal.digital.BinarySignal; -import org.jtmc.core.signal.Signal; /** - * BinaryInverter takes a binary signal and inverts the signal logically + * BinaryInverter takes a binary signal and inverts the signal logically. */ public class BinaryInverter implements BinarySampler { - @Override - public BinarySignal sample(Signal signal) { - BinarySignal output = new BinarySignal("~" + signal.getId()); - signal.stream().forEach(point -> { - output.add(point.time, !point.value); - }); - return output; - } - -} \ No newline at end of file + @Override + public BinarySignal sample(Signal signal) { + BinarySignal output = new BinarySignal("~" + signal.getId()); + signal.stream().forEach(point -> { + output.add(point.time, !point.value); + }); + return output; + } + +} diff --git a/measure-signals/src/main/java/org/jtmc/core/signal/digital/sampler/SchmittTrigger.java b/measure-signals/src/main/java/org/jtmc/core/signal/digital/sampler/SchmittTrigger.java index 90334df..1577858 100644 --- a/measure-signals/src/main/java/org/jtmc/core/signal/digital/sampler/SchmittTrigger.java +++ b/measure-signals/src/main/java/org/jtmc/core/signal/digital/sampler/SchmittTrigger.java @@ -1,44 +1,45 @@ package org.jtmc.core.signal.digital.sampler; -import org.jtmc.core.signal.digital.BinarySignal; -import org.jtmc.core.signal.sampler.Sampler; import org.jtmc.core.signal.Signal; import org.jtmc.core.signal.Signal.DataPoint; +import org.jtmc.core.signal.digital.BinarySignal; +import org.jtmc.core.signal.sampler.Sampler; /** - * Threshold + * SchmittTrigger takes an analog signal and converts it to a binary signal + * using thresholds. */ public class SchmittTrigger implements Sampler { - private float upper; - - private float lower; - - public SchmittTrigger(float upper, float lower) { - this.upper = upper; - this.lower = lower; - } - - @Override - public BinarySignal sample(Signal signal) { - BinarySignal output = new BinarySignal(signal.getId()); - int state = -state(signal.first(), upper, lower); - for(DataPoint point : signal.getData()) { - int current = state(point.value, upper, lower); - if(current != state && current != 0) { - output.add(point.time, bin(current)); - state = current; - } - } - return null; - } - - private int state(float value, float upper, float lower) { - return (value > upper ? 1 : (value < lower ? -1 : 0)); - } - - private boolean bin(int state) { - return state > 0 ? true : false; - } - -} \ No newline at end of file + private float upper; + + private float lower; + + public SchmittTrigger(float upper, float lower) { + this.upper = upper; + this.lower = lower; + } + + @Override + public BinarySignal sample(Signal signal) { + BinarySignal output = new BinarySignal(signal.getId()); + int state = -state(signal.first(), upper, lower); + for (DataPoint point : signal.getData()) { + int current = state(point.value, upper, lower); + if (current != state && current != 0) { + output.add(point.time, bin(current)); + state = current; + } + } + return null; + } + + private int state(float value, float upper, float lower) { + return (value > upper ? 1 : (value < lower ? -1 : 0)); + } + + private boolean bin(int state) { + return state > 0 ? true : false; + } + +} diff --git a/measure-signals/src/main/java/org/jtmc/core/signal/digital/sampler/Threshold.java b/measure-signals/src/main/java/org/jtmc/core/signal/digital/sampler/Threshold.java index a711abb..398e2fb 100644 --- a/measure-signals/src/main/java/org/jtmc/core/signal/digital/sampler/Threshold.java +++ b/measure-signals/src/main/java/org/jtmc/core/signal/digital/sampler/Threshold.java @@ -1,33 +1,40 @@ package org.jtmc.core.signal.digital.sampler; -import org.jtmc.core.signal.digital.BinarySignal; -import org.jtmc.core.signal.sampler.Sampler; import org.jtmc.core.signal.Signal; import org.jtmc.core.signal.Signal.DataPoint; +import org.jtmc.core.signal.digital.BinarySignal; +import org.jtmc.core.signal.sampler.Sampler; /** - * Threshold + * Threshold converts an analog signal to a binary signal using the given + * threshold. */ public class Threshold implements Sampler { - private float threshold; + private float threshold; + + /** + * Constructs a Threshold sampler with the given threshold, values + * below it will be interpreted as 0, otherwise will be 1's. + * + * @param threshold Threshold + */ + public Threshold(float threshold) { + this.threshold = threshold; + } - public Threshold(float threshold) { - this.threshold = threshold; - } + @Override + public BinarySignal sample(Signal signal) { + BinarySignal output = new BinarySignal(signal.getId()); + boolean state = !(signal.first() > threshold); + for (DataPoint point : signal.getData()) { + boolean current = point.value > threshold; + if (current != state) { + output.add(point.time, current); + state = current; + } + } + return null; + } - @Override - public BinarySignal sample(Signal signal) { - BinarySignal output = new BinarySignal(signal.getId()); - boolean state = !(signal.first() > threshold); - for(DataPoint point : signal.getData()) { - boolean current = point.value > threshold; - if(current != state) { - output.add(point.time, current); - state = current; - } - } - return null; - } - -} \ No newline at end of file +} diff --git a/measure-spring-adapter/src/main/java/org/jtmc/spring/configuration/ServiceAutoConfiguration.java b/measure-spring-adapter/src/main/java/org/jtmc/spring/configuration/ServiceAutoConfiguration.java index fc685bb..5a896c0 100644 --- a/measure-spring-adapter/src/main/java/org/jtmc/spring/configuration/ServiceAutoConfiguration.java +++ b/measure-spring-adapter/src/main/java/org/jtmc/spring/configuration/ServiceAutoConfiguration.java @@ -1,7 +1,6 @@ package org.jtmc.spring.configuration; import java.util.List; - import org.jtmc.core.visa.VISAResourceManager; import org.jtmc.core.visa.VisaDeviceFactory; import org.jtmc.core.visa.factory.SocketFactory; @@ -9,14 +8,16 @@ import org.springframework.context.annotation.Configuration; /** - * ServiceAutoConfiguration + * ServiceAutoConfiguration provides VISA resource beans. */ @Configuration public class ServiceAutoConfiguration { - @Bean - public VISAResourceManager visaResourceManager(SocketFactory socketFactory, List> visaFactories) { - return new VISAResourceManager(socketFactory, visaFactories); - } - -} \ No newline at end of file + @Bean + public VISAResourceManager visaResourceManager( + SocketFactory socketFactory, + List> visaFactories) { + return new VISAResourceManager(socketFactory, visaFactories); + } + +} diff --git a/measure-spring-adapter/src/main/java/org/jtmc/spring/configuration/SocketFactoryAutoConfiguration.java b/measure-spring-adapter/src/main/java/org/jtmc/spring/configuration/SocketFactoryAutoConfiguration.java index 3da8bf6..b25bff2 100644 --- a/measure-spring-adapter/src/main/java/org/jtmc/spring/configuration/SocketFactoryAutoConfiguration.java +++ b/measure-spring-adapter/src/main/java/org/jtmc/spring/configuration/SocketFactoryAutoConfiguration.java @@ -1,7 +1,6 @@ package org.jtmc.spring.configuration; import java.util.List; - import org.jtmc.core.lxi.raw.RawSocketFactory; import org.jtmc.core.lxi.vxi11.VXI11SocketFactory; import org.jtmc.core.visa.factory.ISocketFactory; @@ -10,27 +9,30 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +/** + * SocketFactoryAutoConfiguration provides Socket factory beans. + */ @Configuration public class SocketFactoryAutoConfiguration { - @Bean - public ISocketFactory rawSocketFactory() { - return new RawSocketFactory(); - } + @Bean + public ISocketFactory rawSocketFactory() { + return new RawSocketFactory(); + } - @Bean - public ISocketFactory vxi11SocketFactory() { - return new VXI11SocketFactory(); - } + @Bean + public ISocketFactory vxi11SocketFactory() { + return new VXI11SocketFactory(); + } - @Bean - public ISocketFactory mockSocketFactory() { - return new MockSocketFactory(); - } + @Bean + public ISocketFactory mockSocketFactory() { + return new MockSocketFactory(); + } - @Bean - public SocketFactory socketFactory(List factories){ - return new SocketFactory(factories); - } - -} \ No newline at end of file + @Bean + public SocketFactory socketFactory(List factories) { + return new SocketFactory(factories); + } + +} diff --git a/measure-test-framework/src/main/java/org/jtmc/test/JMeasureRunner.java b/measure-test-framework/src/main/java/org/jtmc/test/JMeasureRunner.java deleted file mode 100644 index 647ef04..0000000 --- a/measure-test-framework/src/main/java/org/jtmc/test/JMeasureRunner.java +++ /dev/null @@ -1,8 +0,0 @@ -package org.jtmc.test; - -/** - * JMeasureRunner - */ -public class JMeasureRunner { - -} \ No newline at end of file diff --git a/measure-test-framework/src/main/java/org/jtmc/test/scpi/SCPIScriptBasicRunner.java b/measure-test-framework/src/main/java/org/jtmc/test/scpi/SCPIScriptBasicRunner.java deleted file mode 100644 index 7877431..0000000 --- a/measure-test-framework/src/main/java/org/jtmc/test/scpi/SCPIScriptBasicRunner.java +++ /dev/null @@ -1,5 +0,0 @@ -package org.jtmc.test.scpi; - -public class SCPIScriptBasicRunner { - -} \ No newline at end of file