├── .devcontainer
├── Dockerfile
└── devcontainer.json
├── .dockerignore
├── .gitattributes
├── .github
└── workflows
│ ├── docker.yml
│ └── e2e.yml
├── .gitignore
├── .gitmodules
├── .gitpod.yml
├── CMakeLists.txt
├── COPYING
├── Dockerfile
├── Dockerfile.gitpod
├── Procfile.multi
├── Procfile.single
├── README.md
├── config
├── multi-device
│ ├── device-1.xml
│ └── device-2.xml
├── phenix-image.yml
├── single-device
│ ├── device.xml
│ └── node-red-hmi.json
└── wind-turbine
│ ├── blade-1.xml
│ ├── blade-2.xml
│ ├── blade-3.xml
│ ├── main-controller.xml
│ ├── signal-converter.xml
│ └── yaw-controller.xml
├── data
└── weather.csv
├── install-node-red.sh
├── ot-sim.code-workspace
├── src
├── c++
│ ├── .vscode
│ │ └── c_cpp_properties.json
│ ├── CMakeLists.txt
│ ├── cmd
│ │ ├── ot-sim-dnp3-module
│ │ │ ├── CMakeLists.txt
│ │ │ └── main.cpp
│ │ └── ot-sim-e2e-dnp3-master
│ │ │ ├── CMakeLists.txt
│ │ │ ├── handler.hpp
│ │ │ └── main.cpp
│ ├── dnp3
│ │ ├── CMakeLists.txt
│ │ ├── client.cpp
│ │ ├── client.hpp
│ │ ├── common.hpp
│ │ ├── master.cpp
│ │ ├── master.hpp
│ │ ├── outstation.cpp
│ │ ├── outstation.hpp
│ │ ├── server.cpp
│ │ └── server.hpp
│ └── msgbus
│ │ ├── CMakeLists.txt
│ │ ├── envelope.hpp
│ │ ├── metrics.cpp
│ │ ├── metrics.hpp
│ │ ├── pusher.cpp
│ │ ├── pusher.hpp
│ │ ├── subscriber.cpp
│ │ └── subscriber.hpp
├── c
│ ├── .vscode
│ │ └── c_cpp_properties.json
│ ├── CMakeLists.txt
│ └── cmd
│ │ └── ot-sim-message-bus
│ │ ├── CMakeLists.txt
│ │ └── main.c
├── go
│ ├── .gitignore
│ ├── Makefile
│ ├── cmd
│ │ ├── ot-sim-cpu-module
│ │ │ └── main.go
│ │ ├── ot-sim-logic-module
│ │ │ └── main.go
│ │ ├── ot-sim-modbus-module
│ │ │ └── main.go
│ │ ├── ot-sim-mqtt-module
│ │ │ └── main.go
│ │ ├── ot-sim-node-red-module
│ │ │ └── main.go
│ │ ├── ot-sim-sunspec-module
│ │ │ └── main.go
│ │ ├── ot-sim-tailscale-module
│ │ │ └── main.go
│ │ └── ot-sim-telnet-module
│ │ │ └── main.go
│ ├── cpu
│ │ ├── api.go
│ │ ├── context.go
│ │ ├── cpu.go
│ │ ├── execute.go
│ │ ├── internal.go
│ │ ├── metrics.go
│ │ └── monitor.go
│ ├── go.mod
│ ├── go.sum
│ ├── logic
│ │ ├── logic.go
│ │ └── logic_test.go
│ ├── modbus
│ │ ├── client
│ │ │ └── client.go
│ │ ├── modbus.go
│ │ ├── server
│ │ │ ├── coil.go
│ │ │ ├── discrete.go
│ │ │ ├── holding.go
│ │ │ ├── input.go
│ │ │ └── server.go
│ │ └── util
│ │ │ ├── bytes.go
│ │ │ ├── bytes_test.go
│ │ │ ├── register.go
│ │ │ └── register_test.go
│ ├── mqtt
│ │ ├── mqtt.go
│ │ └── types.go
│ ├── msgbus
│ │ ├── envelope.go
│ │ ├── health.go
│ │ ├── metric.go
│ │ ├── module.go
│ │ ├── pusher.go
│ │ ├── runtime.go
│ │ └── subscriber.go
│ ├── nodered
│ │ ├── nodered.go
│ │ └── settings.js.tmpl
│ ├── ot-sim.go
│ ├── staticcheck.conf
│ ├── sunspec
│ │ ├── README.md
│ │ ├── client
│ │ │ ├── client.go
│ │ │ └── util.go
│ │ ├── common
│ │ │ ├── common.go
│ │ │ ├── register.go
│ │ │ ├── schema.go
│ │ │ └── types.go
│ │ ├── server
│ │ │ └── server.go
│ │ └── sunspec.go
│ ├── tailscale
│ │ └── tailscale.go
│ ├── telnet
│ │ ├── banner.go
│ │ ├── modules.go
│ │ └── telnet.go
│ └── util
│ │ ├── context.go
│ │ ├── exit.go
│ │ ├── sigterm
│ │ └── context.go
│ │ └── slice.go
├── js
│ └── node-red
│ │ ├── icons
│ │ └── zeromq.png
│ │ ├── ot-sim.html
│ │ ├── ot-sim.js
│ │ └── package.json
├── old
│ ├── README.md
│ ├── c
│ │ ├── CMakeLists.txt
│ │ ├── Makefile
│ │ ├── cmd
│ │ │ └── ot-sim-io-module
│ │ │ │ ├── CMakeLists.txt
│ │ │ │ └── main.c
│ │ └── msgbus
│ │ │ ├── debugger.c
│ │ │ ├── logger.c
│ │ │ ├── msgbus-ini-example.c
│ │ │ └── msgbus-test.c
│ └── python
│ │ └── dnp3
│ │ ├── client.py
│ │ ├── dnp3.py
│ │ ├── envelope.py
│ │ ├── logger.py
│ │ ├── master.py
│ │ ├── outstation.py
│ │ ├── point.py
│ │ ├── server.py
│ │ └── variations.py
└── python
│ ├── .gitignore
│ ├── otsim
│ ├── __init__.py
│ ├── ground_truth
│ │ ├── __init__.py
│ │ └── ground_truth.py
│ ├── helics_helper
│ │ ├── README.md
│ │ ├── __init__.py
│ │ └── version.py
│ ├── io
│ │ ├── __init__.py
│ │ └── io.py
│ ├── msgbus
│ │ ├── __init__.py
│ │ ├── envelope.py
│ │ ├── metrics.py
│ │ ├── pusher.py
│ │ └── subscriber.py
│ ├── rpi_gpio
│ │ ├── README.md
│ │ ├── __init__.py
│ │ └── rpi_gpio.py
│ └── wind_turbine
│ │ ├── __init__.py
│ │ ├── anemometer
│ │ ├── __init__.py
│ │ └── anemometer.py
│ │ └── power_output
│ │ ├── __init__.py
│ │ └── power_output.py
│ └── setup.py
└── testing
├── dnp3
├── master.py
└── visitors.py
└── e2e
├── Procfile
├── README.md
├── configs
├── device-1.xml
└── device-2.xml
└── helics
├── broker.py
├── data
└── IEEE13
│ ├── IEEE13Node_BusXY.csv
│ ├── IEEE13Nodeckt.dss
│ ├── IEEELineCodes.dss
│ └── LoadShape1.csv
└── opendss-federate.py
/.devcontainer/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM debian:bookworm
2 |
3 | ENV TZ=America/Denver
4 | RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
5 |
6 | RUN apt update && apt install -y \
7 | bash-completion build-essential cmake curl git git-lfs mbpoll sudo tmux tree vim wget xz-utils \
8 | cmake libboost-dev libczmq-dev libxml2-dev libzmq3-dev pkg-config python3-dev python3-pip
9 |
10 | ARG USERNAME=vscode
11 | ARG USER_UID=1000
12 |
13 | RUN useradd -l -u $USER_UID -md /home/$USERNAME -s /bin/bash -p $USERNAME $USERNAME \
14 | && echo $USERNAME ALL=\(root\) NOPASSWD:ALL > /etc/sudoers.d/$USERNAME \
15 | && chmod 0440 /etc/sudoers.d/$USERNAME
16 |
17 | ADD https://raw.githubusercontent.com/git/git/master/contrib/completion/git-prompt.sh /home/$USERNAME/.bash/git-prompt.sh
18 | RUN chown $USERNAME:$USERNAME /home/$USERNAME/.bash/git-prompt.sh \
19 | && chmod +x /home/$USERNAME/.bash/git-prompt.sh \
20 | && echo "\nsource ~/.bash/git-prompt.sh" >> /home/$USERNAME/.bashrc \
21 | && echo "export GIT_PS1_SHOWCOLORHINTS=true" >> /home/$USERNAME/.bashrc \
22 | && echo "export PROMPT_COMMAND='__git_ps1 \"\W\" \" » \"'" >> /home/$USERNAME/.bashrc
23 |
24 | ARG GOLANG_VERSION=1.21.1
25 |
26 | RUN wget -O go.tgz https://golang.org/dl/go${GOLANG_VERSION}.linux-amd64.tar.gz \
27 | && tar -C /usr/local -xzf go.tgz && rm go.tgz \
28 | && ln -s /usr/local/go/bin/* /usr/local/bin
29 |
30 | ENV GOPATH /go
31 | ENV PATH $GOPATH/bin:$PATH
32 |
33 | RUN mkdir -p "$GOPATH/src" "$GOPATH/bin" \
34 | && chmod -R 777 "$GOPATH"
35 |
36 | RUN go install github.com/ramya-rao-a/go-outline@latest 2>&1
37 | RUN go install github.com/mdempsky/gocode@latest 2>&1
38 | RUN go install github.com/stamblerre/gocode@latest 2>&1
39 | RUN go install github.com/rogpeppe/godef@latest 2>&1
40 | RUN go install github.com/uudashr/gopkgs/v2/cmd/gopkgs@latest 2>&1
41 | RUN go install golang.org/x/tools/gopls@latest 2>&1
42 | RUN go install honnef.co/go/tools/cmd/staticcheck@latest 2>&1
43 | RUN go install github.com/cweill/gotests/gotests@latest 2>&1
44 | RUN go install github.com/fatih/gomodifytags@latest 2>&1
45 | RUN go install github.com/josharian/impl@latest 2>&1
46 | RUN go install github.com/haya14busa/goplay/cmd/goplay@latest 2>&1
47 | RUN go install github.com/go-delve/delve/cmd/dlv@latest 2>&1
48 |
49 | RUN chmod -R a+rwX /go/pkg && rm -rf /go/src/*
50 |
51 | RUN wget -O hivemind.gz https://github.com/DarthSim/hivemind/releases/download/v1.1.0/hivemind-v1.1.0-linux-amd64.gz \
52 | && gunzip --stdout hivemind.gz > /usr/local/bin/hivemind \
53 | && chmod +x /usr/local/bin/hivemind \
54 | && rm hivemind.gz
55 |
56 | RUN wget -O overmind.gz https://github.com/DarthSim/overmind/releases/download/v2.4.0/overmind-v2.4.0-linux-amd64.gz \
57 | && gunzip --stdout overmind.gz > /usr/local/bin/overmind \
58 | && chmod +x /usr/local/bin/overmind \
59 | && rm overmind.gz
60 |
61 | ADD install-node-red.sh /root/install-node-red.sh
62 |
63 | # needed by nod-red install script
64 | ARG TARGETARCH
65 | RUN /root/install-node-red.sh \
66 | && rm /root/install-node-red.sh
67 |
68 | ADD ./src/js/node-red /root/.node-red/nodes/ot-sim
69 | RUN cd /root/.node-red/nodes/ot-sim && npm install
70 |
71 | RUN python3 -m pip install --break-system-packages opendssdirect.py~=0.8.4
72 |
--------------------------------------------------------------------------------
/.devcontainer/devcontainer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "ot-sim",
3 | "build": {
4 | "dockerfile": "Dockerfile",
5 | "context": "..",
6 | "args": {
7 | "GOLANG_VERSION": "1.25.0"
8 | }
9 | },
10 |
11 | "runArgs": [
12 | "--security-opt", "seccomp=unconfined" // needed for debug support
13 | ],
14 |
15 | "mounts": [],
16 |
17 | "customizations": {
18 | "vscode": {
19 | "settings": {
20 | "terminal.integrated.profiles.linux": {
21 | "bash": {
22 | "path": "/bin/bash"
23 | }
24 | },
25 |
26 | "terminal.integrated.defaultProfile.linux": "bash"
27 | },
28 |
29 | "extensions": [
30 | "golang.go",
31 | "ms-python.python",
32 | "ms-vscode.cpptools",
33 | "ms-vsliveshare.vsliveshare-pack"
34 | ]
35 | }
36 | },
37 |
38 | "remoteUser": "vscode"
39 | }
40 |
--------------------------------------------------------------------------------
/.dockerignore:
--------------------------------------------------------------------------------
1 | build
2 | Dockerfile
3 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | *.csv filter=lfs diff=lfs merge=lfs -text
2 |
--------------------------------------------------------------------------------
/.github/workflows/e2e.yml:
--------------------------------------------------------------------------------
1 | name: End-to-End Testing
2 | on:
3 | - workflow_call
4 | jobs:
5 | e2e:
6 | name: Run End-to-End Tests
7 | runs-on: ubuntu-latest
8 | container: debian:bookworm # OT-sim I/O module segfaults in ubuntu-latest directly
9 | permissions:
10 | contents: read
11 | env:
12 | GOPATH: /go
13 | steps:
14 | - name: Install Build Dependencies
15 | run: |
16 | apt update
17 | apt install -y git git-lfs wget build-essential cmake libboost-dev libczmq-dev libxml2-dev libzmq5-dev pkg-config python3-dev python3-pip
18 | python3 -m pip install --break-system-packages opendssdirect.py~=0.8.4
19 | wget -O /tmp/go.tgz https://golang.org/dl/go1.22.1.linux-amd64.tar.gz \
20 | && tar -C /usr/local -xzf /tmp/go.tgz && rm /tmp/go.tgz \
21 | && ln -s /usr/local/go/bin/* /usr/local/bin
22 | echo "/go/bin" >> $GITHUB_PATH
23 | mkdir -p /go/src /go/bin
24 | chmod -R 777 /go
25 | wget -O /tmp/hivemind.gz https://github.com/DarthSim/hivemind/releases/download/v1.1.0/hivemind-v1.1.0-linux-amd64.gz \
26 | && gunzip --stdout /tmp/hivemind.gz > /usr/local/bin/hivemind \
27 | && chmod +x /usr/local/bin/hivemind \
28 | && rm /tmp/hivemind.gz
29 | - name: Check Out Repo
30 | uses: actions/checkout@v3
31 | with:
32 | lfs: true
33 | - name: Build Code
34 | run: |
35 | chown -R root:root /$GITHUB_WORKSPACE
36 | go version
37 | cmake -S . -B ./build -DBUILD_E2E=ON
38 | cmake --build ./build -j $(nproc) --target install
39 | git submodule update --init --recursive -- src/go/sunspec/common/models
40 | make -C ./src/go install
41 | python3 -m pip install --break-system-packages ./src/python
42 | ldconfig
43 | - name: Run Tests
44 | working-directory: testing/e2e
45 | run: |
46 | hivemind &> /tmp/test.log &
47 | sleep 15 # give devices time to propagate data
48 | ot-sim-e2e-dnp3-master || (cat /tmp/test.log ; exit 1)
49 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | bin
2 | build
3 |
4 | __pycache__
5 |
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "src/old/c/deps/json-c"]
2 | path = src/old/c/deps/json-c
3 | url = https://github.com/json-c/json-c.git
4 | [submodule "src/c++/deps/cppzmq"]
5 | path = src/c++/deps/cppzmq
6 | url = https://github.com/zeromq/cppzmq.git
7 | [submodule "src/c++/deps/fmt"]
8 | path = src/c++/deps/fmt
9 | url = https://github.com/fmtlib/fmt.git
10 | [submodule "src/c++/deps/json"]
11 | path = src/c++/deps/json
12 | url = https://github.com/nlohmann/json.git
13 | [submodule "src/c++/deps/opendnp3"]
14 | path = src/c++/deps/opendnp3
15 | url = https://github.com/dnp3/opendnp3.git
16 | [submodule "src/go/sunspec/common/models"]
17 | path = src/go/sunspec/common/models
18 | url = https://github.com/sunspec/models.git
19 |
--------------------------------------------------------------------------------
/.gitpod.yml:
--------------------------------------------------------------------------------
1 | image:
2 | file: Dockerfile.gitpod
3 | context: .
4 |
--------------------------------------------------------------------------------
/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 3.11)
2 |
3 | project(ot-sim)
4 |
5 | find_package(Git QUIET)
6 | if (GIT_FOUND AND EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/.git")
7 | message(STATUS "Executing git submodule update")
8 | execute_process(
9 | COMMAND ${GIT_EXECUTABLE} submodule update --init --recursive
10 | WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
11 | RESULT_VARIABLE GIT_SUBMOD_RESULT
12 | )
13 |
14 | if (NOT GIT_SUBMOD_RESULT EQUAL "0")
15 | message(
16 | FATAL_ERROR "git submodule update failed with ${GIT_SUBMOD_RESULT}"
17 | )
18 | endif ()
19 | endif ()
20 |
21 | OPTION(BUILD_E2E "Build E2E test executables" OFF)
22 |
23 | add_subdirectory(src/c)
24 | add_subdirectory(src/c++)
25 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM golang:1.25.0-bookworm AS gobuild
2 |
3 | ENV TZ=Etc/UTC
4 | RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
5 |
6 | RUN apt update && apt install -y \
7 | libzmq5-dev \
8 | make \
9 | pkg-config
10 |
11 | ADD .git /usr/local/src/ot-sim/.git
12 | ADD .gitmodules /usr/local/src/ot-sim/.gitmodules
13 |
14 | ADD src/go /usr/local/src/ot-sim/src/go
15 | RUN git -C /usr/local/src/ot-sim submodule update --init --recursive -- src/go/sunspec/common/models
16 | RUN make -C /usr/local/src/ot-sim/src/go install
17 |
18 | FROM python:3.11-bookworm AS pybuild
19 |
20 | ADD .git /usr/local/src/ot-sim/.git
21 |
22 | ADD src/python /usr/local/src/ot-sim/src/python
23 | RUN python3 -m pip install /usr/local/src/ot-sim/src/python
24 |
25 | FROM debian:bookworm AS build
26 |
27 | ENV TZ=Etc/UTC
28 | RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
29 |
30 | RUN apt update && apt install -y \
31 | build-essential \
32 | cmake \
33 | git \
34 | libboost-dev \
35 | libczmq-dev \
36 | libxml2-dev \
37 | libzmq3-dev \
38 | pkg-config \
39 | python3-dev \
40 | python3-pip \
41 | wget
42 |
43 | ADD .git /usr/local/src/ot-sim/.git
44 |
45 | ADD CMakeLists.txt /usr/local/src/ot-sim/CMakeLists.txt
46 | ADD src/c /usr/local/src/ot-sim/src/c
47 | ADD src/c++ /usr/local/src/ot-sim/src/c++
48 | RUN cmake -S /usr/local/src/ot-sim -B /usr/local/src/ot-sim/build \
49 | && cmake --build /usr/local/src/ot-sim/build -j $(nproc) --target install
50 |
51 | FROM debian:bookworm AS prod
52 |
53 | ENV TZ=Etc/UTC
54 | RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
55 |
56 | RUN apt update && apt install -y \
57 | bash-completion curl git tmux tree vim wget xz-utils \
58 | libczmq4 libsodium23 libxml2 libzmq5 python3-pip
59 |
60 | RUN curl -fsSL https://pkgs.tailscale.com/stable/debian/bookworm.noarmor.gpg \
61 | | tee /usr/share/keyrings/tailscale-archive-keyring.gpg >/dev/null \
62 | && curl -fsSL https://pkgs.tailscale.com/stable/debian/bookworm.tailscale-keyring.list \
63 | | tee /etc/apt/sources.list.d/tailscale.list \
64 | && apt update && apt install -y tailscale
65 |
66 | WORKDIR /root
67 |
68 | ADD install-node-red.sh .
69 |
70 | # needed by nod-red install script
71 | ARG TARGETARCH
72 | RUN /root/install-node-red.sh \
73 | && rm /root/install-node-red.sh
74 |
75 | ADD ./src/js/node-red /root/.node-red/nodes/ot-sim
76 | RUN cd /root/.node-red/nodes/ot-sim && npm install && cd /root
77 |
78 | COPY --from=gobuild /usr/local /usr/local
79 | COPY --from=pybuild /usr/local /usr/local
80 | COPY --from=build /usr/local /usr/local
81 |
82 | RUN ldconfig
83 |
84 | WORKDIR /
85 |
86 | CMD ["ot-sim-cpu-module", "/etc/ot-sim/config.xml"]
87 |
88 | FROM debian:bookworm AS test
89 |
90 | ENV TZ=Etc/UTC
91 | RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
92 |
93 | RUN apt update && apt install -y \
94 | bash-completion curl git mbpoll tmux tree vim wget xz-utils \
95 | build-essential cmake libczmq4 libsodium23 libxml2 libzmq5 python3-dev python3-pip
96 |
97 | RUN curl -fsSL https://pkgs.tailscale.com/stable/debian/bookworm.noarmor.gpg \
98 | | tee /usr/share/keyrings/tailscale-archive-keyring.gpg >/dev/null \
99 | && curl -fsSL https://pkgs.tailscale.com/stable/debian/bookworm.tailscale-keyring.list \
100 | | tee /etc/apt/sources.list.d/tailscale.list \
101 | && apt update && apt install -y tailscale
102 |
103 | RUN wget -O hivemind.gz https://github.com/DarthSim/hivemind/releases/download/v1.1.0/hivemind-v1.1.0-linux-amd64.gz \
104 | && gunzip --stdout hivemind.gz > /usr/local/bin/hivemind \
105 | && chmod +x /usr/local/bin/hivemind \
106 | && rm hivemind.gz
107 |
108 | RUN wget -O overmind.gz https://github.com/DarthSim/overmind/releases/download/v2.2.2/overmind-v2.2.2-linux-amd64.gz \
109 | && gunzip --stdout overmind.gz > /usr/local/bin/overmind \
110 | && chmod +x /usr/local/bin/overmind \
111 | && rm overmind.gz
112 |
113 | WORKDIR /root
114 |
115 | ADD install-node-red.sh .
116 |
117 | # needed by nod-red install script
118 | ARG TARGETARCH
119 | RUN /root/install-node-red.sh \
120 | && rm /root/install-node-red.sh
121 |
122 | ADD ./src/js/node-red /root/.node-red/nodes/ot-sim
123 | RUN cd /root/.node-red/nodes/ot-sim && npm install && cd /root
124 |
125 | COPY --from=gobuild /usr/local /usr/local
126 | COPY --from=pybuild /usr/local /usr/local
127 | COPY --from=build /usr/local /usr/local
128 |
129 | RUN python3 -m pip install --break-system-packages opendssdirect.py~=0.8.4
130 |
131 | RUN ldconfig
132 |
133 | ADD . /usr/local/src/ot-sim
134 | WORKDIR /usr/local/src/ot-sim
135 |
--------------------------------------------------------------------------------
/Dockerfile.gitpod:
--------------------------------------------------------------------------------
1 | FROM debian:bookworm
2 |
3 | ENV TZ=America/Denver
4 | RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
5 |
6 | RUN apt update && apt install -y \
7 | bash-completion build-essential cmake curl git git-lfs mbpoll sudo tmux tree vim wget xz-utils \
8 | cmake libboost-dev libczmq-dev libxml2-dev libzmq3-dev pkg-config python3-dev python3-pip
9 |
10 | RUN useradd -l -u 33333 -G sudo -md /home/gitpod -s /bin/bash -p gitpod gitpod
11 |
12 | ADD https://raw.githubusercontent.com/git/git/master/contrib/completion/git-prompt.sh /home/gitpod/.bash/git-prompt.sh
13 | RUN chown gitpod:gitpod /home/gitpod/.bash/git-prompt.sh \
14 | && chmod +x /home/gitpod/.bash/git-prompt.sh \
15 | && echo "\nsource ~/.bash/git-prompt.sh" >> /home/gitpod/.bashrc \
16 | && echo "export GIT_PS1_SHOWCOLORHINTS=true" >> /home/gitpod/.bashrc \
17 | && echo "export PROMPT_COMMAND='__git_ps1 \"\W\" \" » \"'" >> /home/gitpod/.bashrc
18 |
19 | ENV GOLANG_VERSION=1.22.1
20 |
21 | RUN wget -O go.tgz https://golang.org/dl/go${GOLANG_VERSION}.linux-amd64.tar.gz \
22 | && tar -C /usr/local -xzf go.tgz && rm go.tgz \
23 | && ln -s /usr/local/go/bin/* /usr/local/bin
24 |
25 | ENV GOPATH /go
26 | ENV PATH $GOPATH/bin:$PATH
27 |
28 | RUN mkdir -p "$GOPATH/src" "$GOPATH/bin" \
29 | && chmod -R 777 "$GOPATH"
30 |
31 | RUN go install github.com/ramya-rao-a/go-outline@latest 2>&1
32 | RUN go install github.com/mdempsky/gocode@latest 2>&1
33 | RUN go install github.com/stamblerre/gocode@latest 2>&1
34 | RUN go install github.com/rogpeppe/godef@latest 2>&1
35 | RUN go install github.com/uudashr/gopkgs/v2/cmd/gopkgs@latest 2>&1
36 | RUN go install golang.org/x/tools/gopls@latest 2>&1
37 | RUN go install honnef.co/go/tools/cmd/staticcheck@latest 2>&1
38 | RUN go install github.com/cweill/gotests/gotests@latest 2>&1
39 | RUN go install github.com/fatih/gomodifytags@latest 2>&1
40 | RUN go install github.com/josharian/impl@latest 2>&1
41 | RUN go install github.com/haya14busa/goplay/cmd/goplay@latest 2>&1
42 | RUN go install github.com/go-delve/delve/cmd/dlv@latest 2>&1
43 |
44 | RUN chmod -R a+rwX /go/pkg && rm -rf /go/src/*
45 |
46 | RUN wget -O hivemind.gz https://github.com/DarthSim/hivemind/releases/download/v1.1.0/hivemind-v1.1.0-linux-amd64.gz \
47 | && gunzip --stdout hivemind.gz > /usr/local/bin/hivemind \
48 | && chmod +x /usr/local/bin/hivemind \
49 | && rm hivemind.gz
50 |
51 | RUN wget -O overmind.gz https://github.com/DarthSim/overmind/releases/download/v2.4.0/overmind-v2.4.0-linux-amd64.gz \
52 | && gunzip --stdout overmind.gz > /usr/local/bin/overmind \
53 | && chmod +x /usr/local/bin/overmind \
54 | && rm overmind.gz
55 |
56 | ADD install-node-red.sh /root/install-node-red.sh
57 |
58 | # needed by nod-red install script
59 | ARG TARGETARCH
60 | RUN /root/install-node-red.sh \
61 | && rm /root/install-node-red.sh
62 |
63 | ADD ./src/js/node-red /root/.node-red/nodes/ot-sim
64 | RUN cd /root/.node-red/nodes/ot-sim && npm install
65 |
66 | ADD .git /workspaces/ot-sim/.git
67 |
68 | ADD CMakeLists.txt /workspaces/ot-sim/CMakeLists.txt
69 | ADD src/c /workspaces/ot-sim/src/c
70 | ADD src/c++ /workspaces/ot-sim/src/c++
71 | RUN cmake -S /workspaces/ot-sim -B /workspaces/ot-sim/build \
72 | && cmake --build /workspaces/ot-sim/build -j $(nproc) --target install \
73 | && ldconfig
74 |
75 | ADD src/go /workspaces/ot-sim/src/go
76 | RUN make -C /workspaces/ot-sim/src/go install
77 |
78 | ADD src/python /workspaces/ot-sim/src/python
79 | RUN python3 -m pip install --break-system-packages /workspaces/ot-sim/src/python
80 |
81 | RUN python3 -m pip install --break-system-packages opendssdirect.py~=0.8.4
82 |
83 | USER gitpod
84 |
--------------------------------------------------------------------------------
/Procfile.multi:
--------------------------------------------------------------------------------
1 | broker: python3 testing/e2e/helics/broker.py
2 | dss: python3 testing/e2e/helics/opendss-federate.py
3 | cpu-2: ot-sim-cpu-module config/multi-device/device-2.xml
4 | cpu-1: ot-sim-cpu-module config/multi-device/device-1.xml
5 |
--------------------------------------------------------------------------------
/Procfile.single:
--------------------------------------------------------------------------------
1 | broker: python3 testing/e2e/helics/broker.py
2 | dss: python3 testing/e2e/helics/opendss-federate.py
3 | cpu: ot-sim-cpu-module config/single-device/device.xml
4 |
--------------------------------------------------------------------------------
/config/multi-device/device-1.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 1
5 | tcp://127.0.0.1:1234
6 | tcp://127.0.0.1:5678
7 |
8 |
9 | ot-sim-message-bus {{config_file}}
10 | ot-sim-modbus-module {{config_file}}
11 | ot-sim-dnp3-module {{config_file}}
12 |
13 |
14 | 0.0.0.0:20000
15 | 15
16 |
17 | 1024
18 | 1
19 | 5
20 |
21 | 0
22 | line-650632.closed
23 | Group1Var1
24 | Group2Var1
25 | Class1
26 |
27 |
35 |
36 | 0
37 | line-650632.kW
38 | Group30Var6
39 | Group32Var6
40 | Class1
41 |
42 |
43 |
44 |
45 | 127.0.0.1:5502
46 | 2s
47 |
48 | 0
49 | line-650632.closed
50 |
51 |
52 | 30000
53 | line-650632.kW
54 | 2
55 |
56 |
57 |
58 |
--------------------------------------------------------------------------------
/config/multi-device/device-2.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | tcp://127.0.0.1:9012
5 | tcp://127.0.0.1:3456
6 |
7 |
8 | ot-sim-message-bus {{config_file}}
9 | ot-sim-io-module {{config_file}}
10 | ot-sim-modbus-module {{config_file}}
11 |
12 |
13 | 127.0.0.1:5502
14 |
15 | 0
16 | line-650632.closed
17 |
18 |
19 | 30000
20 | line-650632.kW
21 | -2
22 |
23 |
24 |
25 | localhost
26 | ot-sim-io
27 |
28 | OpenDSS/line-650632.kW
29 | double
30 | line-650632.kW
31 |
32 |
33 | OpenDSS/line-650632.kVAR
34 | double
35 | line-650632.kVAR
36 |
37 |
38 | OpenDSS/line-650632.closed
39 | boolean
40 | line-650632.closed
41 |
42 |
43 | OpenDSS/switch-671692.closed
44 | boolean
45 | switch-671692.closed
46 |
47 |
48 | line-650632.closed
49 |
50 |
51 |
52 |
--------------------------------------------------------------------------------
/config/single-device/device.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | tcp://127.0.0.1:1234
5 | tcp://127.0.0.1:5678
6 |
7 |
8 | ot-sim-message-bus {{config_file}}
9 | ot-sim-io-module {{config_file}}
10 | ot-sim-modbus-module {{config_file}}
11 | ot-sim-dnp3-module {{config_file}}
12 | ot-sim-telnet-module {{config_file}}
13 | ot-sim-node-red-module {{config_file}}
14 |
15 |
16 | node-red
17 | /etc/node-red.js
18 | dark
19 | config/single-device/node-red-hmi.json
20 |
26 |
27 |
28 |
29 | :23
30 | default
31 |
32 |
33 | 127.0.0.1:5502
34 |
35 | 0
36 | line-650632.closed
37 |
38 |
39 | 30000
40 | line-650632.kW
41 | -2
42 |
43 |
44 |
45 | 127.0.0.1:20000
46 | 15
47 |
48 | 1024
49 | 1
50 | 5
51 |
52 | 0
53 | line-650632.closed
54 | Group1Var1
55 | Group2Var1
56 | Class1
57 |
58 |
66 |
67 | 0
68 | line-650632.kW
69 | Group30Var6
70 | Group32Var6
71 | Class1
72 |
73 |
74 |
75 |
76 | localhost
77 | ot-sim-io
78 |
79 | OpenDSS/line-650632.kW
80 | double
81 | line-650632.kW
82 |
83 |
84 | OpenDSS/line-650632.kVAR
85 | double
86 | line-650632.kVAR
87 |
88 |
89 | OpenDSS/line-650632.closed
90 | boolean
91 | line-650632.closed
92 |
93 |
94 | OpenDSS/switch-671692.closed
95 | boolean
96 | switch-671692.closed
97 |
98 |
99 | line-650632.closed
100 |
101 |
102 |
103 |
--------------------------------------------------------------------------------
/config/wind-turbine/blade-1.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | tcp://127.0.0.1:1234
4 | tcp://127.0.0.1:5678
5 |
6 |
7 | 0.0.0.0:9101
8 | ot-sim-message-bus {{config_file}}
9 | ot-sim-modbus-module {{config_file}}
10 |
11 |
12 | 1.1.1.31:502
13 |
14 | 1
15 | feathered
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/config/wind-turbine/blade-2.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | tcp://127.0.0.1:1234
4 | tcp://127.0.0.1:5678
5 |
6 |
7 | 0.0.0.0:9101
8 | ot-sim-message-bus {{config_file}}
9 | ot-sim-modbus-module {{config_file}}
10 |
11 |
12 | 1.1.1.32:502
13 |
14 | 1
15 | feathered
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/config/wind-turbine/blade-3.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | tcp://127.0.0.1:1234
4 | tcp://127.0.0.1:5678
5 |
6 |
7 | 0.0.0.0:9101
8 | ot-sim-message-bus {{config_file}}
9 | ot-sim-modbus-module {{config_file}}
10 |
11 |
12 | 1.1.1.33:502
13 |
14 | 1
15 | feathered
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/config/wind-turbine/signal-converter.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | tcp://127.0.0.1:1234
4 | tcp://127.0.0.1:5678
5 |
6 |
7 | 0.0.0.0:9101
8 | ot-sim-message-bus {{config_file}}
9 | ot-sim-wind-turbine-anemometer-module {{config_file}}
10 | ot-sim-modbus-module {{config_file}}
11 |
12 |
13 |
14 |
15 | speed.high
16 | speed.med
17 | speed.low
18 | dir.high
19 | dir.med
20 | dir.low
21 | temp.high
22 | temp.low
23 | pressure
24 |
25 | /workspaces/ot-sim/data/weather.csv
26 |
27 |
28 |
29 | 1.1.1.21:502
30 |
31 | 30001
32 | speed.high
33 | -2
34 |
35 |
36 | 30002
37 | speed.med
38 | -2
39 |
40 |
41 | 30003
42 | speed.low
43 | -2
44 |
45 |
46 | 30004
47 | dir.high
48 | -2
49 |
50 |
51 | 30005
52 | dir.med
53 | -2
54 |
55 |
56 | 30006
57 | dir.low
58 | -2
59 |
60 |
61 | 30007
62 | temp.high
63 | -2
64 |
65 |
66 | 30008
67 | temp.low
68 | -2
69 |
70 |
71 | 30009
72 | pressure
73 | -2
74 |
75 |
76 |
77 |
--------------------------------------------------------------------------------
/config/wind-turbine/yaw-controller.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | tcp://127.0.0.1:1234
4 | tcp://127.0.0.1:5678
5 |
6 |
7 | 0.0.0.0:9101
8 | ot-sim-message-bus {{config_file}}
9 | ot-sim-modbus-module {{config_file}}
10 | ot-sim-logic-module {{config_file}}
11 |
12 |
13 | 1.1.1.11:502
14 |
15 | 30001
16 | yaw.current
17 | -2
18 |
19 |
20 | 40001
21 | yaw.setpoint
22 | -2
23 |
24 |
25 |
26 | 1s
27 | true
28 | current_yaw ? 1 : -1
32 | current_yaw = adjust ? current_yaw + (dir * 0.1) : current_yaw
33 | ]]>
34 |
35 | 0
36 | 0
37 |
38 |
39 |
40 |
--------------------------------------------------------------------------------
/data/weather.csv:
--------------------------------------------------------------------------------
1 | version https://git-lfs.github.com/spec/v1
2 | oid sha256:35d0c31dfca5135eb2b30781006b218862768a5c4c5bddb444f84663b56207f7
3 | size 6384023
4 |
--------------------------------------------------------------------------------
/install-node-red.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | echo "TARGET ARCHITECTURE: ${TARGETARCH}"
4 |
5 | wget -O installer.sh \
6 | https://raw.githubusercontent.com/node-red/linux-installers/master/deb/update-nodejs-and-nodered
7 |
8 | if [[ ${TARGETARCH} = arm* ]]; then
9 | echo "BUILDING FOR ARM"
10 |
11 | bash ./installer.sh --confirm-root --confirm-install --no-init \
12 | && rm installer.sh
13 |
14 | apt install -y libzmq3-dev
15 |
16 | pushd /root/.node-red
17 |
18 | npm install zeromq --zmq-shared
19 |
20 | apt purge -y libzmq3-dev && apt autoremove -y
21 | else
22 | echo "NOT BUILDING FOR ARM"
23 |
24 | bash ./installer.sh --confirm-root --confirm-install --no-init --skip-pi \
25 | && rm installer.sh
26 |
27 | pushd /root/.node-red
28 |
29 | npm install zeromq
30 | fi
31 |
32 | npm install \
33 | node-red-dashboard \
34 | node-red-contrib-modbus \
35 | @node-red-contrib-themes/theme-collection
36 |
37 | popd
38 |
--------------------------------------------------------------------------------
/ot-sim.code-workspace:
--------------------------------------------------------------------------------
1 | {
2 | "folders": [
3 | {
4 | "path": "."
5 | },
6 | {
7 | "path": "src/c"
8 | },
9 | {
10 | "path": "src/c++"
11 | },
12 | {
13 | "path": "src/go"
14 | },
15 | {
16 | "path": "src/python"
17 | }
18 | ],
19 | "extensions": {
20 | "recommendations": [
21 | "golang.go",
22 | "ms-python.python",
23 | "ms-vscode.cpptools"
24 | ]
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/c++/.vscode/c_cpp_properties.json:
--------------------------------------------------------------------------------
1 | {
2 | "configurations": [
3 | {
4 | "name": "Linux",
5 | "includePath": [
6 | "${workspaceFolder}",
7 | "${workspaceFolder}/deps",
8 | "${workspaceFolder}/deps/fmt/include",
9 | "${workspaceFolder}/deps/json/single_include",
10 | "${workspaceFolder}/deps/opendnp3/cpp/lib/include",
11 | "/usr/include/**",
12 | "/usr/local/include/**"
13 | ],
14 | "defines": [],
15 | "compilerPath": "/usr/bin/gcc",
16 | "cStandard": "gnu17",
17 | "cppStandard": "gnu++17",
18 | "intelliSenseMode": "linux-gcc-x64"
19 | }
20 | ],
21 | "version": 4
22 | }
23 |
--------------------------------------------------------------------------------
/src/c++/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | set(CMAKE_CXX_STANDARD 17)
2 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthread -Wall")
3 |
4 | set(CPPZMQ_INCLUDE_DIRS
5 | ${CMAKE_CURRENT_SOURCE_DIR}/deps
6 | )
7 |
8 | set(FMT_INCLUDE_DIRS
9 | ${CMAKE_CURRENT_SOURCE_DIR}/deps/fmt/include
10 | )
11 |
12 | set(JSON_INCLUDE_DIRS
13 | ${CMAKE_CURRENT_SOURCE_DIR}/deps/json/single_include
14 | )
15 |
16 | set(OPENDNP3_INCLUDE_DIRS
17 | ${CMAKE_CURRENT_SOURCE_DIR}/deps/opendnp3/cpp/lib/include
18 | )
19 |
20 | set(OTSIM_INCLUDE_DIRS
21 | ${CMAKE_CURRENT_SOURCE_DIR}
22 | )
23 |
24 | set(CPPZMQ_BUILD_TESTS OFF)
25 | set(JSON_BuildTests OFF)
26 |
27 | set(CMAKE_POLICY_DEFAULT_CMP0077 NEW)
28 | set(CMAKE_POSITION_INDEPENDENT_CODE ON)
29 |
30 | add_subdirectory(deps/cppzmq)
31 | add_subdirectory(deps/fmt)
32 | add_subdirectory(deps/json)
33 | add_subdirectory(deps/opendnp3)
34 |
35 | add_subdirectory(dnp3)
36 | add_subdirectory(msgbus)
37 |
38 | add_subdirectory(cmd/ot-sim-dnp3-module)
39 |
40 | if(BUILD_E2E)
41 | add_subdirectory(cmd/ot-sim-e2e-dnp3-master)
42 | endif()
43 |
--------------------------------------------------------------------------------
/src/c++/cmd/ot-sim-dnp3-module/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | find_package(Boost REQUIRED)
2 |
3 | include_directories(
4 | ${Boost_INCLUDE_DIRS}
5 | ${CPPZMQ_INCLUDE_DIRS}
6 | ${FMT_INCLUDE_DIRS}
7 | ${OPENDNP3_INCLUDE_DIRS}
8 | ${OTSIM_INCLUDE_DIRS}
9 | )
10 |
11 | link_directories(
12 | ${Boost_LIBRARY_DIRS}
13 | )
14 |
15 | add_definitions(-DBOOST_ALL_NO_LIB -DBOOST_ALL_DYN_LINK)
16 |
17 | add_executable(ot-sim-dnp3-module
18 | main.cpp
19 | )
20 |
21 | target_link_libraries(ot-sim-dnp3-module
22 | ${Boost_LIBRARIES}
23 | fmt::fmt
24 | ot-sim-dnp3
25 | ot-sim-msgbus
26 | )
27 |
28 | install(TARGETS ot-sim-dnp3-module
29 | RUNTIME DESTINATION bin
30 | )
--------------------------------------------------------------------------------
/src/c++/cmd/ot-sim-e2e-dnp3-master/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | include_directories(
2 | ${OPENDNP3_INCLUDE_DIRS}
3 | )
4 |
5 | add_executable(ot-sim-e2e-dnp3-master
6 | handler.hpp
7 | main.cpp
8 | )
9 |
10 | target_link_libraries(ot-sim-e2e-dnp3-master
11 | opendnp3
12 | )
13 |
14 | install(TARGETS ot-sim-e2e-dnp3-master
15 | RUNTIME DESTINATION bin
16 | )
--------------------------------------------------------------------------------
/src/c++/cmd/ot-sim-e2e-dnp3-master/handler.hpp:
--------------------------------------------------------------------------------
1 | #ifndef OTSIM_E2E_DNP3_MASTER_HANDLER_HPP
2 | #define OTSIM_E2E_DNP3_MASTER_HANDLER_HPP
3 |
4 | #include