├── softplc
├── src
│ ├── test
│ │ ├── resources
│ │ │ ├── cl
│ │ │ │ ├── file1.jar
│ │ │ │ └── file2.jar
│ │ │ ├── program
│ │ │ │ ├── invalid.js
│ │ │ │ ├── valid.js
│ │ │ │ ├── main.js
│ │ │ │ └── main_translated.js
│ │ │ ├── file1.snapshot
│ │ │ ├── composite.xml
│ │ │ ├── config.xml
│ │ │ └── softplc.dtd
│ │ └── java
│ │ │ └── de
│ │ │ └── peteral
│ │ │ └── softplc
│ │ │ ├── reflection
│ │ │ ├── TestInterface.java
│ │ │ ├── TestClass1.java
│ │ │ ├── TestClass2.java
│ │ │ ├── TestAnnotation.java
│ │ │ └── AnnotationProcessorTest.java
│ │ │ ├── classloader
│ │ │ └── FolderClassLoaderTest.java
│ │ │ ├── symbol
│ │ │ └── SymbolTableImplTest.java
│ │ │ ├── main
│ │ │ └── JavascriptTest.java
│ │ │ ├── comm
│ │ │ ├── common
│ │ │ │ ├── ChangeRequestTest.java
│ │ │ │ ├── ServerDataEventTest.java
│ │ │ │ └── ClientChannelCacheTest.java
│ │ │ ├── PutGetServerImplTest.java
│ │ │ └── RequestWorkerTest.java
│ │ │ ├── program
│ │ │ ├── PrecompilerTest.java
│ │ │ └── ProgramImplTest.java
│ │ │ ├── datatype
│ │ │ ├── BCDTest.java
│ │ │ └── DataTypeFactoryTest.java
│ │ │ ├── memorytables
│ │ │ ├── MemoryTableWriteTaskTest.java
│ │ │ └── MemoryTableUpdateTaskTest.java
│ │ │ ├── view
│ │ │ ├── ApplicationControllerTest.java
│ │ │ └── JavaFXThreadingRule.java
│ │ │ ├── memory
│ │ │ ├── MemoryAreaImplTest.java
│ │ │ └── MemoryIntegrationTest.java
│ │ │ ├── address
│ │ │ └── ParsedAddressTest.java
│ │ │ ├── plc
│ │ │ └── PlcImplTest.java
│ │ │ ├── transformer
│ │ │ └── PlcTransformerTest.java
│ │ │ ├── factory
│ │ │ └── PlcFactoryTest.java
│ │ │ └── cpu
│ │ │ └── CpuImplTest.java
│ └── main
│ │ ├── resources
│ │ ├── script
│ │ │ └── main.js
│ │ └── images
│ │ │ └── softplc_32.png
│ │ └── java
│ │ └── de
│ │ └── peteral
│ │ └── softplc
│ │ ├── model
│ │ ├── PutGetServerEvent.java
│ │ ├── CpuStatus.java
│ │ ├── PutGetServerObserver.java
│ │ ├── SoftplcResponseFactory.java
│ │ ├── CommunicationTask.java
│ │ ├── MemoryAccessViolationException.java
│ │ ├── SymbolTable.java
│ │ ├── ProgramCycleObserver.java
│ │ ├── ErrorLog.java
│ │ ├── ResponseFactory.java
│ │ ├── MemoryTable.java
│ │ ├── Symbol.java
│ │ ├── ScriptFile.java
│ │ ├── PutGetServer.java
│ │ ├── NetworkInterface.java
│ │ ├── Program.java
│ │ ├── Plc.java
│ │ ├── Converter.java
│ │ ├── ErrorLogEntry.java
│ │ ├── MemoryTableVariable.java
│ │ ├── MemorySnapshot.java
│ │ ├── MemoryArea.java
│ │ ├── Cpu.java
│ │ └── Memory.java
│ │ ├── datatype
│ │ ├── ConverterException.java
│ │ ├── DataTypeUtils.java
│ │ ├── DataTypeException.java
│ │ ├── BCD.java
│ │ ├── ByteConverter.java
│ │ ├── IntConverter.java
│ │ ├── DIntConverter.java
│ │ ├── WordConverter.java
│ │ ├── RealConverter.java
│ │ ├── S7StringConverter.java
│ │ ├── DwordConverter.java
│ │ ├── StringConverter.java
│ │ └── DateConverter.java
│ │ ├── protocol
│ │ ├── Protocol.java
│ │ ├── CommunicationTask.java
│ │ ├── ProtocolDefinition.java
│ │ ├── ResponseFactory.java
│ │ └── TaskFactory.java
│ │ ├── address
│ │ ├── AddressParserFactory.java
│ │ ├── AddressParserException.java
│ │ └── ParsedAddress.java
│ │ ├── executor
│ │ └── ScheduledThreadPoolExecutorFactory.java
│ │ ├── factory
│ │ └── PlcFactoryException.java
│ │ ├── view
│ │ ├── error
│ │ │ └── ErrorDialog.java
│ │ ├── AddMemoryAreaRangeDialogController.java
│ │ ├── AddMemoryAreaRangeDialog.fxml
│ │ ├── Application.fxml
│ │ ├── CpuTableView.fxml
│ │ └── ApplicationController.java
│ │ ├── serializer
│ │ ├── MemoryAreaData.java
│ │ └── MemorySerializer.java
│ │ ├── symbol
│ │ └── SymbolTableImpl.java
│ │ ├── memorytables
│ │ ├── MemoryTableWriteTask.java
│ │ └── MemoryTableUpdateTask.java
│ │ ├── cpu
│ │ └── ErrorLogImpl.java
│ │ ├── program
│ │ └── Precompiler.java
│ │ ├── comm
│ │ ├── common
│ │ │ ├── ServerDataEvent.java
│ │ │ ├── ChangeRequest.java
│ │ │ └── ClientChannelCache.java
│ │ ├── tasks
│ │ │ ├── AbstractCommunicationTask.java
│ │ │ └── CommunicationTaskFactory.java
│ │ ├── NetworkInterfaceImpl.java
│ │ └── RequestWorker.java
│ │ ├── classloader
│ │ └── FolderClassLoader.java
│ │ ├── file
│ │ ├── FileUtil.java
│ │ └── FileManager.java
│ │ ├── plc
│ │ └── PlcImpl.java
│ │ ├── memory
│ │ └── MemoryAreaImpl.java
│ │ ├── SoftplcApplication.java
│ │ ├── reflection
│ │ └── AnnotationProcessor.java
│ │ └── transformer
│ │ └── PlcTransformer.java
├── protocols
│ └── .gitignore
├── build
│ ├── .gitignore
│ └── resources
│ │ ├── Softplc.ico
│ │ └── logging.properties
├── softplc.asta
├── images
│ ├── menu-file.png
│ ├── menu-view.png
│ ├── overview.png
│ ├── about-dialog.png
│ ├── softplc-cfg.png
│ ├── symbol-table.png
│ ├── table-cpus.png
│ ├── errorlog-table.png
│ ├── program-table.png
│ ├── add-range-dialog.png
│ ├── context-menu-cpu.png
│ ├── snapshots-table.png
│ ├── installation-folder.png
│ ├── memory-config-table.png
│ ├── memory-tables-table.png
│ ├── context-menu-program.png
│ ├── context-menu-snapshots.png
│ ├── context-menu-memory-config.png
│ ├── context-menu-memory-tables.png
│ ├── context-menu-symbol-table.png
│ └── context-menu-memory-variables.png
├── captures
│ ├── telegrams.xlsx
│ ├── readBytes.pcapng
│ ├── writeBits.pcapng
│ ├── invalidSlot.pcapng
│ └── writeBytes.pcapng
├── .gitignore
├── build.fxbuild
├── LICENCE
├── softplc.dtd
├── pom.xml
└── logging.properties
└── README.md
/softplc/src/test/resources/cl/file1.jar:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/softplc/src/test/resources/cl/file2.jar:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/softplc/src/test/resources/program/invalid.js:
--------------------------------------------------------------------------------
1 | +ü31ü+13ü
--------------------------------------------------------------------------------
/softplc/src/main/resources/script/main.js:
--------------------------------------------------------------------------------
1 | main = function() {
2 |
3 | }
--------------------------------------------------------------------------------
/softplc/protocols/.gitignore:
--------------------------------------------------------------------------------
1 | /connector.plc.ra.virtualplc-0.0.1-SNAPSHOT.jar
2 |
--------------------------------------------------------------------------------
/softplc/build/.gitignore:
--------------------------------------------------------------------------------
1 | /build/
2 | /deploy/
3 | /dist/
4 | /externalLibs/
5 | /project/
6 |
--------------------------------------------------------------------------------
/softplc/softplc.asta:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/peteral/softplc/HEAD/softplc/softplc.asta
--------------------------------------------------------------------------------
/softplc/src/test/resources/program/valid.js:
--------------------------------------------------------------------------------
1 | function main() {
2 | ${"M,W100"} = 10;
3 | }
4 |
--------------------------------------------------------------------------------
/softplc/images/menu-file.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/peteral/softplc/HEAD/softplc/images/menu-file.png
--------------------------------------------------------------------------------
/softplc/images/menu-view.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/peteral/softplc/HEAD/softplc/images/menu-view.png
--------------------------------------------------------------------------------
/softplc/images/overview.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/peteral/softplc/HEAD/softplc/images/overview.png
--------------------------------------------------------------------------------
/softplc/captures/telegrams.xlsx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/peteral/softplc/HEAD/softplc/captures/telegrams.xlsx
--------------------------------------------------------------------------------
/softplc/images/about-dialog.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/peteral/softplc/HEAD/softplc/images/about-dialog.png
--------------------------------------------------------------------------------
/softplc/images/softplc-cfg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/peteral/softplc/HEAD/softplc/images/softplc-cfg.png
--------------------------------------------------------------------------------
/softplc/images/symbol-table.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/peteral/softplc/HEAD/softplc/images/symbol-table.png
--------------------------------------------------------------------------------
/softplc/images/table-cpus.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/peteral/softplc/HEAD/softplc/images/table-cpus.png
--------------------------------------------------------------------------------
/softplc/captures/readBytes.pcapng:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/peteral/softplc/HEAD/softplc/captures/readBytes.pcapng
--------------------------------------------------------------------------------
/softplc/captures/writeBits.pcapng:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/peteral/softplc/HEAD/softplc/captures/writeBits.pcapng
--------------------------------------------------------------------------------
/softplc/images/errorlog-table.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/peteral/softplc/HEAD/softplc/images/errorlog-table.png
--------------------------------------------------------------------------------
/softplc/images/program-table.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/peteral/softplc/HEAD/softplc/images/program-table.png
--------------------------------------------------------------------------------
/softplc/build/resources/Softplc.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/peteral/softplc/HEAD/softplc/build/resources/Softplc.ico
--------------------------------------------------------------------------------
/softplc/captures/invalidSlot.pcapng:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/peteral/softplc/HEAD/softplc/captures/invalidSlot.pcapng
--------------------------------------------------------------------------------
/softplc/captures/writeBytes.pcapng:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/peteral/softplc/HEAD/softplc/captures/writeBytes.pcapng
--------------------------------------------------------------------------------
/softplc/images/add-range-dialog.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/peteral/softplc/HEAD/softplc/images/add-range-dialog.png
--------------------------------------------------------------------------------
/softplc/images/context-menu-cpu.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/peteral/softplc/HEAD/softplc/images/context-menu-cpu.png
--------------------------------------------------------------------------------
/softplc/images/snapshots-table.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/peteral/softplc/HEAD/softplc/images/snapshots-table.png
--------------------------------------------------------------------------------
/softplc/images/installation-folder.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/peteral/softplc/HEAD/softplc/images/installation-folder.png
--------------------------------------------------------------------------------
/softplc/images/memory-config-table.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/peteral/softplc/HEAD/softplc/images/memory-config-table.png
--------------------------------------------------------------------------------
/softplc/images/memory-tables-table.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/peteral/softplc/HEAD/softplc/images/memory-tables-table.png
--------------------------------------------------------------------------------
/softplc/images/context-menu-program.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/peteral/softplc/HEAD/softplc/images/context-menu-program.png
--------------------------------------------------------------------------------
/softplc/images/context-menu-snapshots.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/peteral/softplc/HEAD/softplc/images/context-menu-snapshots.png
--------------------------------------------------------------------------------
/softplc/src/test/resources/file1.snapshot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/peteral/softplc/HEAD/softplc/src/test/resources/file1.snapshot
--------------------------------------------------------------------------------
/softplc/images/context-menu-memory-config.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/peteral/softplc/HEAD/softplc/images/context-menu-memory-config.png
--------------------------------------------------------------------------------
/softplc/images/context-menu-memory-tables.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/peteral/softplc/HEAD/softplc/images/context-menu-memory-tables.png
--------------------------------------------------------------------------------
/softplc/images/context-menu-symbol-table.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/peteral/softplc/HEAD/softplc/images/context-menu-symbol-table.png
--------------------------------------------------------------------------------
/softplc/images/context-menu-memory-variables.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/peteral/softplc/HEAD/softplc/images/context-menu-memory-variables.png
--------------------------------------------------------------------------------
/softplc/src/main/resources/images/softplc_32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/peteral/softplc/HEAD/softplc/src/main/resources/images/softplc_32.png
--------------------------------------------------------------------------------
/softplc/.gitignore:
--------------------------------------------------------------------------------
1 | /target/
2 | /softplc.asta.bak
3 | /.classpath
4 | /.project
5 | /.settings/
6 | /dependency-reduced-pom.xml
7 | /softplc.log.*
8 | /.idea
9 | /softplc.iml
--------------------------------------------------------------------------------
/softplc/src/test/resources/program/main.js:
--------------------------------------------------------------------------------
1 | function main() {
2 | ${"M,W100"} = ${"M,W100"}.intValue() + 1;
3 |
4 | logger.info("New value: " + ${"M,W100"});
5 | }
6 |
--------------------------------------------------------------------------------
/softplc/src/test/resources/composite.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/softplc/src/test/java/de/peteral/softplc/reflection/TestInterface.java:
--------------------------------------------------------------------------------
1 | package de.peteral.softplc.reflection;
2 |
3 | @SuppressWarnings("javadoc")
4 | public interface TestInterface {
5 |
6 | }
7 |
--------------------------------------------------------------------------------
/softplc/src/test/resources/program/main_translated.js:
--------------------------------------------------------------------------------
1 | function main() {
2 | memory.write("M,W100", memory.read("M,W100").intValue() + 1);
3 |
4 | logger.info("New value: " + memory.read("M,W100"));
5 | }
6 |
--------------------------------------------------------------------------------
/softplc/src/test/java/de/peteral/softplc/reflection/TestClass1.java:
--------------------------------------------------------------------------------
1 | package de.peteral.softplc.reflection;
2 |
3 | @SuppressWarnings("javadoc")
4 | @TestAnnotation(priority = 10)
5 | public class TestClass1 implements TestInterface {
6 |
7 | }
8 |
--------------------------------------------------------------------------------
/softplc/src/test/java/de/peteral/softplc/reflection/TestClass2.java:
--------------------------------------------------------------------------------
1 | package de.peteral.softplc.reflection;
2 |
3 | @SuppressWarnings("javadoc")
4 | @TestAnnotation(priority = 20)
5 | public class TestClass2 implements TestInterface {
6 |
7 | }
8 |
--------------------------------------------------------------------------------
/softplc/src/main/java/de/peteral/softplc/model/PutGetServerEvent.java:
--------------------------------------------------------------------------------
1 | package de.peteral.softplc.model;
2 |
3 | /**
4 | * Contains information about one interaction between {@link NetworkInterface} and a
5 | * client.
6 | *
7 | * Implement {@link PutGetServerObserver} in order to receive this information.
8 | *
9 | * @author peteral
10 | *
11 | */
12 | public class PutGetServerEvent {
13 |
14 | }
15 |
--------------------------------------------------------------------------------
/softplc/src/main/java/de/peteral/softplc/datatype/ConverterException.java:
--------------------------------------------------------------------------------
1 | package de.peteral.softplc.datatype;
2 |
3 | /**
4 | * @author peteral
5 | *
6 | */
7 | public class ConverterException extends RuntimeException {
8 | private static final long serialVersionUID = 1L;
9 |
10 | /**
11 | * Create new instance.
12 | *
13 | * @param message
14 | */
15 | public ConverterException(String message) {
16 | super(message);
17 | }
18 |
19 | }
20 |
--------------------------------------------------------------------------------
/softplc/src/main/java/de/peteral/softplc/model/CpuStatus.java:
--------------------------------------------------------------------------------
1 | package de.peteral.softplc.model;
2 |
3 | /**
4 | * Defines the possible states the {@link Cpu} can enter.
5 | *
6 | * @author peteral
7 | *
8 | */
9 | public enum CpuStatus {
10 | /**
11 | * Invalid program.
12 | */
13 | ERROR,
14 |
15 | /**
16 | * Running
17 | */
18 | RUN,
19 |
20 | /**
21 | * Stopped - program execution interrupted by user
22 | */
23 | STOP;
24 | }
25 |
--------------------------------------------------------------------------------
/softplc/build.fxbuild:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/softplc/src/test/java/de/peteral/softplc/reflection/TestAnnotation.java:
--------------------------------------------------------------------------------
1 | package de.peteral.softplc.reflection;
2 |
3 | import java.lang.annotation.ElementType;
4 | import java.lang.annotation.Retention;
5 | import java.lang.annotation.RetentionPolicy;
6 | import java.lang.annotation.Target;
7 |
8 | @SuppressWarnings("javadoc")
9 | @Retention(RetentionPolicy.RUNTIME)
10 | @Target(ElementType.TYPE)
11 | public @interface TestAnnotation {
12 | int priority() default 0;
13 | }
14 |
--------------------------------------------------------------------------------
/softplc/src/main/java/de/peteral/softplc/datatype/DataTypeUtils.java:
--------------------------------------------------------------------------------
1 | package de.peteral.softplc.datatype;
2 |
3 | /**
4 | * Data type utilities.
5 | *
6 | * @author peteral
7 | *
8 | */
9 | public final class DataTypeUtils {
10 |
11 | private DataTypeUtils() {
12 |
13 | }
14 |
15 | /**
16 | * Converts unsigned 8 bit stored within byte into integer value.
17 | *
18 | * @param b
19 | * @return integer
20 | */
21 | public static int byteToInt(byte b) {
22 | return (b < 0) ? 128 + (b & 0x7F) : b;
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/softplc/src/main/java/de/peteral/softplc/protocol/Protocol.java:
--------------------------------------------------------------------------------
1 | package de.peteral.softplc.protocol;
2 |
3 | import java.lang.annotation.ElementType;
4 | import java.lang.annotation.Retention;
5 | import java.lang.annotation.RetentionPolicy;
6 | import java.lang.annotation.Target;
7 |
8 | /**
9 | * Marks a class as a protocol definition. Must implement
10 | * {@link ProtocolDefinition} interface,
11 | *
12 | * @author peteral
13 | */
14 | @Target(ElementType.TYPE)
15 | @Retention(RetentionPolicy.RUNTIME)
16 | public @interface Protocol {
17 | }
18 |
--------------------------------------------------------------------------------
/softplc/src/main/java/de/peteral/softplc/address/AddressParserFactory.java:
--------------------------------------------------------------------------------
1 | package de.peteral.softplc.address;
2 |
3 | /**
4 | * {@link ParsedAddress} factory for easier unit testing.
5 | *
6 | * @author peteral
7 | *
8 | */
9 | public class AddressParserFactory {
10 |
11 | /**
12 | * Parses the address.
13 | *
14 | * @param address
15 | * address to be parsed.
16 | * @return {@link ParsedAddress} instance according the address.
17 | */
18 | public ParsedAddress parse(String address) {
19 | return new ParsedAddress(address);
20 | }
21 |
22 | }
23 |
--------------------------------------------------------------------------------
/softplc/src/main/java/de/peteral/softplc/model/PutGetServerObserver.java:
--------------------------------------------------------------------------------
1 | package de.peteral.softplc.model;
2 |
3 | /**
4 | * Classes implementing this interface can register with the
5 | * {@link NetworkInterface} and receive information about data exchange with
6 | * clients.
7 | *
8 | * @author peteral
9 | *
10 | */
11 | public interface PutGetServerObserver {
12 | /**
13 | * Invoked after each processed telegram processed.
14 | *
15 | * @param e
16 | * event containing information about the data exchange.
17 | */
18 | void onTelegram(PutGetServerEvent e);
19 | }
20 |
--------------------------------------------------------------------------------
/softplc/src/main/java/de/peteral/softplc/address/AddressParserException.java:
--------------------------------------------------------------------------------
1 | package de.peteral.softplc.address;
2 |
3 | /**
4 | * This exception is thrown by the {@link AddressParserFactory} in case of an
5 | * invalid address.
6 | *
7 | * @author peteral
8 | */
9 | public class AddressParserException extends RuntimeException {
10 |
11 | private static final long serialVersionUID = 1L;
12 |
13 | /**
14 | * Creates a new instance.
15 | *
16 | *
17 | * @param address
18 | * requested address
19 | */
20 | public AddressParserException(String address) {
21 | super("Invalid address: [" + address + "]");
22 | }
23 |
24 | }
25 |
--------------------------------------------------------------------------------
/softplc/src/main/java/de/peteral/softplc/model/SoftplcResponseFactory.java:
--------------------------------------------------------------------------------
1 | package de.peteral.softplc.model;
2 |
3 | import java.lang.annotation.ElementType;
4 | import java.lang.annotation.Retention;
5 | import java.lang.annotation.RetentionPolicy;
6 | import java.lang.annotation.Target;
7 |
8 | /**
9 | * Marks a class as a response factory. Must implement {@link ResponseFactory}
10 | * interface,
11 | *
12 | * @author peteral
13 | */
14 | @Target(ElementType.TYPE)
15 | @Retention(RetentionPolicy.RUNTIME)
16 | public @interface SoftplcResponseFactory {
17 | /**
18 | *
19 | * @return priority of this factory 0 = lowest
20 | */
21 | int priority() default 0;
22 | }
23 |
--------------------------------------------------------------------------------
/softplc/src/main/java/de/peteral/softplc/executor/ScheduledThreadPoolExecutorFactory.java:
--------------------------------------------------------------------------------
1 | package de.peteral.softplc.executor;
2 |
3 | import java.util.concurrent.ScheduledThreadPoolExecutor;
4 |
5 | /**
6 | * An shut down executor cannot be reused. We need to create a new instance upon
7 | * every CPU start.
8 | *
9 | * This factory allows testability.
10 | *
11 | * @author peteral
12 | *
13 | */
14 | public class ScheduledThreadPoolExecutorFactory {
15 |
16 | /**
17 | *
18 | * @return new {@link ScheduledThreadPoolExecutor} for cpu execution
19 | */
20 | public ScheduledThreadPoolExecutor createExecutor() {
21 | return new ScheduledThreadPoolExecutor(1);
22 | }
23 |
24 | }
25 |
--------------------------------------------------------------------------------
/softplc/src/main/java/de/peteral/softplc/datatype/DataTypeException.java:
--------------------------------------------------------------------------------
1 | package de.peteral.softplc.datatype;
2 |
3 | /**
4 | * Exception thrown by {@link DataTypeFactory} when invalid data type is
5 | * requested.
6 | *
7 | * @author peteral
8 | *
9 | */
10 | public class DataTypeException extends RuntimeException {
11 |
12 | private static final long serialVersionUID = 1L;
13 |
14 | /**
15 | * Creates new instance.
16 | *
17 | * @param typeName
18 | * name of the requested type
19 | * @param message
20 | * additional message
21 | */
22 | public DataTypeException(String typeName, String message) {
23 | super("Data type error [" + typeName + "]: " + message);
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/softplc/src/main/java/de/peteral/softplc/model/CommunicationTask.java:
--------------------------------------------------------------------------------
1 | package de.peteral.softplc.model;
2 |
3 | /**
4 | * Defines a task created by {@link PutGetServer} to be executed by {@link Cpu}
5 | * between two {@link Program} cycles.
6 | *
7 | * @author peteral
8 | *
9 | */
10 | public interface CommunicationTask {
11 |
12 | /**
13 | * This method will be invoked by the {@link Cpu}
14 | *
15 | * @param cpu
16 | * {@link Cpu} instance executing this task (for memory
17 | * interaction)
18 | */
19 | void execute(Cpu cpu);
20 |
21 | /**
22 | * Invoked when task requests invalid CPU slot
23 | *
24 | * @param slot
25 | * requested cpu slot
26 | */
27 | void onInvalidCpu(int slot);
28 | }
29 |
--------------------------------------------------------------------------------
/softplc/src/main/java/de/peteral/softplc/model/MemoryAccessViolationException.java:
--------------------------------------------------------------------------------
1 | package de.peteral.softplc.model;
2 |
3 | import de.peteral.softplc.protocol.CommunicationTask;
4 |
5 | /**
6 | * This exception is thrown when the {@link Program} or a
7 | * {@link CommunicationTask} attempts to access invalid memory area.
8 | *
9 | * @author peteral
10 | *
11 | */
12 | public class MemoryAccessViolationException extends RuntimeException {
13 |
14 | private static final long serialVersionUID = 1L;
15 |
16 | /**
17 | * Creates new instance.
18 | *
19 | * @param message
20 | * message describing the origin of this exception.
21 | */
22 | public MemoryAccessViolationException(String message) {
23 | super(message);
24 | }
25 |
26 | }
27 |
--------------------------------------------------------------------------------
/softplc/src/main/java/de/peteral/softplc/model/SymbolTable.java:
--------------------------------------------------------------------------------
1 | package de.peteral.softplc.model;
2 |
3 | import javafx.collections.ObservableList;
4 |
5 | /**
6 | * This interface defines a symbol table of a CPU.
7 | *
8 | * A symbol table translates symbolic names to physical addresses.
9 | *
10 | * @author peteral
11 | *
12 | */
13 | public interface SymbolTable {
14 | /**
15 | *
16 | * @return observable list of all symbols - elements can be added, removed
17 | * and modified
18 | */
19 | ObservableList getAllSymbols();
20 |
21 | /**
22 | * Translates symbolic name to an address
23 | *
24 | * @param name
25 | * symbolic name
26 | * @return hardware address, null when symbol is not defined
27 | */
28 | String getAddress(String name);
29 | }
30 |
--------------------------------------------------------------------------------
/softplc/src/main/java/de/peteral/softplc/datatype/BCD.java:
--------------------------------------------------------------------------------
1 | package de.peteral.softplc.datatype;
2 |
3 | /**
4 | * BCD conversion utility class.
5 | *
6 | * @author peteral
7 | *
8 | */
9 | public class BCD {
10 |
11 | /**
12 | * Converts decimal value to BCD code.
13 | *
14 | * @param value
15 | * only handled as byte!
16 | * @return BCD coded value
17 | */
18 | public static byte toBCD(int value) {
19 | int low = value % 10;
20 | int high = value / 10;
21 | return (byte) ((high << 4) + low);
22 | }
23 |
24 | /**
25 | * Converts BCD coded value to decimal.
26 | *
27 | * @param bcd
28 | * @return decimal value
29 | */
30 | public static int fromBCD(byte bcd) {
31 | int high = (bcd & 0xf0) >> 4;
32 | int low = bcd & 0x0f;
33 |
34 | return (10 * high) + low;
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/softplc/src/main/java/de/peteral/softplc/model/ProgramCycleObserver.java:
--------------------------------------------------------------------------------
1 | package de.peteral.softplc.model;
2 |
3 | /**
4 | * Classes implementing this interface can register to be informed about the
5 | * life cycle of a {@link Program} execution.
6 | *
7 | * @author peteral
8 | */
9 | public interface ProgramCycleObserver {
10 | /**
11 | * Invoked after a program cycle was finished.
12 | */
13 | void afterCycleEnd();
14 |
15 | /**
16 | * Invoked in case of error just before the exception would be thrown.
17 | *
18 | * @param context
19 | * context in which the error happened - for extended logging
20 | *
21 | * @param e
22 | * exception about to be thrown
23 | * @return false - exception has been handled, do not throw it
24 | */
25 | boolean onError(String context, Throwable e);
26 | }
27 |
--------------------------------------------------------------------------------
/softplc/src/main/java/de/peteral/softplc/protocol/CommunicationTask.java:
--------------------------------------------------------------------------------
1 | package de.peteral.softplc.protocol;
2 |
3 | import de.peteral.softplc.model.Cpu;
4 | import de.peteral.softplc.model.Program;
5 | import de.peteral.softplc.model.NetworkInterface;
6 |
7 | /**
8 | * Defines a task created by {@link NetworkInterface} to be executed by {@link Cpu}
9 | * between two {@link Program} cycles.
10 | *
11 | * @author peteral
12 | *
13 | */
14 | public interface CommunicationTask {
15 |
16 | /**
17 | * This method will be invoked by the {@link Cpu}
18 | *
19 | * @param cpu
20 | * {@link Cpu} instance executing this task (for memory
21 | * interaction)
22 | */
23 | void execute(Cpu cpu);
24 |
25 | /**
26 | * Invoked when task requests invalid CPU slot
27 | *
28 | * @param slot
29 | * requested cpu slot
30 | */
31 | void onInvalidCpu(int slot);
32 | }
33 |
--------------------------------------------------------------------------------
/softplc/src/test/java/de/peteral/softplc/classloader/FolderClassLoaderTest.java:
--------------------------------------------------------------------------------
1 | package de.peteral.softplc.classloader;
2 |
3 | import static org.junit.Assert.assertArrayEquals;
4 |
5 | import java.io.File;
6 | import java.net.URISyntaxException;
7 | import java.net.URL;
8 |
9 | import org.junit.Before;
10 | import org.junit.Test;
11 |
12 | @SuppressWarnings("javadoc")
13 | public class FolderClassLoaderTest {
14 |
15 | private FolderClassLoader classLoader;
16 |
17 | @Before
18 | public void setup() throws URISyntaxException {
19 | classLoader = new FolderClassLoader(new File(getClass().getResource("/cl").toURI()));
20 | }
21 |
22 | @Test
23 | public void getURLs_FolderWith2JarFiles_ReturnsCorrectURLs() {
24 | URL[] urls = classLoader.getURLs();
25 |
26 | assertArrayEquals(urls,
27 | new URL[] { getClass().getResource("/cl/file1.jar"), getClass().getResource("/cl/file2.jar"), });
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | softplc
2 | =======
3 |
4 | Simulation of a PLC for integration testing of SCADA systems. Originally planned to make a simulation of Siemens Simatic PLC. However over time decided to make a generic PLC. Network protocols are pluggable via @Protocol annotation.
5 |
6 | My company runs an internal fork with S7 protocol implementation. Planning to implement at least modbus as reference protocol implementation.
7 |
8 | Current functionality:
9 | - multiple configurable CPUs per PLC
10 | - each CPU executes a JavaScript program
11 | - serves pluggable network protocols
12 | - manual access to memory via "variable tables"
13 | - GUI
14 |
15 | See wiki for more information and docs: https://github.com/peteral/softplc/wiki
16 |
17 | Licence: http://opensource.org/licenses/MIT
18 |
19 | Special thanks to:
20 | - https://github.com/
21 | - http://sourceforge.net/projects/libnodave/
22 | - https://www.wireshark.org/
23 | - http://astah.net/editions/community
--------------------------------------------------------------------------------
/softplc/src/main/java/de/peteral/softplc/model/ErrorLog.java:
--------------------------------------------------------------------------------
1 | package de.peteral.softplc.model;
2 |
3 | import java.util.logging.Level;
4 |
5 | import javafx.collections.ObservableList;
6 |
7 | /**
8 | * Each {@link Cpu} posesses an {@link ErrorLog} instance. This is used by
9 | * {@link Memory} and {@link Program} to log problems and helps locate errors in
10 | * user programs.
11 | *
12 | * @author peteral
13 | *
14 | */
15 | public interface ErrorLog {
16 |
17 | /**
18 | * Logs a message.
19 | *
20 | * @param level
21 | * log level
22 | * @param module
23 | * module reporting the error
24 | * @param message
25 | * message text
26 | */
27 | void log(Level level, String module, String message);
28 |
29 | /**
30 | *
31 | * @return observable list of last n logged entries for this CPU
32 | */
33 | ObservableList getEntries();
34 |
35 | }
36 |
--------------------------------------------------------------------------------
/softplc/src/main/java/de/peteral/softplc/model/ResponseFactory.java:
--------------------------------------------------------------------------------
1 | package de.peteral.softplc.model;
2 |
3 | /**
4 | * Creates a byte array to be sent back to a client from a
5 | * {@link CommunicationTask} instance.
6 | *
7 | * @author peteral
8 | *
9 | */
10 | public interface ResponseFactory {
11 | /**
12 | * Signals whether this factory is able to handle the concrete
13 | * {@link CommunicationTask}.
14 | *
15 | * @param task
16 | * task to be checked
17 | * @return true - this {@link ResponseFactory} instance can handle the task
18 | */
19 | boolean canHandle(CommunicationTask task);
20 |
21 | /**
22 | * Creates a byte array to be sent back to a client from a
23 | * {@link CommunicationTask} instance.
24 | *
25 | * @param task
26 | * communication task
27 | * @return byte array to be sent back to a client
28 | */
29 | byte[] createResponse(CommunicationTask task);
30 |
31 | }
32 |
--------------------------------------------------------------------------------
/softplc/src/test/java/de/peteral/softplc/reflection/AnnotationProcessorTest.java:
--------------------------------------------------------------------------------
1 | package de.peteral.softplc.reflection;
2 |
3 | import static org.junit.Assert.assertEquals;
4 | import static org.junit.Assert.assertTrue;
5 |
6 | import java.util.ArrayList;
7 | import java.util.List;
8 |
9 | import org.junit.Test;
10 | import org.reflections.util.ClasspathHelper;
11 |
12 | @SuppressWarnings("javadoc")
13 | public class AnnotationProcessorTest {
14 |
15 | @Test
16 | public void loadAnnotations_TwoClassesInClasspath_ReturnsInstancesInCorrectOrder() {
17 | List result = new ArrayList<>();
18 | new AnnotationProcessor(TestAnnotation.class,
19 | ClasspathHelper.forClass(AnnotationProcessorTest.class))
20 | .loadAnnotations(result);
21 |
22 | assertEquals(2, result.size());
23 |
24 | assertTrue(result.get(0) instanceof TestClass2);
25 | assertTrue(result.get(1) instanceof TestClass1);
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/softplc/src/main/java/de/peteral/softplc/model/MemoryTable.java:
--------------------------------------------------------------------------------
1 | package de.peteral.softplc.model;
2 |
3 | import javafx.beans.property.SimpleStringProperty;
4 | import javafx.beans.property.StringProperty;
5 | import javafx.collections.FXCollections;
6 | import javafx.collections.ObservableList;
7 |
8 | /**
9 | * Memory tables can be configured in UI to observe and interact with PLC
10 | * memory.
11 | *
12 | * @author peteral
13 | *
14 | */
15 | public class MemoryTable {
16 | private final StringProperty name = new SimpleStringProperty();
17 |
18 | private final ObservableList variables = FXCollections
19 | .observableArrayList();
20 |
21 | /**
22 | * @return the variables
23 | */
24 | public ObservableList getVariables() {
25 | return variables;
26 | }
27 |
28 | /**
29 | * @return the name
30 | */
31 | public StringProperty getName() {
32 | return name;
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/softplc/src/main/java/de/peteral/softplc/protocol/ProtocolDefinition.java:
--------------------------------------------------------------------------------
1 | package de.peteral.softplc.protocol;
2 |
3 | import java.util.List;
4 |
5 | /**
6 | * Defines a communication protocol.
7 | *
8 | * @author peteral
9 | *
10 | */
11 | public interface ProtocolDefinition {
12 | /**
13 | *
14 | * @return array containing a list of communication task factories of this
15 | * protocol (byte array -> command)
16 | */
17 | List getTaskFactories();
18 |
19 | /**
20 | *
21 | * @return array containing list of response factories (executed command ->
22 | * byte array)
23 | */
24 | List getResponseFactories();
25 |
26 | /**
27 | *
28 | * @return array containing list of ports this protocol is bound to
29 | */
30 | List getPorts();
31 |
32 | /**
33 | *
34 | * @return protocol name (for showing active protocols)
35 | */
36 | String getName();
37 | }
38 |
--------------------------------------------------------------------------------
/softplc/src/main/java/de/peteral/softplc/protocol/ResponseFactory.java:
--------------------------------------------------------------------------------
1 | package de.peteral.softplc.protocol;
2 |
3 | /**
4 | * Creates a byte array to be sent back to a client from a
5 | * {@link CommunicationTask} instance.
6 | *
7 | * @author peteral
8 | *
9 | */
10 | public interface ResponseFactory {
11 | /**
12 | * Signals whether this factory is able to handle the concrete
13 | * {@link CommunicationTask}.
14 | *
15 | * @param task
16 | * task to be checked
17 | * @return true - this {@link ResponseFactory} instance can handle the task
18 | */
19 | boolean canHandle(CommunicationTask task);
20 |
21 | /**
22 | * Creates a byte array to be sent back to a client from a
23 | * {@link CommunicationTask} instance.
24 | *
25 | * @param task
26 | * communication task
27 | * @return byte array to be sent back to a client
28 | */
29 | byte[] createResponse(CommunicationTask task);
30 |
31 | }
32 |
--------------------------------------------------------------------------------
/softplc/src/test/java/de/peteral/softplc/symbol/SymbolTableImplTest.java:
--------------------------------------------------------------------------------
1 | package de.peteral.softplc.symbol;
2 |
3 | import static org.junit.Assert.assertEquals;
4 | import static org.junit.Assert.assertNull;
5 |
6 | import org.junit.Before;
7 | import org.junit.Test;
8 |
9 | import de.peteral.softplc.model.Symbol;
10 |
11 | @SuppressWarnings("javadoc")
12 | public class SymbolTableImplTest {
13 |
14 | private static final String SYMBOL = "symbol";
15 | private static final String ADDRESS = "address";
16 | private SymbolTableImpl table;
17 |
18 | @Before
19 | public void setup() {
20 | table = new SymbolTableImpl();
21 | }
22 |
23 | @Test
24 | public void getAddress_SymbolDefined_ReturnsAddress() {
25 | table.getAllSymbols().add(new Symbol(SYMBOL, ADDRESS));
26 |
27 | assertEquals(ADDRESS, table.getAddress(SYMBOL));
28 | }
29 |
30 | @Test
31 | public void getAddress_SymbolUndefined_ReturnsNull() {
32 | assertNull(table.getAddress(SYMBOL));
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/softplc/src/main/java/de/peteral/softplc/factory/PlcFactoryException.java:
--------------------------------------------------------------------------------
1 | package de.peteral.softplc.factory;
2 |
3 | /**
4 | * This exception is thrown by {@link PlcFactory} in case of an invalid
5 | * configuration file.
6 | *
7 | * @author peteral
8 | *
9 | */
10 | public class PlcFactoryException extends RuntimeException {
11 |
12 | private static final long serialVersionUID = 1L;
13 |
14 | /**
15 | * Creates a new instance.
16 | *
17 | * @param path
18 | * configuration file
19 | * @param reason
20 | * causing exception
21 | */
22 | public PlcFactoryException(String path, Throwable reason) {
23 | super("Failed parsing configuration [" + path + "]: ", reason);
24 | }
25 |
26 | /**
27 | * Creates instance with additional information.
28 | *
29 | * @param path
30 | * @param reason
31 | */
32 | public PlcFactoryException(String path, String reason) {
33 | super("Failed parsing configuration [" + path + "]: " + reason);
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/softplc/src/main/java/de/peteral/softplc/view/error/ErrorDialog.java:
--------------------------------------------------------------------------------
1 | package de.peteral.softplc.view.error;
2 |
3 | import java.util.logging.Level;
4 | import java.util.logging.Logger;
5 |
6 | import javafx.scene.control.Alert;
7 | import javafx.scene.control.Alert.AlertType;
8 |
9 | /**
10 | * Dialog for displaying exceptions.
11 | *
12 | * @author peteral
13 | *
14 | */
15 | public final class ErrorDialog {
16 |
17 | private ErrorDialog() {
18 | }
19 |
20 | /**
21 | * Shows the error dialog
22 | *
23 | * @param message
24 | * additional message
25 | * @param reason
26 | * error description
27 | */
28 | public static void show(String message, Throwable reason) {
29 | Logger.getLogger("global").log(Level.WARNING, message, reason);
30 |
31 | Alert alert = new Alert(AlertType.ERROR);
32 | alert.setTitle("Error");
33 | alert.setHeaderText(message);
34 | alert.setContentText((reason == null) ? "" : reason.getMessage());
35 |
36 | alert.showAndWait();
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/softplc/src/main/java/de/peteral/softplc/serializer/MemoryAreaData.java:
--------------------------------------------------------------------------------
1 | package de.peteral.softplc.serializer;
2 |
3 | import java.io.Serializable;
4 |
5 | /**
6 | * Serializable memory area reprezentation to be saved in a memory snapshot.
7 | *
8 | * @author peteral
9 | *
10 | */
11 | public class MemoryAreaData implements Serializable {
12 |
13 | private static final long serialVersionUID = 1L;
14 | private final String areaCode;
15 | private final byte[] bytes;
16 |
17 | /**
18 | * Default constructor
19 | *
20 | * @param areaCode
21 | * memory area name
22 | * @param bytes
23 | * memory area content
24 | */
25 | public MemoryAreaData(String areaCode, byte[] bytes) {
26 | this.areaCode = areaCode;
27 | this.bytes = bytes;
28 | }
29 |
30 | /**
31 | * @return the areaCode
32 | */
33 | public String getAreaCode() {
34 | return areaCode;
35 | }
36 |
37 | /**
38 | * @return the bytes
39 | */
40 | public byte[] getBytes() {
41 | return bytes;
42 | }
43 |
44 | }
45 |
--------------------------------------------------------------------------------
/softplc/src/main/java/de/peteral/softplc/datatype/ByteConverter.java:
--------------------------------------------------------------------------------
1 | package de.peteral.softplc.datatype;
2 |
3 | import de.peteral.softplc.address.ParsedAddress;
4 | import de.peteral.softplc.model.Converter;
5 |
6 | /**
7 | * Converter implementation for byte (8 bit unsigned) conversion.
8 | *
9 | * @author peteral
10 | */
11 | public class ByteConverter implements Converter {
12 |
13 | @Override
14 | public Number[] createArray(int count) {
15 | return new Integer[count];
16 | }
17 |
18 | @Override
19 | public void toBytes(Number value, ParsedAddress address, byte[] buffer,
20 | int offset) {
21 |
22 | buffer[offset] = value.byteValue();
23 | }
24 |
25 | @Override
26 | public Number fromBytes(byte[] bytes, ParsedAddress address, int offset) {
27 | return Integer.valueOf(DataTypeUtils.byteToInt(bytes[offset]));
28 | }
29 |
30 | @Override
31 | public void parseToBytes(String value, ParsedAddress address,
32 | byte[] buffer, int offset) {
33 |
34 | toBytes(Integer.parseInt(value), address, buffer, offset);
35 | }
36 |
37 | }
38 |
--------------------------------------------------------------------------------
/softplc/src/main/java/de/peteral/softplc/symbol/SymbolTableImpl.java:
--------------------------------------------------------------------------------
1 | package de.peteral.softplc.symbol;
2 |
3 | import de.peteral.softplc.model.Symbol;
4 | import de.peteral.softplc.model.SymbolTable;
5 | import javafx.collections.FXCollections;
6 | import javafx.collections.ObservableList;
7 |
8 | /**
9 | * {@link SymbolTable} implementation.
10 | *
11 | * not thread safe
12 | *
13 | * @author peteral
14 | *
15 | */
16 | // TODO - possible optimization - listen to symbols list changes and update a
17 | // map, use this map to resolve addresses
18 | public class SymbolTableImpl implements SymbolTable {
19 | private final ObservableList symbols = FXCollections.observableArrayList();
20 |
21 | @Override
22 | public ObservableList getAllSymbols() {
23 | return symbols;
24 | }
25 |
26 | @Override
27 | public String getAddress(String name) {
28 | for (Symbol symbol : symbols) {
29 | if (symbol.getName().get().equals(name)) {
30 | return symbol.getAddress().get();
31 | }
32 | }
33 |
34 | return null;
35 | }
36 |
37 | }
38 |
--------------------------------------------------------------------------------
/softplc/src/test/java/de/peteral/softplc/main/JavascriptTest.java:
--------------------------------------------------------------------------------
1 | package de.peteral.softplc.main;
2 |
3 | import javax.script.Bindings;
4 | import javax.script.Compilable;
5 | import javax.script.CompiledScript;
6 | import javax.script.ScriptContext;
7 | import javax.script.ScriptEngine;
8 | import javax.script.ScriptEngineManager;
9 | import javax.script.ScriptException;
10 | import javax.script.SimpleScriptContext;
11 |
12 | @SuppressWarnings("javadoc")
13 | public class JavascriptTest {
14 |
15 | public static void main(String[] args) throws ScriptException {
16 | ScriptEngineManager manager = new ScriptEngineManager();
17 |
18 | ScriptEngine engine = manager.getEngineByMimeType("text/javascript");
19 | Compilable compiler = (Compilable) engine;
20 |
21 | CompiledScript compiledScript = compiler.compile("a = 20;");
22 |
23 | ScriptContext context = new SimpleScriptContext();
24 | Bindings bindings = context.getBindings(ScriptContext.ENGINE_SCOPE);
25 |
26 | compiledScript.eval(context);
27 |
28 | System.out.println(bindings.get("a"));
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/softplc/src/test/java/de/peteral/softplc/comm/common/ChangeRequestTest.java:
--------------------------------------------------------------------------------
1 | package de.peteral.softplc.comm.common;
2 |
3 | import static org.junit.Assert.assertEquals;
4 |
5 | import java.nio.channels.SocketChannel;
6 |
7 | import org.junit.Before;
8 | import org.junit.Test;
9 | import org.mockito.Mock;
10 | import org.mockito.MockitoAnnotations;
11 |
12 | @SuppressWarnings("javadoc")
13 | public class ChangeRequestTest {
14 | private static final int TYPE = 10;
15 | private static final int OPS = 20;
16 | @Mock
17 | private SocketChannel socket;
18 | private ChangeRequest request;
19 |
20 | @Before
21 | public void setup() {
22 | MockitoAnnotations.initMocks(this);
23 |
24 | request = new ChangeRequest(socket, TYPE, OPS);
25 | }
26 |
27 | @Test
28 | public void getSocket_None_ReturnsSocket() {
29 | assertEquals(socket, request.getSocket());
30 | }
31 |
32 | @Test
33 | public void getType_None_ReturnsType() {
34 | assertEquals(TYPE, request.getType());
35 | }
36 |
37 | @Test
38 | public void getOps_None_ReturnsOps() {
39 | assertEquals(OPS, request.getOps());
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/softplc/src/main/java/de/peteral/softplc/memorytables/MemoryTableWriteTask.java:
--------------------------------------------------------------------------------
1 | package de.peteral.softplc.memorytables;
2 |
3 | import java.util.Arrays;
4 |
5 | import de.peteral.softplc.model.Cpu;
6 | import de.peteral.softplc.model.MemoryTableVariable;
7 | import de.peteral.softplc.protocol.CommunicationTask;
8 |
9 | /**
10 | * Writes memory table variables to memory.
11 | *
12 | * @author peteral
13 | *
14 | */
15 | public class MemoryTableWriteTask implements CommunicationTask {
16 |
17 | private final MemoryTableVariable[] variables;
18 |
19 | /**
20 | * Initializes new instance.
21 | *
22 | * @param variables
23 | * variables to be written
24 | */
25 | public MemoryTableWriteTask(MemoryTableVariable... variables) {
26 | this.variables = variables;
27 | }
28 |
29 | @Override
30 | public void execute(Cpu cpu) {
31 | Arrays.asList(variables).forEach(
32 | variable -> cpu.getMemory().parse(variable.getVariable().get(),
33 | variable.getNewValue().get()));
34 | }
35 |
36 | @Override
37 | public void onInvalidCpu(int slot) {
38 | // nothing to do here
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/softplc/src/main/java/de/peteral/softplc/cpu/ErrorLogImpl.java:
--------------------------------------------------------------------------------
1 | package de.peteral.softplc.cpu;
2 |
3 | import java.util.logging.Level;
4 | import java.util.logging.Logger;
5 |
6 | import javafx.collections.FXCollections;
7 | import javafx.collections.ObservableList;
8 | import de.peteral.softplc.model.ErrorLog;
9 | import de.peteral.softplc.model.ErrorLogEntry;
10 |
11 | /**
12 | * Default {@link ErrorLog} implementation.
13 | *
14 | * @author peteral
15 | *
16 | */
17 | public class ErrorLogImpl implements ErrorLog {
18 | private static final int MAX_ENTRIES = 300;
19 |
20 | private final ObservableList entries = FXCollections
21 | .observableArrayList();
22 |
23 | @Override
24 | public void log(Level level, String module, String message) {
25 | getEntries().add(new ErrorLogEntry(level, module, message));
26 |
27 | while (getEntries().size() > MAX_ENTRIES) {
28 | getEntries().remove(0);
29 | }
30 |
31 | Logger.getLogger(module).log(level, module + ": " + message);
32 | }
33 |
34 | @Override
35 | public ObservableList getEntries() {
36 | return entries;
37 | }
38 |
39 | }
40 |
--------------------------------------------------------------------------------
/softplc/LICENCE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2014 Ladislav Petera
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/softplc/src/main/java/de/peteral/softplc/datatype/IntConverter.java:
--------------------------------------------------------------------------------
1 | package de.peteral.softplc.datatype;
2 |
3 | import de.peteral.softplc.address.ParsedAddress;
4 | import de.peteral.softplc.model.Converter;
5 |
6 | /**
7 | * Converts signed 16-bit integer.
8 | *
9 | * @author peteral
10 | */
11 | public class IntConverter implements Converter {
12 | private final WordConverter wordConverter = new WordConverter();
13 |
14 | @Override
15 | public Number[] createArray(int count) {
16 | return new Short[count];
17 | }
18 |
19 | @Override
20 | public void toBytes(Number value, ParsedAddress address, byte[] buffer,
21 | int offset) {
22 | wordConverter.toBytes(value, address, buffer, offset);
23 | }
24 |
25 | @Override
26 | public Number fromBytes(byte[] bytes, ParsedAddress address, int offset) {
27 | int intValue = (int) wordConverter.fromBytes(bytes, address, offset);
28 |
29 | return (short) intValue;
30 | }
31 |
32 | @Override
33 | public void parseToBytes(String value, ParsedAddress address,
34 | byte[] buffer, int offset) {
35 |
36 | toBytes(Short.parseShort(value), address, buffer, offset);
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/softplc/src/main/java/de/peteral/softplc/model/Symbol.java:
--------------------------------------------------------------------------------
1 | package de.peteral.softplc.model;
2 |
3 | import javafx.beans.property.SimpleStringProperty;
4 | import javafx.beans.property.StringProperty;
5 |
6 | /**
7 | * This class represents an entry in the symbol table of a CPU.
8 | *
9 | * @author peteral
10 | *
11 | */
12 | public class Symbol {
13 | private final StringProperty address = new SimpleStringProperty();
14 | private final StringProperty name = new SimpleStringProperty();
15 |
16 | /**
17 | * Default constructor.
18 | *
19 | * @param name
20 | * symbolic name
21 | * @param address
22 | * address
23 | */
24 | public Symbol(String name, String address) {
25 | getAddress().set(address);
26 | getName().set(name);
27 | }
28 |
29 | /**
30 | * @return the address
31 | */
32 | public StringProperty getAddress() {
33 | return address;
34 | }
35 |
36 | /**
37 | * @return the name
38 | */
39 | public StringProperty getName() {
40 | return name;
41 | }
42 |
43 | @Override
44 | public String toString() {
45 | return getName().get() + " = " + getAddress().get();
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/softplc/src/main/java/de/peteral/softplc/protocol/TaskFactory.java:
--------------------------------------------------------------------------------
1 | package de.peteral.softplc.protocol;
2 |
3 | import de.peteral.softplc.comm.common.ServerDataEvent;
4 | import de.peteral.softplc.comm.tasks.CommunicationTaskFactory;
5 |
6 | /**
7 | * TaskFactory implementations create {@link CommunicationTask} from
8 | * {@link ServerDataEvent}.
9 | *
10 | * @author peteral
11 | *
12 | */
13 | public interface TaskFactory {
14 | /**
15 | * Signals whether this factory instance can handle the event.
16 | *
17 | * @param dataEvent
18 | * event that needs to be translated into
19 | * {@link CommunicationTask}
20 | * @return true - this instance can handle the event
21 | */
22 | boolean canHandle(ServerDataEvent dataEvent);
23 |
24 | /**
25 | * Creates a concrete {@link CommunicationTask} instance from a
26 | * {@link ServerDataEvent}.
27 | *
28 | * @param dataEvent
29 | * server data event
30 | * @param factory
31 | * @return concrete {@link CommunicationTask} instance
32 | */
33 | CommunicationTask createTask(ServerDataEvent dataEvent,
34 | CommunicationTaskFactory factory);
35 |
36 | }
37 |
--------------------------------------------------------------------------------
/softplc/src/test/resources/config.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | ./program/main.js
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 | ./program/main.js
21 |
22 |
23 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/softplc/src/main/java/de/peteral/softplc/datatype/DIntConverter.java:
--------------------------------------------------------------------------------
1 | package de.peteral.softplc.datatype;
2 |
3 | import de.peteral.softplc.address.ParsedAddress;
4 | import de.peteral.softplc.model.Converter;
5 |
6 | /**
7 | * Converter handling 32 bit signed decimals.
8 | *
9 | * @author peteral
10 | */
11 | public class DIntConverter implements Converter {
12 | private final DwordConverter dwordConverter = new DwordConverter();
13 |
14 | @Override
15 | public Number[] createArray(int count) {
16 | return new Integer[count];
17 | }
18 |
19 | @Override
20 | public void toBytes(Number value, ParsedAddress address, byte[] buffer,
21 | int offset) {
22 | dwordConverter.toBytes(value, address, buffer, offset);
23 | }
24 |
25 | @Override
26 | public Number fromBytes(byte[] bytes, ParsedAddress address, int offset) {
27 | long longValue = (long) dwordConverter
28 | .fromBytes(bytes, address, offset);
29 | return (int) longValue;
30 | }
31 |
32 | @Override
33 | public void parseToBytes(String value, ParsedAddress address,
34 | byte[] buffer, int offset) {
35 |
36 | toBytes(Long.parseLong(value), address, buffer, offset);
37 | }
38 |
39 | }
40 |
--------------------------------------------------------------------------------
/softplc/src/test/java/de/peteral/softplc/program/PrecompilerTest.java:
--------------------------------------------------------------------------------
1 | package de.peteral.softplc.program;
2 |
3 | import static org.junit.Assert.assertEquals;
4 |
5 | import java.io.IOException;
6 | import java.nio.file.Files;
7 | import java.nio.file.Paths;
8 |
9 | import org.junit.Before;
10 | import org.junit.Test;
11 | import org.mockito.MockitoAnnotations;
12 |
13 | import de.peteral.softplc.program.Precompiler;
14 |
15 | @SuppressWarnings("javadoc")
16 | public class PrecompilerTest {
17 |
18 | private Precompiler precompiler;
19 |
20 | @Before
21 | public void setup() {
22 | MockitoAnnotations.initMocks(this);
23 |
24 | precompiler = new Precompiler();
25 | }
26 |
27 | @Test
28 | public void translate_ValidFile_ReturnsCorrectScript() throws IOException {
29 | String input = readFile("./src/test/resources/program/main.js");
30 | String output = readFile("./src/test/resources/program/main_translated.js");
31 |
32 | assertEquals(output, precompiler.translate(input));
33 | }
34 |
35 | private String readFile(String filename) throws IOException {
36 | byte[] bytes = Files.readAllBytes(Paths.get(filename));
37 | return new String(bytes);
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/softplc/src/test/java/de/peteral/softplc/datatype/BCDTest.java:
--------------------------------------------------------------------------------
1 | package de.peteral.softplc.datatype;
2 |
3 | import static org.junit.Assert.assertEquals;
4 |
5 | import java.util.Arrays;
6 |
7 | import org.junit.Test;
8 | import org.junit.runner.RunWith;
9 | import org.junit.runners.Parameterized;
10 | import org.junit.runners.Parameterized.Parameters;
11 |
12 | @SuppressWarnings({ "javadoc" })
13 | @RunWith(Parameterized.class)
14 | public class BCDTest {
15 |
16 | private final byte bcd;
17 | private final int value;
18 |
19 | /* @formatter:off */
20 | @Parameters(name = "{index}: {0} = {1}")
21 | public static Iterable