mirror of
https://github.com/thiagoralves/OpenPLC_v3.git
synced 2026-09-22 19:11:15 +08:00
Merge branch 'master' into fix_defunct
This commit is contained in:
@@ -0,0 +1,2 @@
|
||||
# Shell script files should never have line endings converted
|
||||
*.sh text eol=lf
|
||||
+14
-7
@@ -1,10 +1,17 @@
|
||||
*.py[co]
|
||||
*.DS_*
|
||||
# Packages
|
||||
*.egg
|
||||
*.egg-info
|
||||
# where docs are output to on gitlab.. MUST be named public/
|
||||
public/
|
||||
|
||||
.sass-cache/
|
||||
.idea/
|
||||
|
||||
*.pyc
|
||||
/venv/
|
||||
|
||||
.vscode/
|
||||
# IDE files for Visual Studio Code and Visual Studio
|
||||
CMakeSettings.json
|
||||
*.vscode/
|
||||
*.vs/
|
||||
|
||||
# Build directories
|
||||
webserver/bin
|
||||
webserver/build
|
||||
obj/
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
image: ubuntu
|
||||
|
||||
pages:
|
||||
script:
|
||||
- apt-get update -qq
|
||||
- apt-get install -y -qq doxygen graphviz
|
||||
- doxygen doxygen.conf
|
||||
artifacts:
|
||||
paths:
|
||||
- public
|
||||
only:
|
||||
- master
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
language: bash
|
||||
|
||||
services:
|
||||
- docker
|
||||
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- doxygen
|
||||
|
||||
script:
|
||||
- docker build .
|
||||
- doxygen doxygen.conf
|
||||
|
||||
#deploy:
|
||||
# provider: pages
|
||||
# skip_cleanup: true
|
||||
# local_dir: public
|
||||
# github_token: $GITHUB_TOKEN
|
||||
# keep-history: true
|
||||
# on:
|
||||
# branch: master
|
||||
@@ -0,0 +1,6 @@
|
||||
FROM debian
|
||||
|
||||
COPY . /workdir
|
||||
RUN cd /workdir && ./install.sh docker
|
||||
WORKDIR /workdir
|
||||
ENTRYPOINT ["./start_openplc.sh"]
|
||||
@@ -1,24 +1,46 @@
|
||||
# OpenPLC Runtime version 3
|
||||
|
||||
[](https://travis-ci.org/thiagoralves/OpenPLC_v3)
|
||||
[](https://ci.appveyor.com/project/shrmrf/openplc-v3)
|
||||
|
||||
OpenPLC is an open-source [Programmable Logic Controller](https://en.wikipedia.org/wiki/Programmable_logic_controller) that is based on easy to use software. Our focus is to provide a low cost industrial solution for automation and research. OpenPLC has been used in [many research papers](https://scholar.google.com/scholar?as_ylo=2014&q=openplc&hl=en&as_sdt=0,1) as a framework for industrial cyber security research, given that it is the only controller to provide the entire source code.
|
||||
The OpenPLC Project consists of three sub-projects:
|
||||
1. [Runtime](https://github.com/thiagoralves/OpenPLC_v3)
|
||||
2. [Programming editor](http://www.openplcproject.com/plcopen-editor)
|
||||
3. [HMI builder](http://www.openplcproject.com/reference-installing-scadabr)
|
||||
<br/>
|
||||
To install:
|
||||
<br/>
|
||||
|
||||
## Installation:
|
||||
```bash
|
||||
git clone https://github.com/thiagoralves/OpenPLC_v3.git
|
||||
<br/>
|
||||
cd OpenPLC_v3
|
||||
<br/>
|
||||
./install.sh [platform]
|
||||
<br/>
|
||||
where [platform] can be:
|
||||
<br/>
|
||||
win - Install OpenPLC on Windows over Cygwin
|
||||
<br/>
|
||||
linux - Install OpenPLC on a Debian-based Linux distribution
|
||||
<br/>
|
||||
rpi - Install OpenPLC on a Raspberry Pi
|
||||
<br/>
|
||||
custom - Skip all specific package installation and tries to install OpenPLC assuming your system already has all dependencies met. This option can be useful if you're trying to install OpenPLC on an unsuported Linux platform or had manually installed all the dependency packages before.
|
||||
```
|
||||
|
||||
Where `[platform]` can be:
|
||||
|
||||
`win` - Install OpenPLC on Windows over Cygwin
|
||||
|
||||
`linux` - Install OpenPLC on a Debian-based Linux distribution
|
||||
|
||||
`docker` - Used by the `Dockerfile` (i.e. doesn't invoke `sudo`)
|
||||
|
||||
`rpi` - Install OpenPLC on a Raspberry Pi
|
||||
|
||||
`custom` - Skip all specific package installation and tries to install OpenPLC assuming your system already has all dependencies met. This option can be useful if you're trying to install OpenPLC on an unsuported Linux platform or had manually installed all the dependency packages before.
|
||||
|
||||
### Building, Installing and Running inside Docker
|
||||
Make sure [`docker` is installed](https://docs.docker.com/install/linux/docker-ce/ubuntu/)
|
||||
|
||||
#### Build
|
||||
```
|
||||
# instead of running install.sh as stated above, run:
|
||||
docker build -t openplc:v3 .
|
||||
```
|
||||
|
||||
#### RUN
|
||||
_Devices can be passed to the `docker` daemon using the `-v` flag (e.g. `-v /dev/ttyACM0:/dev/ttyACM0`)_
|
||||
|
||||
```bash
|
||||
docker run -it --rm --privileged -p 8080:8080 openplc:v3
|
||||
```
|
||||
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
image: Visual Studio 2015
|
||||
init:
|
||||
- git config --global core.autocrlf input
|
||||
clone_folder: c:\projects
|
||||
environment:
|
||||
matrix:
|
||||
- TARGET: cygwin-gcc
|
||||
BUILD_ENVIRONMENT: cygwin
|
||||
install:
|
||||
# batch commands start from cmd:
|
||||
- cmd: C:\\cygwin\\bin\\bash -l -c "cd C:/projects; ./install.sh win;"
|
||||
|
||||
build: off
|
||||
|
||||
+2431
File diff suppressed because it is too large
Load Diff
+157
-398
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,4 @@
|
||||
Flask==1.0.2
|
||||
Flask-Login==0.4.1
|
||||
pyserial==3.4
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
cmake_minimum_required(VERSION 3.0.0)
|
||||
|
||||
# CMake build for OpenPLC glue generator. The glue generator takes
|
||||
# generated C code from the MATIEC compiler and then generates necessary
|
||||
# glue to bind the MATIEC code with the OpenPLC runtime.
|
||||
project(openplc_gluegenerator)
|
||||
|
||||
option(WERROR "Set all warnings to errors" ON)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 11)
|
||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/bin)
|
||||
|
||||
# Optional build capabilities
|
||||
option(OPLCGLUE_ALL "Build all optional projects (tests)" ON)
|
||||
option(OPLCGLUE_TEST "Build tests" OFF)
|
||||
|
||||
if(OPLCGLUE_ALL)
|
||||
message("enabling all optional components")
|
||||
set(OPLCGLUE_TEST ON)
|
||||
endif()
|
||||
|
||||
# The main application that we always build
|
||||
add_executable(glue_generator glue_generator.cpp)
|
||||
|
||||
#if(OPLCGLUE_TEST)
|
||||
add_executable(glue_generator_test ./test/glue_generator_test.cpp)
|
||||
#endif()
|
||||
@@ -1,20 +1,16 @@
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <cstring>
|
||||
#include <cstdlib>
|
||||
|
||||
using namespace std;
|
||||
|
||||
ifstream locatedVars;
|
||||
ofstream glueVars;
|
||||
|
||||
void generateHeader()
|
||||
/// Write the header to the output stream. The header is common among all glueVars files.
|
||||
/// @param glueVars The output stream to write to.
|
||||
void generateHeader(ostream& glueVars)
|
||||
{
|
||||
if (glueVars.is_open())
|
||||
{
|
||||
glueVars << "\
|
||||
glueVars << "\
|
||||
//-----------------------------------------------------------------------------\r\n\
|
||||
// Copyright 2015 Thiago Alves\r\n\
|
||||
// This file is part of the OpenPLC Software Stack.\r\n\
|
||||
@@ -78,58 +74,41 @@ IEC_LINT *special_functions[BUFFER_SIZE];\r\n\
|
||||
\r\n\
|
||||
void glueVars()\r\n\
|
||||
{\r\n";
|
||||
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
cout << "Error opening glueVars.cpp file!" << endl;
|
||||
}
|
||||
}
|
||||
|
||||
int parseIecVars(char *varName, char *varType)
|
||||
int parseIecVars(istream& locatedVars, char *varName, char *varType)
|
||||
{
|
||||
string line;
|
||||
char buffer[1024];
|
||||
|
||||
if (locatedVars.is_open())
|
||||
{
|
||||
if (getline(locatedVars, line))
|
||||
{
|
||||
int i = 0, j = 0;
|
||||
strncpy(buffer, line.c_str(), 1024);
|
||||
for (i = 0; buffer[i] != '('; i++);
|
||||
i++;
|
||||
if (getline(locatedVars, line))
|
||||
{
|
||||
int i = 0, j = 0;
|
||||
strncpy(buffer, line.c_str(), 1024);
|
||||
for (i = 0; buffer[i] != '('; i++);
|
||||
i++;
|
||||
|
||||
while (buffer[i] != ',')
|
||||
{
|
||||
varType[j] = buffer[i];
|
||||
i++; j++;
|
||||
varType[j] = '\0';
|
||||
}
|
||||
i++; j=0;
|
||||
while (buffer[i] != ',')
|
||||
{
|
||||
varType[j] = buffer[i];
|
||||
i++; j++;
|
||||
varType[j] = '\0';
|
||||
}
|
||||
i++; j=0;
|
||||
|
||||
while (buffer[i] != ',')
|
||||
{
|
||||
varName[j] = buffer[i];
|
||||
i++; j++;
|
||||
varName[j] = '\0';
|
||||
}
|
||||
while (buffer[i] != ',')
|
||||
{
|
||||
varName[j] = buffer[i];
|
||||
i++; j++;
|
||||
varName[j] = '\0';
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
cout << "Error opening located variables file!" << endl;
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
void findPositions(char *varName, int *pos1, int *pos2)
|
||||
@@ -162,7 +141,7 @@ void findPositions(char *varName, int *pos1, int *pos2)
|
||||
*pos2 = atoi(tempBuffer);
|
||||
}
|
||||
|
||||
void glueVar(char *varName, char *varType)
|
||||
void glueVar(ostream& glueVars, char *varName, char *varType)
|
||||
{
|
||||
cout << "varName: " << varName << "\tvarType: " << varType << endl;
|
||||
int pos1, pos2;
|
||||
@@ -198,7 +177,7 @@ void glueVar(char *varName, char *varType)
|
||||
case 'X':
|
||||
glueVars << "\tbool_output[" << pos1 << "][" << pos2 << "] = " << varName << ";\r\n";
|
||||
break;
|
||||
case 'B':
|
||||
case 'B':
|
||||
glueVars << "\tbyte_output[" << pos1 << "] = " << varName << ";\r\n";
|
||||
break;
|
||||
case 'W':
|
||||
@@ -227,7 +206,7 @@ void glueVar(char *varName, char *varType)
|
||||
}
|
||||
}
|
||||
|
||||
void generateBottom()
|
||||
void generateBottom(ostream& glueVars)
|
||||
{
|
||||
glueVars << "}\r\n\
|
||||
\r\n\
|
||||
@@ -243,21 +222,66 @@ void updateTime()\r\n\
|
||||
}";
|
||||
}
|
||||
|
||||
int main()
|
||||
void generateBody(istream& locatedVars, ostream& glueVars) {
|
||||
// Start the generation process.
|
||||
char iecVar_name[100];
|
||||
char iecVar_type[100];
|
||||
|
||||
while (parseIecVars(locatedVars, iecVar_name, iecVar_type))
|
||||
{
|
||||
glueVar(glueVars, iecVar_name, iecVar_type);
|
||||
}
|
||||
}
|
||||
|
||||
/// This is our main function. We define it with a different name and then
|
||||
/// call it from the main function so that we can mock it for the purpose
|
||||
/// of testing.
|
||||
int mainImpl(int argc, char *argv[])
|
||||
{
|
||||
char iecVar_name[100];
|
||||
char iecVar_type[100];
|
||||
locatedVars.open("LOCATED_VARIABLES.h", ios::in);
|
||||
glueVars.open("glueVars.cpp", ios::trunc);
|
||||
|
||||
generateHeader();
|
||||
|
||||
while (parseIecVars(iecVar_name, iecVar_type))
|
||||
{
|
||||
glueVar(iecVar_name, iecVar_type);
|
||||
// Parse the command line arguments - if they exist. Show the help if there are too many arguments
|
||||
// or if the first argument is for help.
|
||||
bool show_help = argc >= 2 && (strcmp(argv[1], "-h") == 0 || strcmp(argv[1], "--help") == 0);
|
||||
if (show_help || (argc != 1 && argc != 3)) {
|
||||
cout << "Usage " << endl << endl;
|
||||
cout << " glue_generator [options] <path-to-located-variables.h> <path-to-glue-vars.cpp>" << endl << endl;
|
||||
cout << "Reads the LOCATED_VARIABLES.h file generated by the MATIEC compiler and produces" << endl;
|
||||
cout << "glueVars.cpp for the OpenPLC runtime. If not specified, paths are relative to" << endl;
|
||||
cout << "the current directory." << endl << endl;
|
||||
cout << "Options" << endl;
|
||||
cout << " --help,-h = Print usage information and exit." << endl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
generateBottom();
|
||||
// If we have 3 arguments, then the user provided input and output paths
|
||||
string input_file_name("LOCATED_VARIABLES.h");
|
||||
string output_file_name("glueVars.cpp");
|
||||
if (argc == 3) {
|
||||
input_file_name = argv[1];
|
||||
output_file_name = argv[2];
|
||||
}
|
||||
|
||||
// Try to open the files for reading and writing.
|
||||
ifstream locatedVars(input_file_name, ios::in);
|
||||
if (!locatedVars.is_open()) {
|
||||
cout << "Error opening located variables file at " << input_file_name << endl;
|
||||
return 1;
|
||||
}
|
||||
ofstream glueVars(output_file_name, ios::trunc);
|
||||
if (!glueVars.is_open()) {
|
||||
cout << "Error opening glue variables file at " << output_file_name << endl;
|
||||
return 2;
|
||||
}
|
||||
|
||||
generateHeader(glueVars);
|
||||
generateBody(locatedVars, glueVars);
|
||||
generateBottom(glueVars);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// For testing, we need to allow omitting the main function defined here.
|
||||
#ifndef OPLCGLUE_OMIT_MAIN
|
||||
int main(int argc, char *argv[]) {
|
||||
return mainImpl(argc, argv);
|
||||
}
|
||||
#endif // OPLCGLUE_OMIT_MAIN
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,115 @@
|
||||
// Catch2 will provide a main() function
|
||||
#define CATCH_CONFIG_MAIN
|
||||
#include "catch.hpp"
|
||||
#include <sstream>
|
||||
|
||||
// Our application is defined in a single CPP file, so to write tests against it
|
||||
// we need to include it directly here. That's ok so long as the application is
|
||||
// no more than one CPP file.
|
||||
#define OPLCGLUE_OMIT_MAIN
|
||||
#include "../glue_generator.cpp"
|
||||
|
||||
using namespace std;
|
||||
|
||||
SCENARIO("Commmand line", "[main]") {
|
||||
GIVEN("<no pre-conditions>") {
|
||||
WHEN("-h command line arguments") {
|
||||
char* args[2] = { "glue_generator", "-h" };
|
||||
REQUIRE(mainImpl(2, args) == 0);
|
||||
}
|
||||
|
||||
WHEN("--help command line arguments") {
|
||||
char* args[2] = { "glue_generator", "-h" };
|
||||
REQUIRE(mainImpl(2, args) == 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SCENARIO("", "") {
|
||||
GIVEN("IO as streams") {
|
||||
std::stringstream output_stream;
|
||||
WHEN("Contains single BOOL at %IX0") {
|
||||
std::stringstream input_stream("__LOCATED_VAR(BOOL,__IX0,I,X,0)");
|
||||
generateBody(input_stream, output_stream);
|
||||
REQUIRE(output_stream.str() == "\tbool_input[0][0] = __IX0;\r\n");
|
||||
}
|
||||
|
||||
WHEN("Contains single BOOL at %QX0") {
|
||||
std::stringstream input_stream("__LOCATED_VAR(BOOL,__QX0,Q,X,0)");
|
||||
generateBody(input_stream, output_stream);
|
||||
REQUIRE(output_stream.str() == "\tbool_output[0][0] = __QX0;\r\n");
|
||||
}
|
||||
|
||||
WHEN("Contains single BYTE at %IB0") {
|
||||
std::stringstream input_stream("__LOCATED_VAR(BYTE,__IB0,I,B,0)");
|
||||
generateBody(input_stream, output_stream);
|
||||
REQUIRE(output_stream.str() == "\tbyte_input[0] = __IB0;\r\n");
|
||||
}
|
||||
|
||||
WHEN("Contains single SINT at %IB1") {
|
||||
std::stringstream input_stream("__LOCATED_VAR(SINT,__IB1,I,B,1)");
|
||||
generateBody(input_stream, output_stream);
|
||||
REQUIRE(output_stream.str() == "\tbyte_input[1] = __IB1;\r\n");
|
||||
}
|
||||
|
||||
WHEN("Contains single SINT at %QB1") {
|
||||
std::stringstream input_stream("__LOCATED_VAR(SINT,__QB1,Q,B,1)");
|
||||
generateBody(input_stream, output_stream);
|
||||
REQUIRE(output_stream.str() == "\tbyte_output[1] = __QB1;\r\n");
|
||||
}
|
||||
|
||||
WHEN("Contains single USINT at %IB2") {
|
||||
std::stringstream input_stream("__LOCATED_VAR(USINT,__IB2,I,B,2)");
|
||||
generateBody(input_stream, output_stream);
|
||||
REQUIRE(output_stream.str() == "\tbyte_input[2] = __IB2;\r\n");
|
||||
}
|
||||
|
||||
WHEN("Contains single WORD at %IW0") {
|
||||
std::stringstream input_stream("__LOCATED_VAR(WORD,__IW0,I,W,0)");
|
||||
generateBody(input_stream, output_stream);
|
||||
REQUIRE(output_stream.str() == "\tint_input[0] = __IW0;\r\n");
|
||||
}
|
||||
|
||||
WHEN("Contains single WORD at %QW0") {
|
||||
std::stringstream input_stream("__LOCATED_VAR(WORD,__QW0,Q,W,0)");
|
||||
generateBody(input_stream, output_stream);
|
||||
REQUIRE(output_stream.str() == "\tint_output[0] = __QW0;\r\n");
|
||||
}
|
||||
|
||||
WHEN("Contains single INT at %IW1") {
|
||||
std::stringstream input_stream("__LOCATED_VAR(INT,__IW1,I,W,1)");
|
||||
generateBody(input_stream, output_stream);
|
||||
REQUIRE(output_stream.str() == "\tint_input[1] = __IW1;\r\n");
|
||||
}
|
||||
|
||||
WHEN("Contains single UINT at %IW2") {
|
||||
std::stringstream input_stream("__LOCATED_VAR(UINT,__IW2,I,W,2)");
|
||||
generateBody(input_stream, output_stream);
|
||||
REQUIRE(output_stream.str() == "\tint_input[2] = __IW2;\r\n");
|
||||
}
|
||||
|
||||
WHEN("Contains single INT at %MW2") {
|
||||
std::stringstream input_stream("__LOCATED_VAR(INT,__MW2,M,W,2)");
|
||||
generateBody(input_stream, output_stream);
|
||||
REQUIRE(output_stream.str() == "\tint_memory[2] = __MW2;\r\n");
|
||||
}
|
||||
|
||||
WHEN("Contains single DWORD at %MD0") {
|
||||
std::stringstream input_stream("__LOCATED_VAR(DWORD,__MD2,M,D,2)");
|
||||
generateBody(input_stream, output_stream);
|
||||
REQUIRE(output_stream.str() == "\tdint_memory[2] = (IEC_DINT *)__MD2;\r\n");
|
||||
}
|
||||
|
||||
WHEN("Contains single LINT at %ML1") {
|
||||
std::stringstream input_stream("__LOCATED_VAR(LINT,__ML1,M,L,1)");
|
||||
generateBody(input_stream, output_stream);
|
||||
REQUIRE(output_stream.str() == "\tlint_memory[1] = (IEC_LINT *)__ML1;\r\n");
|
||||
}
|
||||
|
||||
WHEN("Contains single LINT at %ML1024") {
|
||||
std::stringstream input_stream("__LOCATED_VAR(LINT,__ML1024,M,L,1024)");
|
||||
generateBody(input_stream, output_stream);
|
||||
REQUIRE(output_stream.str() == "\tspecial_functions[0] = (IEC_LINT *)__ML1024;\r\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -462,7 +462,7 @@ void *querySlaveDevices(void *arg)
|
||||
tempBuff = (uint16_t *)malloc(2*mb_devices[i].input_registers.num_regs);
|
||||
nanosleep(&ts, NULL);
|
||||
int return_val = modbus_read_input_registers( mb_devices[i].mb_ctx, mb_devices[i].input_registers.start_address,
|
||||
mb_devices[i].input_registers.num_regs-1, tempBuff);
|
||||
mb_devices[i].input_registers.num_regs, tempBuff);
|
||||
if (return_val == -1)
|
||||
{
|
||||
if (mb_devices[i].protocol != MB_RTU)
|
||||
|
||||
+4
-4
@@ -51,16 +51,16 @@ event_buffer_size = 10
|
||||
database_size = 8
|
||||
|
||||
# First data point offset for DI - required if slave device used (the address should represent 1st data point of slave device)
|
||||
offset_di = 800
|
||||
offset_di = 0
|
||||
|
||||
# First data point offset for DO - required if slave device used (the address should represent 1st data point of slave device)
|
||||
offset_do = 800
|
||||
offset_do = 0
|
||||
|
||||
# First data point offset for AI - required if slave device used (the address should represent 1st data point of slave device)
|
||||
offset_ai = 100
|
||||
offset_ai = 0
|
||||
|
||||
# First data point offset for AO - required if slave device used (the address should represent 1st data point of slave device)
|
||||
offset_ao = 100
|
||||
offset_ao = 0
|
||||
|
||||
#Timeout for solicited confirms
|
||||
# in MS
|
||||
|
||||
Reference in New Issue
Block a user