mirror of
https://github.com/thiagoralves/OpenPLC_v3.git
synced 2026-02-06 02:02:20 +08:00
@@ -4,8 +4,10 @@ from sql.createTables import allTables
|
||||
from sql.scripts import insert
|
||||
from sql.utils import convertData
|
||||
from mirror.user import UserType, UserNullable
|
||||
from mirror.settings import SettingsType
|
||||
from bcrypt import gensalt, hashpw
|
||||
from base64 import b64encode as enc64
|
||||
import json
|
||||
|
||||
db = dirname(__file__) + "/database.sqlite"
|
||||
|
||||
@@ -25,6 +27,19 @@ def getMainUserScript():
|
||||
return insert("User", user)
|
||||
|
||||
|
||||
def getDefaultSettingsScript():
|
||||
staticFolder = dirname(dirname(__file__)) + "/static"
|
||||
settingsFile = staticFolder + "/json/defaultSettings.json"
|
||||
scripts = []
|
||||
with open(settingsFile, "r") as file:
|
||||
settings = json.loads(file.read())
|
||||
for k, v in settings.items():
|
||||
item = {"key": k, "value": v}
|
||||
convertData(item, SettingsType)
|
||||
scripts.append(insert("Settings", item))
|
||||
return scripts
|
||||
|
||||
|
||||
if not exists(db):
|
||||
with open(db, "w"):
|
||||
pass
|
||||
@@ -33,4 +48,6 @@ if not exists(db):
|
||||
for t in allTables:
|
||||
c.execute(t)
|
||||
c.execute(getMainUserScript())
|
||||
for script in getDefaultSettingsScript():
|
||||
c.execute(script)
|
||||
c.commit()
|
||||
|
||||
Binary file not shown.
@@ -11,7 +11,6 @@ loginManager = LoginManager()
|
||||
|
||||
|
||||
def validateLogin(username, password):
|
||||
|
||||
try:
|
||||
userInfo = getUserInfo(username)
|
||||
except:
|
||||
|
||||
48
webserver/database/settings.py
Normal file
48
webserver/database/settings.py
Normal file
@@ -0,0 +1,48 @@
|
||||
from . import db
|
||||
from sqlite3 import connect, Row
|
||||
from mirror.settings import SettingsType, SettingsOptions
|
||||
from sql.scripts import update, select
|
||||
from sql.utils import convertData
|
||||
|
||||
|
||||
def getSettings():
|
||||
database = connect(db)
|
||||
database.row_factory = Row
|
||||
|
||||
script = select("Settings")
|
||||
|
||||
try:
|
||||
settings = {}
|
||||
c = database.execute(script)
|
||||
s = c.fetchall()
|
||||
for setting in s:
|
||||
settings[setting[0]] = setting[1]
|
||||
database.commit()
|
||||
return settings
|
||||
except:
|
||||
raise Exception("Failed getting settings")
|
||||
|
||||
|
||||
def saveSettings(settings):
|
||||
database = connect(db)
|
||||
database.row_factory = Row
|
||||
|
||||
for key in SettingsOptions:
|
||||
if key not in settings.keys():
|
||||
settings[key] = "disabled"
|
||||
|
||||
scripts = []
|
||||
|
||||
for k, v in settings.items():
|
||||
data = {"key": k, "value": v}
|
||||
convertData(data, SettingsType)
|
||||
filter = {"key": data.pop("key")}
|
||||
scripts.append(update("Settings", data, filter))
|
||||
|
||||
try:
|
||||
for script in scripts:
|
||||
database.execute(script)
|
||||
database.commit()
|
||||
return settings
|
||||
except:
|
||||
raise Exception("Failed updating settings")
|
||||
@@ -8,7 +8,6 @@ from base64 import b64encode as enc64
|
||||
|
||||
|
||||
class User:
|
||||
|
||||
__isAuth = True
|
||||
__isActive = True
|
||||
__isAnon = False
|
||||
|
||||
@@ -10,7 +10,7 @@ blueprint = Blueprint("loginApi", __name__)
|
||||
@blueprint.route("/login", methods=["GET", "POST"])
|
||||
def login():
|
||||
if current_user.is_authenticated:
|
||||
return redirect('/dashboard', 302)
|
||||
return redirect("/dashboard", 302)
|
||||
return send_file("static/html/login.html")
|
||||
|
||||
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
from flask import Blueprint, send_file
|
||||
from flask_login import login_required
|
||||
import serial.tools.list_ports
|
||||
import platform
|
||||
import json
|
||||
|
||||
blueprint = Blueprint("modbusApi", __name__)
|
||||
|
||||
@@ -20,3 +23,24 @@ def addDevice():
|
||||
@login_required
|
||||
def editDevice():
|
||||
return send_file("static/html/modbus/editDevice/editModbusDevice.html")
|
||||
|
||||
|
||||
@blueprint.route("/modbus/deviceTypes", methods=["GET", "POST"])
|
||||
@login_required
|
||||
def deviceTypes():
|
||||
return send_file("static/json/deviceTypes.json")
|
||||
|
||||
|
||||
@blueprint.route("/modbus/comPorts", methods=["GET", "POST"])
|
||||
@login_required
|
||||
def comPorts():
|
||||
ports = [comport.device for comport in serial.tools.list_ports.comports()]
|
||||
portNames = []
|
||||
for port in ports:
|
||||
portNames.append(port)
|
||||
if platform.system().startswith("CYGWIN"):
|
||||
portNames = list(
|
||||
map(lambda x: "COM" + str(int(x.split("/dev/ttyS")[1]) + 1), portNames)
|
||||
)
|
||||
|
||||
return json.dumps(portNames)
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
from flask import Blueprint, send_file
|
||||
from flask import Blueprint, send_file, request
|
||||
from flask_login import login_required
|
||||
from database.settings import getSettings, saveSettings
|
||||
import json
|
||||
|
||||
blueprint = Blueprint("settingsApi", __name__)
|
||||
|
||||
@@ -7,4 +9,10 @@ blueprint = Blueprint("settingsApi", __name__)
|
||||
@blueprint.route("/settings", methods=["GET", "POST"])
|
||||
@login_required
|
||||
def settings():
|
||||
if request.method == "GET" and request.args.get("data"):
|
||||
return json.dumps(getSettings())
|
||||
|
||||
elif request.method == "POST":
|
||||
data = dict(request.form)
|
||||
saveSettings(data)
|
||||
return send_file("static/html/settings.html")
|
||||
|
||||
11
webserver/mirror/settings.py
Normal file
11
webserver/mirror/settings.py
Normal file
@@ -0,0 +1,11 @@
|
||||
from .classes import String
|
||||
|
||||
SettingsType = {"key": String, "value": String}
|
||||
|
||||
SettingsOptions = [
|
||||
"modbus_port",
|
||||
"dnp3_port",
|
||||
"enip_port",
|
||||
"pstorage_polling",
|
||||
"start_run_mode",
|
||||
]
|
||||
@@ -10,4 +10,10 @@ User += " salt VARCHAR(255),"
|
||||
User += " CONSTRAINT pk_user PRIMARY KEY (id),"
|
||||
User += " CONSTRAINT uq_user UNIQUE (username));"
|
||||
|
||||
allTables = [User]
|
||||
# Creates Settings table
|
||||
Settings = "CREATE TABLE Settings"
|
||||
Settings += " (key VARCHAR(20) not null,"
|
||||
Settings += " value VARCHAR(20) not null,"
|
||||
Settings += " CONSTRAINT pk_settings PRIMARY KEY (key));"
|
||||
|
||||
allTables = [User, Settings]
|
||||
|
||||
@@ -85,3 +85,7 @@ input[type="text"] {
|
||||
display: block;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
#settingsContainer {
|
||||
padding: 0.01em 16px 50px 16px;
|
||||
}
|
||||
|
||||
@@ -120,14 +120,11 @@
|
||||
placeholder="My Device"
|
||||
/>
|
||||
<label for="dev_protocol"><b>Device Type</b></label>
|
||||
<select id="dev_protocol" name="device_protocol">
|
||||
<option selected="selected" value="Uno">Arduino Uno</option>
|
||||
<option value="Mega">Arduino Mega</option>
|
||||
<option value="ESP32">ESP32</option>
|
||||
<option value="ESP8266">ESP8266</option>
|
||||
<option value="TCP">Generic Modbus TCP Device</option>
|
||||
<option value="RTU">Generic Modbus RTU Device</option>
|
||||
</select>
|
||||
<select
|
||||
id="dev_protocol"
|
||||
name="device_protocol"
|
||||
onchange="refreshSelector()"
|
||||
></select>
|
||||
<label for="dev_id"><b>Slave ID</b></label>
|
||||
<input
|
||||
type="text"
|
||||
@@ -155,7 +152,11 @@
|
||||
</div>
|
||||
<div id="rtu-stuff">
|
||||
<label for="dev_cport"><b>COM Port</b></label>
|
||||
<select id="dev_cport" name="device_cport"></select>
|
||||
<select
|
||||
id="dev_cport"
|
||||
name="device_cport"
|
||||
onclick="populateCOM()"
|
||||
></select>
|
||||
<label for="dev_baud"><b>Baud Rate</b></label>
|
||||
<input
|
||||
type="text"
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
<script src="static/javascript/settings.js"></script>
|
||||
<script src="static/javascript/main.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<body onload="onLoad()">
|
||||
<div class="top">
|
||||
<img
|
||||
src="static/assets/openplc_logo.gif"
|
||||
@@ -101,7 +101,7 @@
|
||||
<a href="/startPlc" class="button start"><b>Start PLC</b></a>
|
||||
</div>
|
||||
<div class="mainContainer">
|
||||
<div class="w3-container">
|
||||
<div id="settingsContainer" class="w3-container">
|
||||
<br />
|
||||
<h2>Settings</h2>
|
||||
<form
|
||||
@@ -122,7 +122,7 @@
|
||||
<input
|
||||
type="text"
|
||||
id="modbus_server_port"
|
||||
name="modbus_server_port"
|
||||
name="modbus_port"
|
||||
value="502"
|
||||
/>
|
||||
<br />
|
||||
@@ -139,7 +139,7 @@
|
||||
<input
|
||||
type="text"
|
||||
id="dnp3_server_port"
|
||||
name="dnp3_server_port"
|
||||
name="dnp3_port"
|
||||
value="20000"
|
||||
/>
|
||||
<br />
|
||||
@@ -156,7 +156,7 @@
|
||||
<input
|
||||
type="text"
|
||||
id="enip_server_port"
|
||||
name="enip_server_port"
|
||||
name="enip_port"
|
||||
value="44818"
|
||||
/>
|
||||
<br />
|
||||
@@ -173,7 +173,7 @@
|
||||
<input
|
||||
type="text"
|
||||
id="pstorage_thread_poll"
|
||||
name="pstorage_thread_poll"
|
||||
name="pstorage_polling"
|
||||
value="10"
|
||||
disabled=""
|
||||
/>
|
||||
@@ -189,7 +189,7 @@
|
||||
type="hidden"
|
||||
value="false"
|
||||
id="auto_run_text"
|
||||
name="auto_run_text"
|
||||
name="start_run_mode"
|
||||
/>
|
||||
<br />
|
||||
<h2>Slave Devices</h2>
|
||||
@@ -199,7 +199,7 @@
|
||||
<input
|
||||
type="text"
|
||||
id="slave_polling_period"
|
||||
name="slave_polling_period"
|
||||
name="slave_polling"
|
||||
value="100"
|
||||
/>
|
||||
<br />
|
||||
|
||||
@@ -1,4 +1,85 @@
|
||||
function populateDropdown() {
|
||||
var tcp_stuff = document.getElementById("tcp-stuff");
|
||||
var rtu_stuff = document.getElementById("rtu-stuff");
|
||||
fetch("/modbus/deviceTypes")
|
||||
.then((response) => {
|
||||
return response.json();
|
||||
})
|
||||
.then((jsondata) => {
|
||||
var selectedType = "rtu";
|
||||
jsondata.forEach((e) => {
|
||||
if (e.isSelected) {
|
||||
selectedType = e.type;
|
||||
}
|
||||
});
|
||||
switch (selectedType) {
|
||||
case "rtu":
|
||||
populateCOM();
|
||||
tcp_stuff.style.display = "none";
|
||||
rtu_stuff.style.display = "block";
|
||||
break;
|
||||
case "tcp":
|
||||
tcp_stuff.style.display = "block";
|
||||
rtu_stuff.style.display = "none";
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
var e = document.getElementById("dev_protocol");
|
||||
var tcp = document.createElement("optgroup");
|
||||
tcp.label = "TCP";
|
||||
var rtu = document.createElement("optgroup");
|
||||
rtu.label = "RTU";
|
||||
for (let hardware of jsondata) {
|
||||
var option = document.createElement("option");
|
||||
option.value = hardware.value;
|
||||
option.selected = hardware.isSelected;
|
||||
option.innerHTML = hardware.label;
|
||||
if (hardware.type === "tcp") {
|
||||
tcp.appendChild(option);
|
||||
} else {
|
||||
rtu.appendChild(option);
|
||||
}
|
||||
}
|
||||
e.appendChild(rtu);
|
||||
e.appendChild(tcp);
|
||||
});
|
||||
}
|
||||
|
||||
function populateCOM() {
|
||||
fetch("/modbus/comPorts")
|
||||
.then((response) => {
|
||||
return response.json();
|
||||
})
|
||||
.then((jsondata) => {
|
||||
var e = document.getElementById("dev_cport");
|
||||
e.innerHTML = "";
|
||||
for (let port of jsondata) {
|
||||
var option = document.createElement("option");
|
||||
option.value = port.toLowerCase();
|
||||
option.innerHTML = port;
|
||||
e.appendChild(option);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function refreshSelector() {
|
||||
var drop_down = document.getElementById("dev_protocol");
|
||||
var selected = [...drop_down.options].find((o) => o.selected);
|
||||
var tcp_stuff = document.getElementById("tcp-stuff");
|
||||
var rtu_stuff = document.getElementById("rtu-stuff");
|
||||
if (selected.parentElement.label === "TCP") {
|
||||
tcp_stuff.style.display = "block";
|
||||
rtu_stuff.style.display = "none";
|
||||
} else {
|
||||
populateCOM();
|
||||
rtu_stuff.style.display = "block";
|
||||
tcp_stuff.style.display = "none";
|
||||
}
|
||||
}
|
||||
|
||||
window.onload = function () {
|
||||
populateDropdown();
|
||||
setupPageContent();
|
||||
LoadValuesFromDB();
|
||||
};
|
||||
|
||||
@@ -1,7 +1,59 @@
|
||||
window.onload = function () {
|
||||
setupCheckboxes();
|
||||
var checkboxes = {
|
||||
modbus_port: "modbus_server",
|
||||
dnp3_port: "dnp3_server",
|
||||
enip_port: "enip_server",
|
||||
pstorage_polling: "pstorage_thread",
|
||||
start_run_mode: "auto_run",
|
||||
};
|
||||
|
||||
function populateFields() {
|
||||
fetch("/settings?data=true")
|
||||
.then((response) => {
|
||||
return response.json();
|
||||
})
|
||||
.then((jsondata) => {
|
||||
for (let key in jsondata) {
|
||||
var e = document.getElementsByName(key)[0];
|
||||
var c = document.getElementById(checkboxes[key]);
|
||||
if (key === "start_run_mode") {
|
||||
c.checked = jsondata[key] === "true";
|
||||
continue;
|
||||
}
|
||||
if (jsondata[key] === "disabled") {
|
||||
if (c) c.checked = false;
|
||||
} else {
|
||||
if (c) c.checked = true;
|
||||
e.value = jsondata[key];
|
||||
}
|
||||
}
|
||||
setupCheckboxes();
|
||||
});
|
||||
}
|
||||
|
||||
function onLoad() {
|
||||
populateFields();
|
||||
setupCheckboxes();
|
||||
document.getElementById("modbus_server").onchange = function () {
|
||||
setupCheckboxes();
|
||||
};
|
||||
|
||||
document.getElementById("dnp3_server").onchange = function () {
|
||||
setupCheckboxes();
|
||||
};
|
||||
|
||||
document.getElementById("enip_server").onchange = function () {
|
||||
setupCheckboxes();
|
||||
};
|
||||
|
||||
document.getElementById("pstorage_thread").onchange = function () {
|
||||
setupCheckboxes();
|
||||
};
|
||||
|
||||
document.getElementById("auto_run").onchange = function () {
|
||||
setupCheckboxes();
|
||||
};
|
||||
}
|
||||
|
||||
function setupCheckboxes() {
|
||||
var modbus_checkbox = document.getElementById("modbus_server");
|
||||
var modbus_text = document.getElementById("modbus_server_port");
|
||||
@@ -45,26 +97,6 @@ function setupCheckboxes() {
|
||||
}
|
||||
}
|
||||
|
||||
document.getElementById("modbus_server").onchange = function () {
|
||||
setupCheckboxes();
|
||||
};
|
||||
|
||||
document.getElementById("dnp3_server").onchange = function () {
|
||||
setupCheckboxes();
|
||||
};
|
||||
|
||||
document.getElementById("enip_server").onchange = function () {
|
||||
setupCheckboxes();
|
||||
};
|
||||
|
||||
document.getElementById("pstorage_thread").onchange = function () {
|
||||
setupCheckboxes();
|
||||
};
|
||||
|
||||
document.getElementById("auto_run").onchange = function () {
|
||||
setupCheckboxes();
|
||||
};
|
||||
|
||||
function validateForm() {
|
||||
var modbus_checkbox = document.forms["uploadForm"]["modbus_server"].checked;
|
||||
var modbus_port = document.forms["uploadForm"]["modbus_server_port"].value;
|
||||
@@ -96,5 +128,6 @@ function validateForm() {
|
||||
alert("Persistent Storage polling rate must be bigger than zero");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
9
webserver/static/json/defaultSettings.json
Normal file
9
webserver/static/json/defaultSettings.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"modbus_port": "502",
|
||||
"dnp3_port": "20000",
|
||||
"start_run_mode": "false",
|
||||
"slave_polling": "100",
|
||||
"slave_timeout": "1000",
|
||||
"enip_port": "44818",
|
||||
"pstorage_polling": "disabled"
|
||||
}
|
||||
44
webserver/static/json/deviceTypes.json
Normal file
44
webserver/static/json/deviceTypes.json
Normal file
@@ -0,0 +1,44 @@
|
||||
[
|
||||
{
|
||||
"id": 1,
|
||||
"value": "arduino_uno",
|
||||
"label": "Arduino Uno",
|
||||
"type": "rtu",
|
||||
"isSelected": false
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"value": "arduino_mega",
|
||||
"label": "Arduino Mega",
|
||||
"type": "rtu",
|
||||
"isSelected": false
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"value": "esp32",
|
||||
"label": "ESP32",
|
||||
"type": "tcp",
|
||||
"isSelected": false
|
||||
},
|
||||
{
|
||||
"id": 4,
|
||||
"value": "esp8266",
|
||||
"label": "ESP8266",
|
||||
"type": "tcp",
|
||||
"isSelected": false
|
||||
},
|
||||
{
|
||||
"id": 5,
|
||||
"value": "generic_tcp",
|
||||
"label": "Generic Modbus TCP Device",
|
||||
"type": "tcp",
|
||||
"isSelected": false
|
||||
},
|
||||
{
|
||||
"id": 6,
|
||||
"value": "generic_rtu",
|
||||
"label": "Generic Modbus RTU Device",
|
||||
"type": "rtu",
|
||||
"isSelected": false
|
||||
}
|
||||
]
|
||||
@@ -1,9 +1,8 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from flask import Flask, jsonify, redirect, request
|
||||
from flask import Flask, redirect
|
||||
from database.login import loginManager
|
||||
from endpoints import blueprint as runtimeApp
|
||||
from os import urandom
|
||||
import json
|
||||
|
||||
app = Flask(__name__)
|
||||
app.secret_key = urandom(12).hex()
|
||||
|
||||
Reference in New Issue
Block a user