基于OpenPLC构造无web前端的软PLC

1 尝试1:基于requests编写无Web的前端

第一种尝试的思路是:基于OpenPLC已经开源的webserver后端,基于requests库编写无web的前端。换句话说,我想用requests的各种get/put请求,替代基于web的前端请求。

但是我在编写了一个前端后,出现了奇怪的问题:

使用web浏览器直接登录openplc的webserver,直接进入了编译的界面,如下:

查看源码可知,说明openplc runtime正在处于编译状态。

 而且点击其他选项栏都不会出现问题,但是点击Programs会出现以下问题:

Internal Server Error The server encountered an internal error and was unable to complete your request. Either the server is overloaded or there is an error in the application.

看起来像是自己写的前端导致OpenPLC崩溃了?

首先查询数据库中存储了多少program:

感觉也没有很多呀?

后来在网上发现了这个帖子:https://github.com/thiagoralves/OpenPLC_v3/issues/133

OpenPLC的开发者回答:这个问题是因为OpepPLC的安装已经被损坏,需要重新安装OpenPLC.

① 删除OpenPLC_v3文件夹

 ② 重新安装OpenPLC

安装结果为:

尝试重新打开OpenPLC的webserver,结果发现新的问题:

参考帖子:https://openplc.discussion.community/post/error-connecting-to-the-database-12687184

发现很可能是因为我之前服务器发生内部错误,很可能是因为我向数据库中输入了中文导致的

重启嵌入式开发板,发现一切正常。

但这种方法还是失败,因为前面遇到的Internal Server Error无法解决。

2 尝试2:基于flask编写OpenPLC的后端

第二种尝试的思路是:基于flask框架自行编写一个web后端,通过调用OpenPLC中开源的各种编译工具,实现无web的OpenPLC。

1、将我写的第一个后端webserver拷贝到有关文件夹,并且运行我写的后端:

 然后发现又报了同样的错误给前端:

发现是后端写的有问题,因为在路由函数中使用了多个return,但修改后依旧不可行。

2、尝试直接在命令行中利用有关命令进行操作:

再用前端发送“启动PLC”的命令,发现可行。神奇。

说明是openplc_runtime.compile_program函数有问题,具体来说,是subprocess.Popen()有问题。

3、修改后端,直接利用subprocess的有关API接口调用编译的程序,发现好像成功了!

 但是还是有报错,发现可能是我发送了不恰当的东西(即我调用了openplc_runtime.compilation_status()函数)于是再调整一下后端。

这次终于成功,发现前后端同时有编译成功的消息:

现在成功的后端代码如下:

import os
import time
import openplc


import subprocess
import flask

app = flask.Flask(__name__)

openplc_runtime = openplc.runtime()

def delete_persistent_file():
  if (os.path.isfile("persistent.file")):
  os.remove("persistent.file")
  print("persistent.file removed!")

@app.route('/upload', methods=['GET', 'POST'])
def upload():
  global openplc_runtime
  if (openplc_runtime.status() == "Compiling"):
    return "正在编译,请稍后"
  prog_file = flask.request.files['file'] # 获得文件

  filename = prog_file.filename
  prog_file.save(os.path.join('st_files', filename)) # 保存到相应位置
  # 这里应该还需要一个修改hardware_layer的代码
  # with open('./core/psm/main.py', 'w+') as f: f.write(custom_layer_code)
  #
  # subprocess.call(['./scripts/change_hardware_layer.sh', hardware_layer])

  delete_persistent_file()
  subprocess.run(['./scripts/compile_program.sh', filename])

  #status_str = openplc_runtime.compilation_status()
  # 最后返回编译状态
  return "编译成功!"

 

@app.route('/run')
def run():
  global openplc_runtime
  openplc_runtime.start_runtime()
  time.sleep(1)
  return "运行成功!"


@app.route('/stop')
def stop():
  global openplc_runtime
  openplc_runtime.stop_runtime()
  return "中止成功!"

if __name__ == '__main__':
app.run(host='0.0.0.0', port=5001)::

3 尝试编写处理IT上传程序的flask后端

1. 找到Python解释器的位置

which python3

2. 尝试通过subprocess.Popen启动python程序

process = subprocess.Popen(['/usr/bin/python3', filename])

但是发现出现以下错误:

Traceback (most recent call last):
File "uploaded_IT_files/color.py", line 79, in <module>
cv2.destroyAllWindows()
cv2.error: OpenCV(4.9.0) /io/opencv/modules/highgui/src/window.cpp:1266: error: (-2:Unspecified error) The function is not implemented. Rebuild the library with Windows, GTK+ 2.x or Cocoa support. If you are on Ubuntu or Debian, install libgtk2.0-dev and pkg-config, then re-run cmake or configure script in function 'cvDestroyAllWindows'

 出现错误的原因应该是:创龙的嵌入式开发板中缺少GUI界面,所以python程序中定义的有关API使用错误。

所以我们将测试的python程序代码改为如下:

import cv2
import numpy as np
import time

def detect_green(frame):
# 将图像转换到HSV颜色空间
hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)

# 定义绿色的HSV范围
lower_green = np.array([50, 90, 90])
upper_green = np.array([90, 255, 255])

# 生成绿色的掩码
mask = cv2.inRange(hsv, lower_green, upper_green)

# 膨胀操作
kernel = np.ones((5,5), np.uint8)
dilated_mask = cv2.dilate(mask, kernel, iterations=1)

# 寻找轮廓
contours, _ = cv2.findContours(dilated_mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

# 如果存在绿色物体,则输出 "green"
if len(contours) > 0:
print("green")

# 打开摄像头
cap = cv2.VideoCapture(0)

while True:
# 读取每一帧
ret, frame = cap.read()
if not ret:
break

# 调用detect_green函数检测绿色物体
detect_green(frame)

# 在每次循环迭代之间添加延迟
time.sleep(0.1) # 延迟100毫秒

# 释放摄像头
cap.release()

这个程序的作用是:利用摄像头检测到绿色,就输出green

实验结果如下:

 也可以顺利利用前端中止该python程序,显示结果如下:

 现在的后端代码如下:

import os
import time
import openplc

import subprocess
import flask

app = flask.Flask(__name__)

openplc_runtime = openplc.runtime()
running_IT_process = {}

def delete_persistent_file():
if (os.path.isfile("persistent.file")):
os.remove("persistent.file")
print("persistent.file removed!")


@app.route('/upload', methods=['GET', 'POST'])
def upload():
global openplc_runtime
if (openplc_runtime.status() == "Compiling"):
return "正在编译,请稍后"
prog_file = flask.request.files['file'] # 获得文件
filename = prog_file.filename
prog_file.save(os.path.join('st_files', filename)) # 保存到相应位置
# 这里应该还需要一个修改hardware_layer的代码
# with open('./core/psm/main.py', 'w+') as f: f.write(custom_layer_code)
#
# subprocess.call(['./scripts/change_hardware_layer.sh', hardware_layer])

delete_persistent_file()
subprocess.run(['./scripts/compile_program.sh', filename])

# status_str = openplc_runtime.compilation_status()
# 最后返回编译状态
return "编译成功!"

@app.route('/run')
def run():
global openplc_runtime
openplc_runtime.start_runtime()
time.sleep(1)
return "运行成功!"


@app.route('/stop')
def stop():
global openplc_runtime
openplc_runtime.stop_runtime()
return "中止成功!"

#############以上都是OT侧的管理,以下是IT侧的管理####################
@app.route('/uploadIT', methods=['POST'])
def uploadIT():
global running_IT_process

if 'file' not in flask.request.files:
return "没有上传文件"

prog_file = flask.request.files['file']
if prog_file.filename == '':
return "未选择文件"

upload_folder = 'uploaded_IT_files'
if not os.path.exists(upload_folder):
os.makedirs(upload_folder)

filename = os.path.join(upload_folder, prog_file.filename)
prog_file.save(filename)

try:
process = subprocess.Popen(['/usr/bin/python3', filename])
running_IT_process[prog_file.filename] = process
return "Python脚本执行成功!"
except Exception as e:
return f"Python脚本执行失败:{e}"


@app.route('/stopIT/<filename>', methods=['GET'])
def stopIT(filename):
global running_IT_process

if filename in running_IT_process:
process = running_IT_process[filename]
process.terminate()
del running_IT_process[filename]
return f"Python脚本 {filename} 已中止"
else:
return f"没有名为 {filename} 的正在执行的Python脚本"


if __name__ == '__main__':
app.run(host='0.0.0.0', port=5001)

 

posted @ 2024-04-13 20:03  碳酸钾K2CO3  阅读(26)  评论(0编辑  收藏  举报