Не помню, он или нет
Исходник в пнд скину, всё там, на работе лежит...
Вид для печати
Если кому-то срочно надо, то саму библиотеку я вспомнил: S7netplus
https://www.nuget.org/packages/S7netplus
https://github.com/S7NetPlus/s7netplus
https://github.com/S7NetPlus/s7netplus/wiki
Ну, модбас я вспомню из тысячи: NModbus. Мы с ним много лет рука-об-руку идём :)
https://www.nuget.org/packages/NModbus
Но исходники всё равно в пнд
UPD
ChatGPT. Примерно так я и делал в итоге:
Вот это пригодится. А то наши программеры думают как с сименсом подружиться.
Как обещал, исходники
У меня нет такого проекта, где я одновременно читаю Профинет, а потом создаю Modbus-сервер. Это были всегда разные задачи
1) Чтение S7-1200. Запись мне была не нужна, но, судя по Wiki, просто вместо Plc.Read нужно будет сделать Plc.Write
2) ModbusTCP сервер:Код:Imports System.Threading 'Для Mutex-ов
Imports S7.Net 'Для Siemens
'Класс опроса паллетайзера
Public Class ОпросПаллетайзера
'Блокировка доступа к переменным
Public Mutex As Mutex = New Mutex()
'Признак связи c АСУ
Public PrConnectASU As Boolean = True
'Создание потока
Public Поток As New System.Threading.Thread(AddressOf ГлавныйПоток)
'Параметры класса
Public ВсегоПаллетов As New Var
'Разное
Dim TimeBadWork As Integer = 0 'Счётчик неудачных попыток коннекта
Dim PrEndWork As Boolean = False 'Признак конца работы
'Функция, запускаемая один раз, при создании класса
Public Sub New()
'Запуск потока
Поток.Start()
End Sub
'Главный поток
Private Sub ГлавныйПоток()
'Бесконечный цикл
While PrEndWork = False
Try
'Подключаемся
Dim Plc As New Plc(CpuType.S71200, "192.168.231.10", 0, 1) 'Создаем объект подключения к контроллеру Siemens S7-1200 по TCP/IP
Plc.Open() ' открываем соединение
'Читаем
Dim Data1 As Long = Plc.Read("DB1006.DBD44") 'Wrapped_pallet 'Counter for wrapped pallet 'Счётчик паллетов, находящийся в блоке DB1006 и имеющий смещение DBD44 с типом DInt (8 байтов ~ Long в VB)
'Закрываем
Plc.Close()
'Закрываем доступ к защищённым ресурсам потока
Mutex.WaitOne()
'Достоверность
ВсегоПаллетов.Valid = True
'Значение
ВсегоПаллетов.Value = Data1
'Открываем доступ к защищённым ресурсам потока
Mutex.ReleaseMutex()
'Счётчик неудачных попыток коннекта
TimeBadWork = 0
Catch Ex As Exception
'Закрываем доступ к защищённым ресурсам потока
Mutex.WaitOne()
'Достоверность
ВсегоПаллетов.Valid = False
'Открываем доступ к защищённым ресурсам потока
Mutex.ReleaseMutex()
'Ошибка
Dim Err = Ex.ToString 'Только для отладки
TimeBadWork = TimeBadWork + 1 'Счётчик неудачных попыток коннекта
End Try
'Признак связи c АСУ
PrConnectASU = (TimeBadWork < 10)
'Пауза в потоке
System.Threading.Thread.Sleep(5000)
End While
End Sub
'Штатное завершение работы
Public Sub EndWork()
PrEndWork = True 'Признак конца работы
End Sub
End Class
3) ModbusTCP клиентКод:using System.Threading; //Потоки
using System.Net; //Работа в сети
using System.Net.Sockets; //Работа в сети
using NModbus; //Modbus
using NModbus.Data; //Modbus
using System;
//Модбас-Сервер
public class Modbus_TCP_Server
{
//Создание потоков
private Thread ЗапускСервера;
private Thread Поток1;
//Сеть
private IModbusSlaveNetwork Network;
//Данные
private ISlaveDataStore dataStore1 = new SlaveDataStore();
private ISlaveDataStore dataStore2 = new SlaveDataStore();
//Разное
private bool PrEndWork = false; //Признак конца работы
//Собственно, вес
public float Ves = 0;
public Modbus_TCP_Server()
{
//Запуск потоков
ЗапускСервера = new Thread(ГлавныйЗапускСервера);
Поток1 = new Thread(ГлавныйПоток1);
ЗапускСервера.Start();
Поток1.Start();
}
//Запуск сервера
public void ГлавныйЗапускСервера()
{
//Порт и IP
int Port = 50300;
IPAddress IP = ((IPAddress[])Dns.GetHostAddresses(Dns.GetHostName()))[1];
//Создаём сервер
TcpListener slaveTcpListener = new TcpListener(IP, Port);
slaveTcpListener.Start();
//Создаём данные
IModbusFactory factory = new ModbusFactory();
Network = factory.CreateSlaveNetwork(slaveTcpListener);
//Создаём Slave-адреса на Modbus-Сервере
IModbusSlave slave1 = factory.CreateSlave(1, dataStore1);
IModbusSlave slave2 = factory.CreateSlave(2, dataStore2);
Network.AddSlave(slave1);
Network.AddSlave(slave2);
//Бесконечный цикл
try
{
Network.ListenAsync().GetAwaiter().GetResult();
Thread.Sleep(Timeout.Infinite);
}
catch (Exception ex)
{
string Test = ex.ToString();
}
}
//Главный поток, где, собственно, происходит обновление данных
public void ГлавныйПоток1()
{
//Бесконечный цикл
while (PrEndWork == false)
{
//Массив для записи на ModbusTCP
ushort[] MassivToModbusTCP = new ushort[11];
//Значения
int Num = 0; //Номер параметра
Num = Num + 1; GetToModbusParamsFromVar(ref MassivToModbusTCP, Num, Ves, true); //Вес
//Запись на Modbus
dataStore1.HoldingRegisters.WritePoints(0, MassivToModbusTCP);
//dataStore2.HoldingRegisters.WritePoints(0, MassivToModbusTCP);
//Пауза в потоке
System.Threading.Thread.Sleep(1000);
}
}
//Функция записи на модбас параметров вара
public void GetToModbusParamsFromVar(ref ushort[] MassivToModbusTCP, int Num, float Var, bool Valid) //Массив Модбаса, номер параметра, Вар для записи на Модбас, Достоверность для записи на Модбас
{
//Массив байтов из значения вара
byte[] TempMas = BitConverter.GetBytes(Var);
//Пишем массив для Modbus-а
MassivToModbusTCP[(Num - 1) * 3 + 0] = (ushort)(TempMas[1] * 256 + TempMas[0]); //0 и 1 байт
MassivToModbusTCP[(Num - 1) * 3 + 1] = (ushort)(TempMas[3] * 256 + TempMas[2]); //2 и 3 байт
MassivToModbusTCP[(Num - 1) * 3 + 2] = (ushort)(Valid ? 1 : 0); //Достоверность
}
//Штатное завершение работы
public void EndWork()
{
PrEndWork = true;
Network.Dispose(); //Прибиваем сеть, из-за чего завершаются все циклы
}
}
4) ModbusCom клиент (Здесь я кучу всего удалил для примера, так как в классе было много ещё чего лишнего, к модбасу не относящееся)Код:Imports NModbus 'Modbus
Imports NModbus.Data 'Modbus
Imports System.Threading
Public Class Водоподготовка
'Создание потока
Dim Поток1 As New System.Threading.Thread(AddressOf ГлавныйПоток1)
'Блокировка доступа к переменным
Public Mutex As Mutex = New Mutex()
'Признак связи c АСУ
Public PrConnectASU As Boolean
'Главные глобальные внешние параметры нашей АСУТП
Public ПараметрАналог(100) As Double 'Аналоговые датчики
Public ПризнакАварии As Boolean 'Дискретный признак аварии
'Разное
Dim TimeBadWork As Integer 'Счётчик неудачных попыток коннекта
Dim PrEndWork 'Признак конца работы
'Функция, запускаемая один раз, при создании класса
Public Sub New()
'Запуск потоков
Поток1.Start()
End Sub
'Главный поток
Private Sub ГлавныйПоток1()
'Бесконечный цикл
While PrEndWork = False
'Закрываем доступ к защищённым ресурсам потока
Mutex.WaitOne()
Try
'Подключаемся
Dim Factory = New ModbusFactory
Dim TcpClient As New TcpClient("192.168.0.110", 8000)
TcpClient.SendTimeout = 5000 'Таймауты
TcpClient.ReceiveTimeout = 5000 'Таймауты
Dim Master As IModbusMaster = Factory.CreateMaster(TcpClient)
Master.Transport.ReadTimeout = 5000 'Таймауты
Master.Transport.WriteTimeout = 5000 'Таймауты
Dim HoldingRegisterIn1 = Master.ReadHoldingRegisters(0, 0, 100) 'Читаем 100 регистров, первый параметр - адрес ModBus-устройства, второй - адрес регистра, третий - количество читаемых регистров (битов)
Dim HoldingRegisterIn2 = Master.ReadHoldingRegisters(0, 100, 106) 'Читаем 106 регистров, первый параметр - адрес ModBus-устройства, второй - адрес регистра, третий - количество читаемых регистров (битов)
Dim HoldingRegisterIn = HoldingRegisterIn1.Concat(HoldingRegisterIn2).ToArray 'Объединим 2 массива
'Параметры
ПараметрАналог(1) = GetSingle(HoldingRegisterIn(6), HoldingRegisterIn(7)) '[Давление В-1 (Из города)]
ПараметрАналог(2) = GetSingle(HoldingRegisterIn(10), HoldingRegisterIn(11)) '[Давление В-1 (В корпуса 1, 2, 3)]
ПараметрАналог(3) = GetSingle(HoldingRegisterIn(14), HoldingRegisterIn(15)) '[Давление В-3 (После перекачного насоса)]
ПараметрАналог(4) = GetSingle(HoldingRegisterIn(184), HoldingRegisterIn(185)) '[Давление В-3 (После насосов УЗВ)]
ПараметрАналог(5) = GetSingle(HoldingRegisterIn(16), HoldingRegisterIn(17)) '[Уровень ёмкости водоподготовка]
ПараметрАналог(6) = GetSingle(HoldingRegisterIn(176), HoldingRegisterIn(177)) '[Уровень ёмкости УЗВ]
ПараметрАналог(7) = GetSingle(HoldingRegisterIn(0), HoldingRegisterIn(1)) '[Температура в помещении]
ПараметрАналог(8) = GetSingle(HoldingRegisterIn(162), HoldingRegisterIn(163)) '[Расход В1. Датчик 10л]
ПараметрАналог(9) = GetSingle(HoldingRegisterIn(164), HoldingRegisterIn(165)) '[Расход В1. Датчик 100л]
ПараметрАналог(10) = GetSingle(HoldingRegisterIn(160), HoldingRegisterIn(161)) '[Расход В1. Суммарный]
ПараметрАналог(11) = GetSingle(HoldingRegisterIn(166), HoldingRegisterIn(167)) '[Расход В3. Датчик 100л]
ПараметрАналог(12) = GetSingle(HoldingRegisterIn(72), HoldingRegisterIn(73)) '[Расход В3. Суммарный]
ПараметрАналог(13) = GetSingle(HoldingRegisterIn(74), HoldingRegisterIn(75)) '[Расход УЗВ. Датчик 10л]
ПараметрАналог(14) = GetSingle(HoldingRegisterIn(76), HoldingRegisterIn(77)) '[Расход УЗВ. Датчик 100л]
ПараметрАналог(15) = GetSingle(HoldingRegisterIn(78), HoldingRegisterIn(79)) '[Расход УЗВ. Суммарный]
'Совокупная авария
ПризнакАварии = Val(Strings.Mid(DecToBin(HoldingRegisterIn(150)), 8, 1)) 'Адрес 150.08. Совокупная авария
'Счётчик неудачных попыток коннекта
TimeBadWork = 0
Catch Ex As Exception
Dim Err = Ex.ToString 'Только для отладки
TimeBadWork = TimeBadWork + 1 'Счётчик неудачных попыток коннекта
End Try
'Признак связи c АСУ
PrConnectASU = (TimeBadWork < 10)
'Открываем доступ к защищённым ресурсам потока
Mutex.ReleaseMutex()
'Пауза в потоке
System.Threading.Thread.Sleep(2000)
End While
End Sub
'Штатное завершение работы
Public Sub EndWork()
PrEndWork = True
End Sub
End Class
Код:using System.Collections.Generic;
using System.Text;
using System.Diagnostics;
using System.Threading;
using System.IO.Ports;
using System.Windows.Forms;
using System.Collections;
using System.IO;
using System;
//Работа с ModBus
using NModbus; //Modbus
using NModbus.Data; //Modbus
using NModbus.Serial; //Modbus
namespace AkvaTex
{
public class SignalPro
{
//Отдельный поток на опрос модбаса (ОВЕНовский модуль)
public void ThreadNewModBus()
{
while (!m_stop) //Действия циклически продолжаются до остановки потока
{
//Проверяем признак имитации, чтобы не трогать модбасы, если включена имитация
if (fImitator)
{
//Тут ничего нет. Пока что...
}
else
{
//Пишем выходы на ОВЕНовский модуль
try
{
if (!sPortModBus.IsOpen)
{
sPortModBus.PortName = Microsoft.Win32.Registry.CurrentUser.OpenSubKey("Software\\Akvatex_05_2012\\").GetValue("comPortTemperature").ToString();
sPortModBus.BaudRate = 9600;
sPortModBus.DataBits = 8;
sPortModBus.Parity = System.IO.Ports.Parity.None;
sPortModBus.StopBits = System.IO.Ports.StopBits.One;
sPortModBus.ReadTimeout = 500;
sPortModBus.Open();
}
var Factory = new ModbusFactory();
IModbusMaster ModbusMaster = Factory.CreateRtuMaster(new SerialPortAdapter(sPortModBus)); //Создаём Модбас-Мастера
ModbusMaster.Transport.ReadTimeout = 500; //Таймауты
ModbusMaster.Transport.WriteTimeout = 500; //Таймауты
ushort[] RegistersToModbus = { 0 }; //Массив регистров для записи на Мобдас. Именно массив, а не отдельный регистр, т.к., модуль, отчего-то не хочет работать по команде 6 (WriteSingleRegister), а хочет работать только по команде 16 (WriteMultipleRegisters)
RegistersToModbus[0] = (ushort)(RegistersToModbus[0] + 001 * komPK_240_1); //ПК-240/1
RegistersToModbus[0] = (ushort)(RegistersToModbus[0] + 002 * komPZ_222_2); //ПЗ-222/2
RegistersToModbus[0] = (ushort)(RegistersToModbus[0] + 004 * komPK_203); //ПК-203
RegistersToModbus[0] = (ushort)(RegistersToModbus[0] + 008 * 0); //Свободный
RegistersToModbus[0] = (ushort)(RegistersToModbus[0] + 016 * 0); //Свободный
RegistersToModbus[0] = (ushort)(RegistersToModbus[0] + 032 * 0); //Свободный
RegistersToModbus[0] = (ushort)(RegistersToModbus[0] + 064 * 0); //Свободный
RegistersToModbus[0] = (ushort)(RegistersToModbus[0] + 128 * 0); //Свободный
ModbusMaster.WriteMultipleRegisters(2, 50, RegistersToModbus); //Пишем на модбас: адрес, регистр, значение
ModbusMaster.Dispose();
sPortModBus.Close();
}
catch
{
}
}
//Пауза потока
Thread.Sleep(100);
}
}
}
}