**
**
Fins协议在TCP/IP网段上的应用
modbus协议
都完全公开的
西门子S7协议,是不公开的
三个PLC,以太网通讯/TCP通讯
PLC做服务器 上位机做客户端
欧姆龙FinsTCP通讯协议
1.协议剖析
1) Fins握手命令(握手=验证)
①发送报文格式
包头/长度/命令/错误码/
最后一行,00 00 00 00-00 00 00 FE之间选哪个,看本低IP地址的最后一位,如下图的IP地址为192.168.1.111.最后一位为111,将111转化为十六进制为6F,进行测试验证。若不断开,证明连接完成。
握手报文只能发送一次,之后发送通讯报文,若再次发送握手报文,连接会断开。
②返回报文
2.Fins通讯命令
modbus格式总结为:起始地址+功能码+数据位+校验
DA1:PC发给PLC,PLC为目标,该位置写PLC的IP最后一位;如果是PLC返回给PC的,则该位置写PC的IP最后一位
SA1:与DA1对应
Parameter:是变化的,相当于数据位
3.Fins读取数据
Fins读取数据会在通用指令的基础上,将Parameter替换为Area+Address+Length,类似于Modbus;Modbus将数据位变为:起始地址+长度 读取数据返回为(数据位:字节计数+数据)
读取数据返回命令
4.Fins写入数据
Fins写入数据会在通用指令基础上,将Parameter替换为Area+Address+Length+Value,因此写入数据命令如下:
'''
MIT License
Copyright (c) 2017-2020 Richard.Hu
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
'''
'''
警告:以下代码只能在测试PLC中运行,禁止使用生产现场的PLC来测试,否则,后果自负
Warning: The following code can only be run in the Test plc, prohibit the use of the production site PLC to test, otherwise, the consequences
'''
# 首先安装 HslCommunication,使用pip来安装 pip install HslCommunication
# 如何配置,然后让本界面的代码运行起来,需要参考如下的网址:https://www.cnblogs.com/dathlin/p/12142663.html
# 我想要PythonQt来做一个类似winform的demo软件
# how to configure, and then let the interface code to run, need to refer to the following url: https://www.cnblogs.com/dathlin/p/12142663.html
# I want PythonQt to do a winform demo
import datetime
import sys
import threading
from time import sleep
import HslCommunication_new
from HslCommunication_new import SoftBasic, MelsecMcNet, MelsecMcAsciiNet, MelsecA1ENet, SiemensS7Net, SiemensPLCS, SiemensFetchWriteNet, OmronFinsNet, ModbusTcpNet, OperateResult, NetSimplifyClient
from PyQt5 import QtWidgets, QtCore, QtGui
from PyQt5.QtWidgets import QWidget, QDesktopWidget, QApplication, QMessageBox, QAction, QPushButton, QVBoxLayout, QLineEdit, QTextEdit
from PyQt5.QtGui import QPalette, QFont, QIcon, QBrush, QColor, QPainter
class WindowsLoad(QtWidgets.QMainWindow):
Language = 1 # 1代表中文,2代表英文
ShowAuthorInfomation = True # 是否显示相关的信息
WindowWidth = 1005
WindowHeight = 645
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
self.setGeometry(100, 100, 1175, 700) # 设置窗体的位置和大小情况
self.setWindowTitle('HslCommunication Test Tool') # 设置窗体的标题
self.center()
self.setWindowIcon(QIcon('bitbug_favicon.ico'))
self.setStyleSheet('WindowsLoad{background:#F0F8FF;font:微软雅黑;}') # 设置背景
# self.setFont(QtGui.QFont("微软雅黑"))
self.menuBar = self.menuBar() # 创建一个菜单栏
self.menuBar.setStyleSheet('QMenuBar{background:#404040;color:#cccccc}') # 设置背景
self.aboutMenu = self.menuBar.addMenu('About') # 添加About菜单
self.blogAction = QAction('Blogs [博客]', self)
self.blogAction.triggered.connect(self.blogClick)
self.aboutMenu.addAction(self.blogAction)
self.websiteAction = QAction( 'Website [官网]', self )
self.websiteAction.triggered.connect(self.WebsiteClick)
self.aboutMenu.addAction(self.websiteAction)
self.mesDemoAction = QAction( 'Mes Demo [简易的MES演示]', self )
self.mesDemoAction.triggered.connect(self.MesDemoClick)
self.aboutMenu.addAction(self.mesDemoAction)
self.chineseAction = QAction( '简体中文', self )
self.chineseAction.triggered.connect( self.ChineseClick )
self.menuBar.addAction(self.chineseAction)
self.englishAction = QAction( 'English', self )
self.englishAction.triggered.connect( self.EnglishClick )
self.menuBar.addAction(self.englishAction)
self.bbsAction = QAction( '论坛', self )
self.bbsAction.triggered.connect( self.BbsClick )
self.menuBar.addAction(self.bbsAction)
self.changeLogAction = QAction( '更新日志', self )
self.changeLogAction.triggered.connect( self.changeLogClick )
self.menuBar.addAction(self.changeLogAction)
self.versionAction = QAction( 'Version: 1.1.0', self )
self.menuBar.addAction(self.versionAction)
# 三菱的PLC的数据通信
self.melsecGroupBox = QtWidgets.QGroupBox(self)
self.melsecGroupBox.setTitle("Melsec PLC(三菱)")
self.melsecGroupBox.setGeometry(QtCore.QRect(9, 29, 183, 335))
self.pushButton1 = QtWidgets.QPushButton(self.melsecGroupBox)
self.pushButton1.setGeometry(QtCore.QRect(15, 24, 150, 32))
self.pushButton1.setText("MC (Binary)")
self.pushButton1.clicked.connect(self.pushButton1_Click)
self.pushButton1.setObjectName("pushButton1")
self.pushButton2 = QtWidgets.QPushButton(self.melsecGroupBox)
self.pushButton2.setGeometry(QtCore.QRect(15, 64, 150, 32))
self.pushButton2.setText("MC (ASCII)")
self.pushButton2.setObjectName("pushButton2")
self.pushButton2.clicked.connect(self.pushButton2_Click)
self.pushButton3 = QtWidgets.QPushButton(self.melsecGroupBox)
self.pushButton3.setGeometry(QtCore.QRect(15, 104, 150, 32))
self.pushButton3.setText("A-1E (Binary)")
self.pushButton3.setObjectName("pushButton3")
self.pushButton3.clicked.connect(self.pushButton3_Click)
# 西门子的PLC的数据通信
self.siemensGroupBox = QtWidgets.QGroupBox(self)
self.siemensGroupBox.setTitle("Siemens PLC(西门子)")
self.siemensGroupBox.setGeometry(QtCore.QRect(203, 29, 183, 335))
self.pushButton101 = QtWidgets.QPushButton(self.siemensGroupBox)
self.pushButton101.setGeometry(QtCore.QRect(18, 24, 150, 32))
self.pushButton101.setText("S7-1200")
self.pushButton101.setObjectName("pushButton101")
self.pushButton101.clicked.connect(self.pushButton101_Click)
self.pushButton102 = QtWidgets.QPushButton(self.siemensGroupBox)
self.pushButton102.setGeometry(QtCore.QRect(18, 64, 150, 32))
self.pushButton102.setText("S7-1500")
self.pushButton102.setObjectName("pushButton102")
self.pushButton102.clicked.connect(self.pushButton102_click)
self.pushButton103 = QtWidgets.QPushButton(self.siemensGroupBox)
self.pushButton103.setGeometry(QtCore.QRect(18, 104, 150, 32))
self.pushButton103.setText("S7-400")
self.pushButton103.setObjectName("pushButton103")
self.pushButton103.clicked.connect(self.pushButton103_click)
self.pushButton104 = QtWidgets.QPushButton(self.siemensGroupBox)
self.pushButton104.setGeometry(QtCore.QRect(18, 144, 150, 32))
self.pushButton104.setText("S7-300")
self.pushButton104.setObjectName("pushButton104")
self.pushButton104.clicked.connect(self.pushButton104_click)
self.pushButton105 = QtWidgets.QPushButton(self.siemensGroupBox)
self.pushButton105.setGeometry(QtCore.QRect(18, 184, 150, 32))
self.pushButton105.setText("s7-200")
self.pushButton105.setObjectName("pushButton105")
self.pushButton105.clicked.connect(self.pushButton105_click)
self.pushButton106 = QtWidgets.QPushButton(self.siemensGroupBox)
self.pushButton106.setGeometry(QtCore.QRect(18, 224, 150, 32))
self.pushButton106.setText("s7-200Smart")
self.pushButton106.setObjectName("pushButton106")
self.pushButton106.clicked.connect(self.pushButton106_click)
self.pushButton107 = QtWidgets.QPushButton(self.siemensGroupBox)
self.pushButton107.setGeometry(QtCore.QRect(18, 264, 150, 32))
self.pushButton107.setText("Fetch/Write")
self.pushButton107.setObjectName("pushButton107")
self.pushButton107.clicked.connect(self.pushButton107_click)
# Modbus
self.modbusGroupBox = QtWidgets.QGroupBox(self)
self.modbusGroupBox.setTitle("Modbus")
self.modbusGroupBox.setGeometry(QtCore.QRect(395, 29, 185, 335))
self.pushButton201 = QtWidgets.QPushButton(self.modbusGroupBox)
self.pushButton201.setGeometry(QtCore.QRect(15, 24, 150, 32))
self.pushButton201.setText("Modbus Tcp")
self.pushButton201.setObjectName("pushButton201")
self.pushButton201.clicked.connect(self.pushButton201_click)
# Omron
self.omronGroupBox = QtWidgets.QGroupBox(self)
self.omronGroupBox.setTitle("Omron PLC(欧姆龙)")
self.omronGroupBox.setGeometry(QtCore.QRect(586, 29, 185, 187))
self.pushButton301 = QtWidgets.QPushButton(self.omronGroupBox)
self.pushButton301.setGeometry(QtCore.QRect(15, 24, 150, 32))
self.pushButton301.setText("Fins Tcp")
self.pushButton301.setObjectName("pushButton301")
self.pushButton301.clicked.connect(self.pushButton301_click)
# Lsis
self.lsisGroupBox = QtWidgets.QGroupBox(self)
self.lsisGroupBox.setTitle("Lsis PLC")
self.lsisGroupBox.setGeometry(QtCore.QRect(586, 224, 185, 140))
# Panasonic
self.panasonicGroupBox = QtWidgets.QGroupBox(self)
self.panasonicGroupBox.setTitle("Panasonic PLC(松下)")
self.panasonicGroupBox.setGeometry(QtCore.QRect(777, 29, 185, 187))
# Keyence
self.keyenceGroupBox = QtWidgets.QGroupBox(self)
self.keyenceGroupBox.setTitle("Keyence PLC(基恩士)")
self.keyenceGroupBox.setGeometry(QtCore.QRect(777, 224, 185, 140))
# FATEK
self.fatekGroupBox = QtWidgets.QGroupBox(self)
self.fatekGroupBox.setTitle("FATEK PLC(永宏)")
self.fatekGroupBox.setGeometry(QtCore.QRect(968, 29, 185, 96))
# AllenBradly
self.abGroupBox = QtWidgets.QGroupBox(self)
self.abGroupBox.setTitle("AB PLC(罗克韦尔)")
self.abGroupBox.setGeometry(QtCore.QRect(968, 133, 185, 121))
# Fuji
self.fujiGroupBox = QtWidgets.QGroupBox(self)
self.fujiGroupBox.setTitle("Fuji PLC(富士)")
self.fujiGroupBox.setGeometry(QtCore.QRect(968, 262, 185, 102))
# HSL
self.fujiGroupBox = QtWidgets.QGroupBox(self)
self.fujiGroupBox.setTitle("Hsl Support(HSL协议)")
self.fujiGroupBox.setGeometry(QtCore.QRect(9, 372, 183, 315))
#self.melsecLayout.setGeometry( 9, 29, 183, 335)
#self.melsecLayout.setStyleSheet('QVBoxLayout{border:1px solid gray;border-radius:10px;padding:2px 4px;}') # 设置背景
self.setFont(QFont("微软雅黑", 15))
t1 = threading.Thread(target=self.threadReadFromServer, args=(13,))
t1.start()
def threadReadFromServer(self, num):
netSimplifyClient = NetSimplifyClient('118.24.36.220', 18467)
# netSimplifyClient.Token = uuid.UUID('66a469ad-a595-48ed-abe1-912f7085dbcd')
# netSimplifyClient.SetLoginAccount("admin","123456") # 如果采用了账户名和密码的验证,就使用这个方法
connect = netSimplifyClient.ConnectServer()
if connect.IsSuccess == False:
print(connect.Message)
else:
netSimplifyClient.ReadFromServer(600,'1.1.0')
netSimplifyClient.ConnectClose()
# 更新界面的相关信息
while True:
sleep(1)
print('HslTimeOut:' + str(len(HslCommunication_new.HslTimeOut.WaitHandleTimeOut)))
#控制窗口显示在屏幕中心的方法
def center(self):
#获得窗口
qr = self.frameGeometry()
#获得屏幕中心点
cp = QDesktopWidget().availableGeometry().center()
#显示到屏幕中心
qr.moveCenter(cp)
self.move(qr.topLeft())
def blogClick( self ):
QtGui.QDesktopServices.openUrl(QtCore.QUrl('http://www.cnblogs.com/dathlin/'))
def WebsiteClick( self ):
QtGui.QDesktopServices.openUrl(QtCore.QUrl('http://118.24.36.220/'))
def MesDemoClick( self ):
QtGui.QDesktopServices.openUrl(QtCore.QUrl('http://118.24.36.220/'))
def ChineseClick( self ):
HslCommunication_new.StringResources.Language = HslCommunication_new.DefaultLanguage()
QMessageBox.information(self,'Info','当前已经选择中文')
WindowsLoad.Language = 1
self.bbsAction.setText('论坛')
self.changeLogAction.setText('更新日志')
def EnglishClick( self ):
HslCommunication_new.StringResources.Language = HslCommunication_new.English()
QMessageBox.information(self,'Info','English Selected !')
WindowsLoad.Language = 2
self.bbsAction.setText('Bbs')
self.changeLogAction.setText('Change Log')
def BbsClick( self ):
QtGui.QDesktopServices.openUrl(QtCore.QUrl('http://118.24.36.220/'))
def changeLogClick( self ):
QtGui.QDesktopServices.openUrl(QtCore.QUrl('http://118.24.36.220:8080/'))
def AboutClick( self ):
reply = QMessageBox.question(self,'询问','这是一个询问消息对话框,默认是No', QMessageBox.Yes | QMessageBox.No | QMessageBox.Cancel, QMessageBox.No)
def pushButton1_Click(self):
self.formMelsecBinary = FormMelsecBinary()
self.formMelsecBinary.show()
def pushButton2_Click(self):
self.formMelsecAscii = FormMelsecAscii()
self.formMelsecAscii.show()
def pushButton3_Click(self):
self.formMelsecA1E = FormMelsecA1E()
self.formMelsecA1E.show()
def pushButton101_Click(self):
self.formSiemens = FormSiemens(SiemensPLCS.S1200)
self.formSiemens.show()
def pushButton102_click(self):
self.formSiemens = FormSiemens(SiemensPLCS.S1500)
self.formSiemens.show()
def pushButton103_click(self):
self.formSiemens = FormSiemens(SiemensPLCS.S400)
self.formSiemens.show()
def pushButton104_click(self):
self.formSiemens = FormSiemens(SiemensPLCS.S300)
self.formSiemens.show()
def pushButton105_click(self):
self.formSiemens = FormSiemens(SiemensPLCS.S200)
self.formSiemens.show()
def pushButton106_click(self):
self.formSiemens = FormSiemens(SiemensPLCS.S200Smart)
self.formSiemens.show()
def pushButton107_click(self):
self.formFW = FormSiemensFetchWriteNet()
self.formFW.show()
def pushButton201_click(self):
self.modbus = FormModbus()
self.modbus.show()
def pushButton301_click(self):
self.omron = FormOmron()
self.omron.show()
def show(self):
super().show()
class DemoUtils:
@staticmethod
def ReadResultRender(result : OperateResult, address : str, textBox : QTextEdit):
if result.IsSuccess == True:
if result.Content is list:
textBox.append(datetime.datetime.now().strftime('%H:%M:%S') + " [" + address + "] "+ SoftBasic.ArrayFormat(result.Content))
else:
textBox.append(datetime.datetime.now().strftime('%H:%M:%S') + " [" + address + "] "+ str(result.Content))
else:
QMessageBox.information(None,'Info',datetime.datetime.now().strftime('%H:%M:%S') + address + " Read Failed\nReason:"+ result.ToMessageShowString())
@staticmethod
def WriteResultRender(result : OperateResult, address : str):
if result.IsSuccess:
QMessageBox.information(None,'Info',datetime.datetime.now().strftime('%H:%M:%S') + address + " Write Success")
else:
QMessageBox.information(None,'Info',datetime.datetime.now().strftime('%H:%M:%S') + address + " Write Failed\nReason:"+ result.ToMessageShowString())
class UserControlHead(QWidget):
def __init__(self, parent = None):
super().__init__()
self.setParent(parent)
self.initUI()
def initUI(self):
self.setMinimumSize(800, 32)
self.HelpLink = 'http://www.hslcommunication.cn/'
self.helpLinkLabel1 = QtWidgets.QLabel('博客地址:', self)
self.helpLinkLabel1.move(13, 10)
self.helpLinkLabel = QtWidgets.QLabel(self.HelpLink, self)
self.helpLinkLabel.move(80, 10)
self.helpLinkLabel.mousePressEvent = self.pushButtonClick
self.protocol1 = QtWidgets.QLabel('使用协议:', self)
self.protocol1.move(544, 10)
self.protocol = QtWidgets.QLabel('HSL', self)
self.protocol.move(618, 10)
self.version = QtWidgets.QLabel('Version: ', self)
self.version.move(886, 10)
self.setStyleSheet('QWidget{color:#eeeeee;}')
def paintEvent(self, e):
qp = QPainter()
qp.begin(self)
self.drawWidget(qp)
qp.end()
def drawWidget(self, qp):
qp.setBrush(QColor(0x40, 0x40, 0x40))
qp.drawRect(0, 0, self.size().width(), self.size().height())
def