Merge branch 'master' into fix_defunct

This commit is contained in:
Thiago Alves
2019-04-25 08:55:26 -07:00
committed by GitHub
16 changed files with 17934 additions and 493 deletions
+2
View File
@@ -0,0 +1,2 @@
# Shell script files should never have line endings converted
*.sh text eol=lf
+14 -7
View File
@@ -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/
+12
View File
@@ -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
View File
@@ -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
+6
View File
@@ -0,0 +1,6 @@
FROM debian
COPY . /workdir
RUN cd /workdir && ./install.sh docker
WORKDIR /workdir
ENTRYPOINT ["./start_openplc.sh"]
+37 -15
View File
@@ -1,24 +1,46 @@
# OpenPLC Runtime version 3
[![Build Status](https://travis-ci.org/thiagoralves/OpenPLC_v3.svg?branch=master)](https://travis-ci.org/thiagoralves/OpenPLC_v3)
[![Build status](https://ci.appveyor.com/api/projects/status/ut3466ixwtyf68qg?svg=true)](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
```
+14
View File
@@ -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
View File
File diff suppressed because it is too large Load Diff
+157 -398
View File
File diff suppressed because it is too large Load Diff
+4
View File
@@ -0,0 +1,4 @@
Flask==1.0.2
Flask-Login==0.4.1
pyserial==3.4
+27
View File
@@ -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()
+92 -68
View File
@@ -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");
}
}
}
+1 -1
View File
@@ -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
View File
@@ -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