Fins TCP协议

**

PLC通讯协议剖析及应用

**

Fins协议在TCP/IP网段上的应用
modbus协议
都完全公开的

西门子S7协议,是不公开的

三个PLC,以太网通讯/TCP通讯

  1. 西门子S7-1200 192.168.1.201 S7 端口号:102 发送第一次未断开,发送第二次断开 西门子PLC的连接在正常的三次握手基础上,有两次验证
  2. 三菱Q系列 192.168.1.250 MC 端口号:4096 怎么发送都不会断开 不存在验证
  3. 欧姆龙CJ-2M系列 192.168.1.14 Fins TCP 端口号:9600 发送第一次就断开 欧姆龙PLC的连接是在正常的三次握手的基础上,有一次验证

PLC做服务器 上位机做客户端

欧姆龙FinsTCP通讯协议

1.协议剖析
1) Fins握手命令(握手=验证)
①发送报文格式
包头/长度/命令/错误码/
Fins TCP协议_第1张图片最后一行,00 00 00 00-00 00 00 FE之间选哪个,看本低IP地址的最后一位,如下图的IP地址为192.168.1.111.最后一位为111,将111转化为十六进制为6F,进行测试验证。若不断开,证明连接完成。
握手报文只能发送一次,之后发送通讯报文,若再次发送握手报文,连接会断开。
Fins TCP协议_第2张图片

②返回报文
Fins TCP协议_第3张图片
2.Fins通讯命令
Fins TCP协议_第4张图片
modbus格式总结为:起始地址+功能码+数据位+校验

Fins TCP协议_第5张图片
DA1:PC发给PLC,PLC为目标,该位置写PLC的IP最后一位;如果是PLC返回给PC的,则该位置写PC的IP最后一位
SA1:与DA1对应
Parameter:是变化的,相当于数据位

3.Fins读取数据
Fins读取数据会在通用指令的基础上,将Parameter替换为Area+Address+Length,类似于Modbus;Modbus将数据位变为:起始地址+长度 读取数据返回为(数据位:字节计数+数据)
Fins TCP协议_第6张图片
Fins TCP协议_第7张图片
读取数据返回命令Fins TCP协议_第8张图片
Fins TCP协议_第9张图片
4.Fins写入数据
Fins写入数据会在通用指令基础上,将Parameter替换为Area+Address+Length+Value,因此写入数据命令如下:
Fins TCP协议_第10张图片

'''
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 

你可能感兴趣的:(PLC,欧姆龙,tcp/ip,网络,网络协议)