# NexIoT-Simulator
**Repository Path**: thingscan/NexIoT-Simulator
## Basic Information
- **Project Name**: NexIoT-Simulator
- **Description**: A single-process device simulator covering 4 industrial protocols, for integration development and testing against host systems such as ThingsBoard Gateway.
- **Primary Language**: Python
- **License**: Apache-2.0
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 1
- **Forks**: 0
- **Created**: 2026-08-28
- **Last Updated**: 2026-08-28
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# NexIoT Simulator
> **English** | [中文](./README.zh-CN.md)
**A single-process device simulator covering 4 industrial protocols, for integration development and testing against host systems such as ThingsBoard Gateway.**
`python -m app` starts everything at once: four protocol servers (Modbus TCP, OPC UA, BACnet, Siemens S7) plus a FastAPI management interface, with a Vue 3 frontend point configuration UI. All point data can be configured and modified via the REST API and takes effect on the protocol wire side **within at most 1 second**.
[](https://www.python.org/)
[](https://fastapi.tiangolo.com/)
[](https://vuejs.org/)
[](LICENSE)
> Want technical details and internals? Read the docs under [`doc/`](doc/en/README.md).
---
## Table of Contents
- [Features](#features)
- [UI Preview](#ui-preview)
- [Supported Protocols & Ports](#supported-protocols--ports)
- [Quick Start](#quick-start)
- [Docker One-Click Install (Recommended)](#docker-one-click-install-recommended)
- [Docker Compose (Development)](#docker-compose-development)
- [Local Run (Python + Frontend)](#local-run-python--frontend)
- [Usage](#usage)
- [API Overview](#api-overview)
- [Technical Documentation](#technical-documentation)
- [Project Structure](#project-structure)
- [Testing & Verification](#testing--verification)
- [Environment Variable Overrides](#environment-variable-overrides)
- [FAQ](#faq)
- [Contributing](#contributing)
- [License](#license)
---
## Features
- **Four industrial protocols**: simultaneously simulates Modbus TCP (2 slaves), OPC UA (2 devices), BACnet (15 objects), and Siemens S7 (4 memory areas) devices.
- **Config immediately effective**: after creating, modifying, or deleting a point via the REST API / frontend, the sync loop writes the value to the protocol wire side within **≤1 second** (see [`doc/03-data-flow.md`](doc/en/03-data-flow.md)).
- **RPC writes**: all protocols support native writes (FC06/FC16, UA Write, BACnet WriteProperty, S7 write_area) plus a unified REST API write.
- **Web management UI**: Vue 3 + Pinia frontend with point list (filter by protocol), detail, create/edit/delete, and JSON import/export.
- **Full REST API**: point CRUD, value read/write, import/export, stats, plus interactive Swagger docs.
- **Flexible configuration**: environment variables override each protocol's initial values without rebuilding the image.
- **Configuration persistence**: point configs and current values are auto-saved to disk (default `./data/points.json`) and restored automatically after a container restart/recreate — no need to reconfigure.
- **Out of the box**: 49 built-in seed points covering multiple protocols and data types (bit/uint16/int32/float32/double/string/boolean, etc.).
## UI Preview
**Web management UI** — point list, filter by protocol, stats and actions:

**Modbus TCP client reads** (coils / discrete inputs / holding registers):

**OPC UA client reads** (MyDevice / MyDevice1 nodes):

**S7 client reads** (I / Q / M areas) and **DB1 data block read**:

## Supported Protocols & Ports
| Protocol | Port | Transport | Description |
|----------|------|-----------|-------------|
| Modbus TCP | `502` | TCP | 2 slave devices (unit 1 / 2), with coils, discrete inputs, input/holding registers |
| OPC UA | `53530` | TCP | `opc.tcp://0.0.0.0:53530/OPCUA/SimulationServer`, 2 devices |
| BACnet | `47808` | UDP | device instance 1234 (SimBACnetDevice), 15 objects |
| S7 | `102` | TCP | Siemens S7 simulator, I / Q / M / DB1 memory areas |
| HTTP | `8000` | TCP | FastAPI management API + frontend (override with the `API_PORT` environment variable) |
> Most ports are privileged 0–1024 ports (502 / 102); running locally on Linux requires root. It is recommended to use the [Docker method](#docker-one-click-install-recommended).
## Quick Start
### Docker One-Click Install (Recommended)
Run the prebuilt Docker Hub image directly — no local build required:
```bash
docker run -d --name nexiot-simulator --restart unless-stopped \
-p 502:502 \
-p 53530:53530 \
-p 47808:47808/udp \
-p 102:102 \
-p 8000:8000 \
-v nexiot-data:/app/data \
jettzhan/nexiot-simulator
```
- `--restart unless-stopped`: the container is restarted automatically if it exits and starts on boot.
- `-v nexiot-data:/app/data`: named-volume persistence. Point configs and current values are stored in `points.json` and restored automatically on container restart/recreate; on first boot with no file, 49 seed points are written.
- To override initial values, append `-e VAR=value` (e.g. `-e "MB_FLOAT_0=42.5"`); see [Environment Variable Overrides](#environment-variable-overrides).
After startup:
| Service | Address |
|---------|---------|
| Web management UI | |
| Swagger API docs | |
| Modbus TCP | `localhost:502` |
| OPC UA | `opc.tcp://localhost:53530/OPCUA/SimulationServer` |
| BACnet | `localhost:47808` (UDP) |
| S7 | `localhost:102` |
### Docker Compose (Development)
For development/debugging: build the image locally and orchestrate with Compose. `docker-compose.yml` already configures the port mappings and the named volume `nexiot-data`.
```bash
docker compose up -d --build
```
### Local Run (Python + Frontend)
Requires Python 3.11+ and Node.js 18+.
```bash
# 1. Install backend dependencies
pip install -r requirements.txt
# 2. Start the simulator (default API port 8000)
python -m app
# or with a custom API port: API_PORT=8001 python -m app
```
The frontend build is optional (otherwise there is no web UI; the API is unaffected):
```bash
cd frontend
npm install
npx vite build # build output goes to ../app/static, served by FastAPI after restart
# dev mode (hot reload, proxies /api → localhost:8000):
npm run dev
```
## Usage
1. Open to view the point list and stats, and filter points by protocol.
2. Create / edit / delete points in the UI, or import/export JSON configuration (all changes are saved automatically).
3. After changing a point value, OPC UA / BACnet / S7 / Modbus sync to the protocol wire side within **≤1 second** and can be read directly by clients such as ThingsBoard Gateway.
4. On the next startup / container restart, previously configured points and current values are restored automatically.
5. The detailed point table (address and initial value of each register/node) is in [`doc/02-data-model.md`](doc/en/02-data-model.md).
## API Overview
Base URL: `http://:8000`, interactive docs: `http://:8000/docs`.
| Method | Path | Description |
|--------|------|-------------|
| `GET` | `/api/points` | point list + stats |
| `POST` | `/api/points` | create a point |
| `GET` | `/api/points/{id}` | point detail + current value |
| `PUT` | `/api/points/{id}` | update a point |
| `DELETE` | `/api/points/{id}` | delete a point |
| `DELETE` | `/api/points` | clear all points |
| `GET` | `/api/points/{id}/value` | read the current value |
| `PUT` | `/api/points/{id}/value` | write the current value (RPC) |
| `GET` | `/api/points/export` | export all point configs |
| `POST` | `/api/points/import` | full import of point configs |
| `GET` | `/api/stats` | point stats |
**Quick start:**
```bash
# List points
curl http://localhost:8000/api/points
# Write an OPC UA point value (syncs to the UA node within 1s)
curl -X PUT http://localhost:8000/api/points/16/value \
-H "Content-Type: application/json" -d '{"value":200.5}'
# Create a Modbus point
curl -X POST http://localhost:8000/api/points \
-H "Content-Type: application/json" \
-d '{"name":"MB_Test","protocol":"modbus","modbus":{"unitId":1,"functionCode":"03","address":30,"dataType":"uint16","byteOrder":"AB"},"initialValue":777,"changeMode":"fixed"}'
```
For the full API reference with request/response examples, see [`doc/05-api.md`](doc/en/05-api.md).
## Technical Documentation
System design, data model, effect mechanism, and protocol implementation details are documented in the [`doc/`](doc/en/README.md) directory:
| Doc | Content |
|-----|---------|
| [doc/README.md](doc/en/README.md) | technical doc index & 30-second architecture overview |
| [01-architecture.md](doc/en/01-architecture.md) | system architecture, tech stack, process/thread model, port allocation |
| [02-data-model.md](doc/en/02-data-model.md) | point data model, Registry design, 49 seed points |
| [03-data-flow.md](doc/en/03-data-flow.md) | **Core**: data flow & the "configuration takes effect" mechanism |
| [04-protocol-implementation.md](doc/en/04-protocol-implementation.md) | implementation details of the 4 protocol servers |
| [05-api.md](doc/en/05-api.md) | REST API reference (with request/response examples) |
| [06-run-deploy.md](doc/en/06-run-deploy.md) | running, frontend build, Docker deployment, environment variable overrides |
| [07-verification.md](doc/en/07-verification.md) | verification & testing methods (three-layer verification system) |
Chinese versions of these documents are available under [`doc/`](doc/README.md).
## Project Structure
```
.
├── app/ # backend (single process)
│ ├── __main__.py # entry point: load persisted config / seed + 4 server threads + uvicorn
│ ├── main.py # FastAPI instance, mounts /api and static assets
│ ├── api.py # REST routes (point CRUD / value read-write / import-export / stats)
│ ├── registry.py # PointRegistry: in-memory point configs + value store (thread-safe)
│ ├── persistence.py # JSON persistence of point configs + current values
│ ├── seed.py # 49 seed points
│ ├── static/ # frontend build output (served by FastAPI at /)
│ └── servers/ # 4 protocol servers
│ ├── modbus_server.py
│ ├── opcua_server.py
│ ├── bacnet_server.py
│ └── s7_server.py
├── frontend/ # Vue 3 + Pinia + Vite + TypeScript frontend
│ └── src/
│ ├── api/ # REST wrapper (fetch)
│ ├── i18n.ts # Chinese/English translations & language switcher
│ ├── stores/ # Pinia store (point list/filter/detail/actions)
│ ├── types/ # TypeScript types (mirror backend point structure)
│ └── components/ # StatsBar / PointList / PointDetail / PointModal
├── doc/ # technical documentation (Chinese + English under doc/en/)
├── img/ # README screenshots
├── tests/ # unit / integration tests (pytest)
├── verify_point_config.py # point-config effect verification script (run after starting the service)
├── Dockerfile
└── docker-compose.yml
```
## Testing & Verification
The project uses a three-layer verification system (see [`doc/07-verification.md`](doc/en/07-verification.md)):
```bash
# 1. Unit tests (37 checks, no service required)
python -m pytest tests -v -p no:cacheprovider
# 2. Wire-level integration tests (start the simulator first, 162 checks)
python tests/test_all_protocols.py
# 3. Point-config effect verification (start the simulator first, 10 checks)
python verify_point_config.py
```
The four protocols have all been verified for "API config syncs to the wire within ≤1s" (creating points, changing values, RPC writes, etc.); see [`doc/07-verification.md`](doc/en/07-verification.md#6-known-conclusions-confirmed-during-verification).
## Environment Variable Overrides
Override each protocol's initial values in `docker-compose.yml` or the shell without modifying code:
| Prefix | Description | Example |
|--------|-------------|---------|
| `MB_*` | Modbus initial values | `MB_FLOAT_0=42.5`, `MB_COIL_BITS=1023` |
| `UA_*` | OPC UA variable values | `UA_MyDevice_Temperature=30.0` |
| `BAC_*` | BACnet object presentValue | `BAC_AV_2=70.0`, `BAC_BV_1=inactive` |
| `S7_*` | S7 memory area bytes | `S7_DB1_DBD0=99999`, `S7_M_0=255` |
| `API_PORT` | FastAPI port (default 8000) | `API_PORT=8001` |
```yaml
# docker-compose.yml example
environment:
- MB_FLOAT_0=42.5 # Modbus 1st float → 42.5
- UA_MyDevice_Count=999 # OPC UA MyDevice.Count → 999
- BAC_AV_2=70.0 # BACnet HumiditySetpoint → 70%
- S7_DB1_DBD0=88888 # S7 DB1.DBD0 → 88888
```
The full variable list is in [`doc/en/06-run-deploy.md`](doc/en/06-run-deploy.md#5-environment-variable-overrides-for-initial-values).
## FAQ
**Q: Modbus / S7 port startup fails (Permission denied)?**
Ports 502 / 102 are privileged ports; on Linux use root or Docker (the container runs as root).
**Q: Docker build fails to pull the base image?**
If your network is restricted and `python:3.11-slim` is unreachable, replace the base image in the `Dockerfile` with an accessible registry and rebuild.
**Q: The wire side didn't change after modifying a point value?**
- OPC UA / BACnet / S7 / Modbus all pull values from the Registry via a per-second sync loop, so in theory they take effect within ≤1s.
- Values written natively by external tools are overwritten by the sync loop with Registry values; to persist, update via the REST API accordingly (see [`doc/03-data-flow.md`](doc/en/03-data-flow.md)).
**Q: The data read during verification/integration is stale?**
A leftover old simulator process may still occupy the port; clean it up and restart.
## Contributing
Issues and Pull Requests are welcome.
- When reporting a bug, please attach the protocol, port, reproduction steps, and logs.
- Follow PEP 8; run `python -m pytest tests` after changes to ensure tests pass.
- When adding or changing protocol behavior, please update the corresponding English technical docs under [`doc/en/`](doc/en/README.md) (and the Chinese versions under [`doc/`](doc/README.md)).
## License
This project is open-sourced under the [Apache License 2.0](LICENSE).