2024-11-05 10:31:38 +03:00
using System ;
using System.Collections.Generic ;
using System.ComponentModel ;
using System.Data ;
using System.Drawing ;
using System.Linq ;
using System.Text ;
using System.Threading.Tasks ;
using System.Windows.Forms ;
using System.IO.Ports ;
2024-12-05 11:10:53 +03:00
using System.Text.RegularExpressions ;
using System.Net.Sockets ;
2024-12-05 15:57:07 +03:00
using System.IO ;
using Newtonsoft.Json ;
2024-12-16 14:31:14 +03:00
using System.Threading ;
2024-11-05 10:31:38 +03:00
2024-12-12 15:14:13 +03:00
2024-11-05 10:31:38 +03:00
namespace Gidrolock_Modbus_Scanner
{
public partial class App : Form
{
2024-12-12 15:14:13 +03:00
public static int [ ] BaudRate = new int [ ]
{
110 , 300 , 1200 , 2400 , 4800 , 9600 , 14400 , 19200 , 28800 , 38400 , 57600 , 76800 , 115200 , 230300 , 460800 , 921600
2024-11-11 10:43:58 +03:00
} ;
public static int [ ] DataBits = new int [ ] { 7 , 8 } ;
2024-12-05 11:10:53 +03:00
Socket socket = new Socket ( SocketType . Stream , ProtocolType . Tcp ) ;
2024-12-06 16:32:10 +03:00
2024-11-11 10:43:58 +03:00
byte [ ] message = new byte [ 255 ] ;
2024-11-05 10:31:38 +03:00
public bool isAwaitingResponse = false ;
public short [ ] res = new short [ 12 ] ;
2024-12-12 15:14:13 +03:00
SerialPort port = Modbus . port ;
2024-11-05 16:57:05 +03:00
public int expectedLength = 0 ;
2024-12-09 16:24:02 +03:00
Datasheet datasheet ;
2024-12-12 15:14:13 +03:00
public SelectedPath selectedPath = SelectedPath . Folder ;
public static Device device ; // Deserialized .json object
string path = String . Empty ; // Path to selected file/folder
string defaultPath = Application . StartupPath + "\\Configs" ; // Default folder path
OpenFileDialog ofd = new OpenFileDialog ( ) ;
FolderBrowserDialog fbd = new FolderBrowserDialog ( ) ;
Dictionary < CheckEntry , List < Device > > juju = new Dictionary < CheckEntry , List < Device > > ( ) ; // dictionary for device identification
string [ ] configs ;
2024-12-05 15:57:07 +03:00
2024-12-12 15:14:13 +03:00
public byte [ ] latestMessage ;
2024-12-16 11:35:20 +03:00
DateTime dateTime ;
2024-12-06 16:32:10 +03:00
#region Initialization
2024-11-05 10:31:38 +03:00
public App ( )
{
InitializeComponent ( ) ;
2024-12-12 15:14:13 +03:00
Modbus . Init ( ) ;
Modbus . ResponseReceived + = OnResponseReceived ;
ofd . InitialDirectory = Application . StartupPath ;
ofd . Filter = "JSON files (*.json)|*.json" ;
ofd . FilterIndex = 2 ;
ofd . RestoreDirectory = true ;
2024-11-05 10:31:38 +03:00
this . UpDown_ModbusID . Value = 30 ;
2024-11-05 16:57:05 +03:00
TextBox_Log . Text = "Приложение готово к работе." ;
2024-12-16 11:35:20 +03:00
CBox_Function . Items . Add ( "01 Read Coil" ) ;
CBox_Function . Items . Add ( "02 Read Discrete Input" ) ;
CBox_Function . Items . Add ( "03 Read Holding Register" ) ;
CBox_Function . Items . Add ( "04 Read Input Register" ) ;
CBox_Function . Items . Add ( "05 Write Single Coil" ) ;
CBox_Function . Items . Add ( "06 Write Single Register" ) ;
CBox_Function . Items . Add ( "0F Write Multiple Coils" ) ;
CBox_Function . Items . Add ( "10 Write Multiple Registers" ) ;
2024-11-05 16:57:05 +03:00
CBox_Function . SelectedItem = CBox_Function . Items [ 0 ] ;
2024-11-11 10:43:58 +03:00
CBox_BaudRate . Items . Add ( "110" ) ;
CBox_BaudRate . Items . Add ( "300" ) ;
CBox_BaudRate . Items . Add ( "1200" ) ;
CBox_BaudRate . Items . Add ( "2400" ) ;
CBox_BaudRate . Items . Add ( "4800" ) ;
CBox_BaudRate . Items . Add ( "9600" ) ;
CBox_BaudRate . Items . Add ( "14400" ) ;
CBox_BaudRate . Items . Add ( "19200" ) ;
CBox_BaudRate . Items . Add ( "28800" ) ;
CBox_BaudRate . Items . Add ( "38400" ) ;
CBox_BaudRate . Items . Add ( "57600" ) ;
CBox_BaudRate . Items . Add ( "76800" ) ;
CBox_BaudRate . Items . Add ( "115200" ) ;
CBox_BaudRate . Items . Add ( "230300" ) ;
CBox_BaudRate . Items . Add ( "460800" ) ;
CBox_BaudRate . Items . Add ( "921600" ) ;
CBox_BaudRate . SelectedIndex = 5 ;
CBox_DataBits . Items . Add ( "7" ) ;
CBox_DataBits . Items . Add ( "8" ) ;
CBox_DataBits . SelectedIndex = 1 ;
CBox_StopBits . Items . Add ( "Нет" ) ;
CBox_StopBits . Items . Add ( "1" ) ;
CBox_StopBits . Items . Add ( "1.5" ) ;
CBox_StopBits . Items . Add ( "2" ) ;
CBox_StopBits . SelectedIndex = 1 ;
2024-12-05 11:10:53 +03:00
CBox_Parity . Items . Add ( "Нет" ) ;
CBox_Parity . Items . Add ( "Четн." ) ;
CBox_Parity . Items . Add ( "Нечетн." ) ;
CBox_Parity . SelectedIndex = 0 ;
2024-12-17 15:56:05 +03:00
UpDown_RegLength . Value = 0 ;
2024-12-05 11:10:53 +03:00
Radio_SerialPort . Checked = true ;
GBox_Ethernet . Enabled = false ;
TBox_IP . Text = "192.168.3.7" ;
TBox_Port . Text = "8887" ;
TBox_Timeout . Text = "3" ;
2024-12-12 15:14:13 +03:00
if ( Directory . GetDirectories ( Application . StartupPath ) . Contains ( Application . StartupPath + "\\Configs" ) = = false )
{
Task . Delay ( 1500 ) . ContinueWith ( t = >
{
MessageBox . Show ( "Приложение не нашло стандартную папку для конфигураций. Была создана папка 'Configs' в папке с приложением." ) ;
Directory . CreateDirectory ( Application . StartupPath + "\\Configs" ) ;
Console . WriteLine ( "New initial directory for OpenFile: " + ofd . InitialDirectory ) ;
} ) ;
}
ofd . InitialDirectory = Application . StartupPath + "\\Configs" ;
path = defaultPath ;
UpdatePathLabel ( ) ;
}
void UpdatePathLabel ( )
{
Label_ConfPath . Text = path ;
2024-11-05 10:31:38 +03:00
}
void App_FormClosed ( object sender , FormClosedEventArgs e )
{
port . Close ( ) ;
if ( ! port . IsOpen )
Application . Exit ( ) ;
}
void Form1_Load ( object sender , EventArgs e )
{
CBox_Ports . Items . AddRange ( SerialPort . GetPortNames ( ) ) ;
if ( CBox_Ports . Items . Count > 0 )
CBox_Ports . SelectedIndex = 0 ;
Init ( ) ;
}
void Init ( )
{
if ( UpDown_ModbusID . Value = = 0 )
2024-12-05 15:57:07 +03:00
Button_Connect . Text = "Найти адрес" ;
else Button_Connect . Text = "Подключиться" ;
2024-11-05 10:31:38 +03:00
}
2024-12-06 16:32:10 +03:00
#endregion
2024-11-05 10:31:38 +03:00
2024-12-06 16:32:10 +03:00
// Send a custom message
2024-12-17 15:56:05 +03:00
async Task ReadRegisterAsync ( FunctionCode functionCode , ushort address , ushort length )
2024-11-05 16:57:05 +03:00
{
2024-11-05 10:31:38 +03:00
if ( CBox_Ports . Text = = "" )
MessageBox . Show ( "Необходимо выбрать COM порт." , "Ошибка" , MessageBoxButtons . OK ) ;
if ( UpDown_ModbusID . Value = = 0 )
MessageBox . Show ( "Глобальное вещание пока не поддерживается" ) ;
2024-11-05 16:57:05 +03:00
2024-12-06 16:32:10 +03:00
/* - Port Setup - */
2024-11-11 10:43:58 +03:00
if ( port . IsOpen )
port . Close ( ) ;
2024-11-05 10:31:38 +03:00
2024-11-11 10:43:58 +03:00
port . Handshake = Handshake . None ;
port . PortName = CBox_Ports . Text ;
port . BaudRate = BaudRate [ CBox_BaudRate . SelectedIndex ] ;
port . Parity = Parity . None ;
port . DataBits = DataBits [ CBox_DataBits . SelectedIndex ] ;
port . StopBits = ( StopBits ) CBox_StopBits . SelectedIndex ;
2024-11-05 10:31:38 +03:00
2024-12-05 15:57:07 +03:00
port . ReadTimeout = 3000 ;
port . WriteTimeout = 3000 ;
2024-11-05 10:31:38 +03:00
2024-11-11 10:43:58 +03:00
message = new byte [ 255 ] ;
port . Open ( ) ;
2024-11-05 16:57:05 +03:00
2024-12-06 16:32:10 +03:00
2024-11-11 10:43:58 +03:00
/* - Reading from Registers - */
if ( CBox_Function . SelectedIndex < 4 )
{
try
2024-12-06 16:32:10 +03:00
{
2024-12-17 16:36:42 +03:00
await Modbus . ReadRegAsync ( port , ( byte ) UpDown_ModbusID . Value , functionCode , address , length ) ;
2024-11-11 10:43:58 +03:00
isAwaitingResponse = true ;
2024-12-09 16:24:02 +03:00
await Task . Delay ( port . ReadTimeout ) . ContinueWith ( _ = >
2024-11-05 16:57:05 +03:00
{
2024-11-11 10:43:58 +03:00
if ( isAwaitingResponse )
{
MessageBox . Show ( "Истекло время ожидания ответа." , "Ошибка" ) ;
port . Close ( ) ;
}
} ) ;
}
catch ( Exception err )
{
port . Close ( ) ;
MessageBox . Show ( err . Message , "Ошибка" , MessageBoxButtons . OK , MessageBoxIcon . Error ) ;
}
2024-11-05 10:31:38 +03:00
}
}
2024-11-05 16:57:05 +03:00
private async void ButtonConnect_Click ( object sender , EventArgs e )
{
2024-12-16 11:35:20 +03:00
progressBar1 . Value = 0 ;
2024-12-12 15:14:13 +03:00
if ( path = = String . Empty )
{
2024-12-05 15:57:07 +03:00
MessageBox . Show ( "Выберите конфигурацию для подключения и опроса устройства." ) ;
2024-12-12 15:14:13 +03:00
return ;
}
if ( CBox_Ports . SelectedItem . ToString ( ) = = "COM1" )
2024-12-05 15:57:07 +03:00
{
2024-12-12 15:14:13 +03:00
DialogResult res = MessageBox . Show ( "Выбран серийный порт COM1, который обычно является портом PS/2 или RS-232, не подключенным к Modbus устройству. Продолжить?" , "Внимание" , MessageBoxButtons . OKCancel ) ;
if ( res = = DialogResult . Cancel )
return ;
}
2024-12-06 16:32:10 +03:00
2024-12-12 15:14:13 +03:00
/* - Port Setup - */
if ( port . IsOpen )
port . Close ( ) ;
2024-12-06 16:32:10 +03:00
2024-12-12 15:14:13 +03:00
port . Handshake = Handshake . None ;
port . PortName = CBox_Ports . Text ;
port . BaudRate = BaudRate [ CBox_BaudRate . SelectedIndex ] ;
port . Parity = Parity . None ;
port . DataBits = DataBits [ CBox_DataBits . SelectedIndex ] ;
port . StopBits = ( StopBits ) CBox_StopBits . SelectedIndex ;
2024-12-06 16:32:10 +03:00
2024-12-12 15:14:13 +03:00
port . ReadTimeout = 3000 ;
port . WriteTimeout = 3000 ;
message = new byte [ 255 ] ;
port . Open ( ) ;
2024-12-06 16:32:10 +03:00
2024-12-16 14:31:14 +03:00
/* - Checking - */
2024-12-12 15:14:13 +03:00
if ( selectedPath = = SelectedPath . File )
{
var fileContent = string . Empty ;
var filePath = string . Empty ;
//Read the contents of the file into a stream
var fileStream = ofd . OpenFile ( ) ;
using ( StreamReader reader = new StreamReader ( fileStream ) )
{
fileContent = reader . ReadToEnd ( ) ;
}
2024-12-16 11:35:20 +03:00
progressBar1 . Value = 100 ;
2024-12-06 16:32:10 +03:00
try
{
2024-12-12 15:14:13 +03:00
device = JsonConvert . DeserializeObject < Device > ( fileContent ) ;
Label_ConfigTip . Text = device . name ;
2024-12-06 16:32:10 +03:00
}
2024-12-16 14:31:14 +03:00
catch ( Exception err ) { MessageBox . Show ( err . Message ) ; }
try
2024-12-06 16:32:10 +03:00
{
2024-12-16 14:31:14 +03:00
AddLog ( "Попытка подключиться к устройству " + device . name ) ;
datasheet = new Datasheet ( ( byte ) UpDown_ModbusID . Value ) ;
datasheet . Show ( ) ;
2024-12-06 16:32:10 +03:00
}
2024-12-16 14:31:14 +03:00
catch ( Exception err ) { MessageBox . Show ( err . Message ) ; }
2024-12-05 15:57:07 +03:00
}
2024-12-12 15:14:13 +03:00
else
{
string [ ] _configs = Directory . GetFiles ( path , "*.json" ) ;
if ( configs ! = _configs )
{
// something changed in the config folder, or we haven't gone through configs,
// remake the dictionary
configs = _configs ;
juju = new Dictionary < CheckEntry , List < Device > > ( ) ;
var fileContent = string . Empty ;
FileStream fileStream ;
Device _device ;
foreach ( string path in configs )
{
fileStream = File . OpenRead ( path ) ;
using ( StreamReader reader = new StreamReader ( fileStream ) )
fileContent = reader . ReadToEnd ( ) ;
// get device object from .json
_device = JsonConvert . DeserializeObject < Device > ( fileContent ) ;
// compare device object to key of each dictionary;
// add to that dictionary if device's check entry registers match the key
2024-12-16 14:31:14 +03:00
bool matched = false ;
2024-12-12 15:14:13 +03:00
foreach ( CheckEntry ce in juju . Keys )
{
if ( _device . checkEntry . address = = ce . address & & _device . checkEntry . length = = ce . length & & _device . checkEntry . dataType = = ce . dataType )
{
juju [ ce ] . Add ( _device ) ;
2024-12-16 14:31:14 +03:00
matched = true ;
2024-12-12 15:14:13 +03:00
break ;
}
}
2024-12-16 14:31:14 +03:00
if ( ! matched )
{
juju . Add ( _device . checkEntry , new List < Device > ( ) ) ;
juju [ _device . checkEntry ] . Add ( _device ) ;
}
} // all configs are sorted out, we can poll for each checkEntry
2024-12-12 15:14:13 +03:00
}
2024-12-16 14:31:14 +03:00
// setup event listener
byte [ ] message = null ;
Modbus . ResponseReceived + = ( sndr , msg ) = >
{
message = msg . Message ;
} ;
2024-12-12 15:14:13 +03:00
foreach ( CheckEntry ce in juju . Keys )
{
2024-12-17 15:56:05 +03:00
await ReadRegisterAsync ( ( FunctionCode ) ce . registerType , ce . address , ce . length ) ; // send read request to device,
2024-12-16 14:31:14 +03:00
while ( message is null ) // wait for response to arrive
Thread . Sleep ( 10 ) ;
if ( message [ 1 ] > 0x10 ) // checking for exception code
continue ;
else
{
// get pure data
byte [ ] data = new byte [ message [ 2 ] ] ;
for ( int i = 0 ; i < data . Length ; i + + )
data [ i ] = message [ i + 3 ] ;
if ( ce . dataType = = "string" )
{
2024-12-16 14:43:50 +03:00
2024-12-16 14:31:14 +03:00
List < byte > bytes = new List < byte > ( ) ;
for ( int i = 0 ; i < data . Length ; i + + )
{
if ( data [ i ] ! = 0 ) // clean empty bytes from 16-bit registers
bytes . Add ( data [ i ] ) ;
}
2024-12-16 14:43:50 +03:00
string value = Encoding . UTF8 . GetString ( bytes . ToArray ( ) ) ;
2024-12-16 14:31:14 +03:00
foreach ( Device dev in juju [ ce ] )
2024-12-16 14:43:50 +03:00
{
2024-12-16 14:31:14 +03:00
if ( dev . checkEntry . expectedValue = = value )
{
2024-12-16 14:43:50 +03:00
Console . WriteLine ( "It's a match!" ) ;
2024-12-16 14:31:14 +03:00
device = dev ;
break ;
}
}
}
2024-12-16 14:43:50 +03:00
else if ( ce . dataType = = "bool" )
2024-12-16 14:31:14 +03:00
{
// why would you even do that lmao
2024-12-18 16:49:58 +03:00
2024-12-16 14:31:14 +03:00
}
2024-12-16 14:43:50 +03:00
else if ( ce . dataType = = "uint16" | | ce . dataType = = "uint32" )
2024-12-16 14:31:14 +03:00
{
byte [ ] _data = data ;
Array . Reverse ( _data ) ;
if ( ce . dataType = = "uint16" )
{
ushort value = BitConverter . ToUInt16 ( _data , 0 ) ;
foreach ( Device dev in juju [ ce ] )
{
short expValue ;
if ( Int16 . TryParse ( dev . checkEntry . expectedValue , out expValue ) & & expValue = = value )
{
device = dev ;
break ;
}
}
}
else
{
int value = BitConverter . ToInt32 ( _data , 0 ) ;
foreach ( Device dev in juju [ ce ] )
{
int expValue ;
if ( Int32 . TryParse ( dev . checkEntry . expectedValue , out expValue ) & & expValue = = value )
{
device = dev ;
break ;
}
}
}
}
}
if ( ! ( device is null ) ) // found the correct device, abort loop
break ;
message = null ; // clear the array for the next item in case we haven't found the correct value
}
if ( device is /* still */ null )
{
// none of the configs match the device responses
MessageBox . Show ( "Ни один из файлов конфигурации не подходит для устройства." ) ;
return ;
}
else
{
2024-12-16 14:33:13 +03:00
try
{
AddLog ( "Попытка подключиться к устройству " + device . name ) ;
datasheet = new Datasheet ( ( byte ) UpDown_ModbusID . Value ) ;
datasheet . Show ( ) ;
}
catch ( Exception err ) { MessageBox . Show ( err . Message ) ; }
2024-12-12 15:14:13 +03:00
}
2024-12-16 15:18:05 +03:00
progressBar1 . Value = 100 ;
2024-12-12 15:14:13 +03:00
}
/ *
if ( Radio_SerialPort . Checked )
2024-12-17 15:56:05 +03:00
await ReadRegisterAsync ( FunctionCode . InputRegister , 200 , 6 ) ;
2024-12-12 15:14:13 +03:00
//else EthernetParse();
* /
2024-12-05 11:10:53 +03:00
}
async void EthernetParse ( )
{
string ipText = TBox_IP . Text ;
string portText = TBox_Port . Text ;
Regex ip = new Regex ( @"\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b" ) ;
Regex port = new Regex ( @"\d" ) ;
if ( ! ip . IsMatch ( ipText ) )
MessageBox . Show ( "Неправильный формат IP-адреса." ) ;
else if ( ! port . IsMatch ( portText ) )
MessageBox . Show ( "Неправильный формат TCP-порта." ) ;
else
{
int portParsed = Int32 . Parse ( portText ) ;
await socket . ConnectAsync ( ipText , portParsed ) ;
byte [ ] data = new byte [ 8 ] ;
2024-12-17 16:36:42 +03:00
Modbus . BuildReadMessage ( 0x1E , 0x03 , 128 , 1 , ref data ) ;
2024-12-06 16:32:10 +03:00
AddLog ( "Sending to " + ipText + ":" + portText + ":" + Modbus . ByteArrayToString ( data ) ) ;
2024-12-05 11:10:53 +03:00
// set up an event listener to receive the response
await SocketDataTransfer ( data ) ;
}
2024-12-12 15:14:13 +03:00
2024-11-05 16:57:05 +03:00
}
2024-11-05 10:31:38 +03:00
void CBox_Ports_Click ( object sender , EventArgs e )
{
CBox_Ports . Items . Clear ( ) ;
CBox_Ports . Items . AddRange ( SerialPort . GetPortNames ( ) ) ;
}
2024-12-12 15:14:13 +03:00
void OnResponseReceived ( object sender , ModbusResponseEventArgs e )
2024-11-05 10:31:38 +03:00
{
isAwaitingResponse = false ;
2024-12-18 11:28:20 +03:00
AddLog ( "Получен ответ: " + Modbus . ByteArrayToString ( e . Message ) ) ;
switch ( e . Status )
{
case ( ModbusStatus . ReadSuccess ) :
2024-12-18 14:28:06 +03:00
string values = "" ;
if ( e . Message [ 1 ] < = 0x02 )
{
for ( int i = 0 ; i < e . Data . Length ; i + + )
{
values + = Convert . ToString ( e . Data [ i ] , 2 ) . PadLeft ( 8 , '0' ) + " " ;
}
AddLog ( "Bin: " + values ) ;
}
2024-12-18 11:28:20 +03:00
if ( e . Message [ 1 ] = = 0x03 | | e . Message [ 1 ] = = 0x04 )
{
2024-12-18 14:28:06 +03:00
for ( int i = 0 ; i < e . Data . Length ; i + = 2 )
{
values + = ( ( e . Data [ i ] < < 8 ) + e . Data [ i + 1 ] ) . ToString ( ) + "; " ;
}
AddLog ( "Dec: " + values ) ;
2024-12-18 11:28:20 +03:00
AddLog ( "Unicode: " + ByteArrayToUnicode ( e . Data ) ) ;
}
break ;
case ( ModbusStatus . WriteSuccess ) :
AddLog ( "Write success;" ) ;
break ;
case ( ModbusStatus . Error ) :
string errorDesc ;
switch ( e . Message [ 2 ] )
{
case ( 0x01 ) :
errorDesc = "01 - Illegal Function" ;
break ;
case ( 0x02 ) :
errorDesc = "02 - Illegal Data Address" ;
break ;
case ( 0x03 ) :
errorDesc = "03 - Illegal Data Value" ;
break ;
case ( 0x04 ) :
errorDesc = "04 - Slave Device Failure" ;
break ;
case ( 0x05 ) :
errorDesc = "05 - Acknowledge" ;
break ;
case ( 0x06 ) :
errorDesc = "06 - Slave Device Busy" ;
break ;
case ( 0x07 ) :
errorDesc = "07 - Negative Acknowledge" ;
break ;
case ( 0x08 ) :
errorDesc = "08 - Memory Parity Error" ;
break ;
case ( 0x0A ) :
errorDesc = "10 - Gateway Path Unavailable" ;
break ;
case ( 0x0B ) :
errorDesc = "11 - Gateway Target Device Failed to Respond" ;
break ;
default :
errorDesc = "Unknown error code" ;
break ;
}
AddLog ( "Error code: " + errorDesc ) ;
break ;
}
2024-11-05 16:57:05 +03:00
}
void AddLog ( string message )
{
2024-12-16 11:35:20 +03:00
dateTime = DateTime . Now ;
2024-12-18 11:28:20 +03:00
TextBox_Log . Invoke ( ( MethodInvoker ) delegate { TextBox_Log . AppendText ( Environment . NewLine + "[" + dateTime . Hour + ":" + dateTime . Minute + ":" + dateTime . Second + "] " + message ) ; } ) ;
2024-11-05 16:57:05 +03:00
}
private async void Button_SendCommand_Click ( object sender , EventArgs e )
{
2024-12-18 11:28:20 +03:00
/* - Port Setup - */
if ( port . IsOpen )
port . Close ( ) ;
port . Handshake = Handshake . None ;
port . PortName = CBox_Ports . Text ;
port . BaudRate = BaudRate [ CBox_BaudRate . SelectedIndex ] ;
port . Parity = Parity . None ;
port . DataBits = DataBits [ CBox_DataBits . SelectedIndex ] ;
port . StopBits = ( StopBits ) CBox_StopBits . SelectedIndex ;
port . ReadTimeout = 3000 ;
port . WriteTimeout = 3000 ;
message = new byte [ 255 ] ;
port . Open ( ) ;
2024-12-17 15:56:05 +03:00
int functionCode = CBox_Function . SelectedIndex + 1 ;
2024-12-18 14:28:06 +03:00
Console . WriteLine ( "Set fCode: " + functionCode ) ;
2024-12-17 15:56:05 +03:00
short address ;
2024-11-05 16:57:05 +03:00
ushort length = ( ushort ) UpDown_RegLength . Value ;
2024-12-17 15:56:05 +03:00
if ( Int16 . TryParse ( TBox_RegAddress . Text , out address ) )
{
if ( functionCode < = 4 )
await ReadRegisterAsync ( ( FunctionCode ) functionCode , ( ushort ) address , length ) ;
else
{
string valueLower = TBox_RegValue . Text . ToLower ( ) ;
switch ( ( FunctionCode ) functionCode )
{
case ( FunctionCode . WriteCoil ) :
2024-12-17 16:55:55 +03:00
Console . WriteLine ( "Trying to force single coil" ) ;
2024-12-17 15:56:05 +03:00
if ( valueLower = = "true" | | valueLower = = "1" )
2024-12-17 16:36:42 +03:00
await Modbus . WriteSingleAsync ( port , ( FunctionCode ) functionCode , ( byte ) UpDown_ModbusID . Value , ( ushort ) address , 0xFF _00 ) ;
2024-12-17 15:56:05 +03:00
else if ( valueLower = = "false" | | valueLower = = "0" )
2024-12-17 16:36:42 +03:00
await Modbus . WriteSingleAsync ( port , ( FunctionCode ) functionCode , ( byte ) UpDown_ModbusID . Value , ( ushort ) address , 0x00 _00 ) ;
2024-12-17 15:56:05 +03:00
else MessageBox . Show ( "Неподходящие значения для регистра типа Coil" ) ;
break ;
case ( FunctionCode . WriteRegister ) :
2024-12-17 16:36:42 +03:00
short value = 0x00 _00 ;
bool canWrite = false ;
2024-12-18 14:28:06 +03:00
if ( IsDec ( valueLower ) )
{
try { value = Convert . ToInt16 ( valueLower ) ; canWrite = true ; }
catch ( Exception err ) { MessageBox . Show ( err . Message ) ; }
}
else if ( IsHex ( valueLower ) )
2024-12-17 15:56:05 +03:00
{
2024-12-18 14:28:06 +03:00
Console . WriteLine ( "Got hex value" ) ;
for ( int i = 0 ; i < valueLower . Length ; i + + )
{
if ( valueLower [ i ] = = 'x' )
{
valueLower = valueLower . Remove ( i , 1 ) ;
break ;
}
}
2024-12-17 16:36:42 +03:00
try { value = Convert . ToInt16 ( valueLower , 16 ) ; canWrite = true ; }
2024-12-17 15:56:05 +03:00
catch ( Exception err ) { MessageBox . Show ( err . Message ) ; }
}
2024-12-18 14:28:06 +03:00
else if ( IsBin ( valueLower ) )
2024-12-17 15:56:05 +03:00
{
2024-12-18 14:28:06 +03:00
Console . WriteLine ( "Got bin value" ) ;
for ( int i = 0 ; i < valueLower . Length ; i + + )
{
if ( valueLower [ i ] = = 'b' )
{
valueLower = valueLower . Remove ( i , 1 ) ;
break ;
}
}
try { value = Convert . ToInt16 ( valueLower , 2 ) ; canWrite = true ; }
2024-12-17 15:56:05 +03:00
catch ( Exception err ) { MessageBox . Show ( err . Message ) ; }
}
2024-12-17 16:36:42 +03:00
else if ( valueLower = = "true" | | valueLower = = "1" )
2024-12-17 15:56:05 +03:00
{
2024-12-17 16:36:42 +03:00
value = 0x00 _01 ;
canWrite = true ;
2024-12-17 15:56:05 +03:00
}
2024-12-17 16:36:42 +03:00
else if ( valueLower = = "false" | | valueLower = = "0" )
2024-12-17 15:56:05 +03:00
{
2024-12-17 16:36:42 +03:00
canWrite = true ;
}
2024-12-18 14:28:06 +03:00
else
2024-12-17 16:36:42 +03:00
{
2024-12-18 14:28:06 +03:00
MessageBox . Show ( "Неподходящие значения для регистра типа Input Register" ) ;
break ;
2024-12-17 15:56:05 +03:00
}
2024-12-18 14:28:06 +03:00
if ( canWrite )
await Modbus . WriteSingleAsync ( port , ( FunctionCode ) functionCode , ( byte ) UpDown_ModbusID . Value , ( ushort ) address ,
( ushort ) value ) ;
2024-12-17 15:56:05 +03:00
break ;
default :
2024-12-18 14:28:06 +03:00
MessageBox . Show ( "WIP" ) ;
2024-12-17 15:56:05 +03:00
break ;
2024-11-05 16:57:05 +03:00
2024-12-17 15:56:05 +03:00
}
}
}
2024-11-05 10:31:38 +03:00
}
2024-11-11 10:43:58 +03:00
private void OnSelectedFunctionChanged ( object sender , EventArgs e )
{
if ( CBox_Function . SelectedIndex < 4 )
2024-12-17 15:56:05 +03:00
TBox_RegValue . Enabled = false ;
else TBox_RegValue . Enabled = true ;
2024-12-18 14:28:06 +03:00
if ( CBox_Function . SelectedIndex = = 4 | | CBox_Function . SelectedIndex = = 5 )
UpDown_RegLength . Enabled = false ;
else UpDown_RegLength . Enabled = true ;
2024-11-11 10:43:58 +03:00
}
2024-12-05 11:10:53 +03:00
private void Radio_SerialPort_CheckedChanged ( object sender , EventArgs e )
{
if ( Radio_SerialPort . Checked )
GBox_Serial . Enabled = true ;
else GBox_Serial . Enabled = false ;
}
private void Radio_Ethernet_CheckedChanged ( object sender , EventArgs e )
{
if ( Radio_Ethernet . Checked )
GBox_Ethernet . Enabled = true ;
else GBox_Ethernet . Enabled = false ;
}
private async Task < bool > SocketDataTransfer ( byte [ ] data )
{
await Task . Run ( ( ) = > { socket . Send ( data ) ; } ) ;
byte [ ] res = new byte [ 64 ] ;
await Task . Run ( ( ) = >
{
while ( true )
{
int bytesReceived = socket . Receive ( res ) ;
if ( bytesReceived = = 0 )
break ;
string resParsed = "" ;
Modbus . ParseResponse ( res , ref resParsed ) ;
Console . Out . WriteLine ( "Received data on TCP socket: " + resParsed ) ;
AddLog ( "Response from TCP Server: " + resParsed ) ;
2024-12-12 15:14:13 +03:00
}
2024-12-05 11:10:53 +03:00
} ) ;
return true ;
}
2024-12-05 15:57:07 +03:00
private void LoadConfig ( object sender , EventArgs e )
{
2024-12-12 15:14:13 +03:00
if ( ofd . ShowDialog ( ) = = DialogResult . OK )
2024-12-05 15:57:07 +03:00
{
2024-12-12 15:14:13 +03:00
//Get the path of specified file
path = ofd . FileName ;
Label_ConfPath . Text = ofd . FileName ;
2024-12-05 15:57:07 +03:00
2024-12-12 15:14:13 +03:00
selectedPath = SelectedPath . File ;
}
UpdatePathLabel ( ) ;
}
2024-12-05 15:57:07 +03:00
2024-12-12 15:14:13 +03:00
private void LoadFolder ( object sender , EventArgs e )
{
2024-12-05 15:57:07 +03:00
2024-12-12 15:14:13 +03:00
fbd . RootFolder = Environment . SpecialFolder . MyComputer ;
if ( fbd . ShowDialog ( ) = = DialogResult . OK )
{
path = fbd . SelectedPath ;
Label_ConfPath . Text = fbd . SelectedPath ;
selectedPath = SelectedPath . Folder ;
2024-12-05 15:57:07 +03:00
}
2024-12-12 15:14:13 +03:00
UpdatePathLabel ( ) ;
2024-12-05 15:57:07 +03:00
}
2024-12-16 11:35:20 +03:00
private void CBox_Ports_Click ( object sender , MouseEventArgs e )
{
}
2024-12-17 15:56:05 +03:00
public static bool IsHex ( string str )
{
str = str . ToLower ( ) ;
for ( int i = 0 ; i < str . Length ; i + + )
{
if ( str [ i ] < '0' | | str [ i ] > 'F' )
{
if ( ( i = = 0 | | i = = 1 ) & & str [ i ] = = 'x' )
continue ;
else return false ;
}
}
return true ;
}
public static bool IsDec ( string str )
{
foreach ( char c in str )
{
if ( c < '0' | | c > '9' )
return false ;
}
return true ;
}
2024-12-18 14:28:06 +03:00
public static bool IsBin ( string str )
{
str = str . ToLower ( ) ;
for ( int i = 0 ; i < str . Length ; i + + )
{
if ( str [ i ] ! = '0' & & str [ i ] ! = '1' )
{
if ( ( i = = 0 | | i = = 1 ) & & str [ i ] = = 'b' )
continue ;
else return false ;
}
}
return true ;
}
2024-12-18 11:28:20 +03:00
public static string ByteArrayToUnicode ( byte [ ] input )
{
// stupid fucking WinForm textbox breaks from null symbols
// stupid fucking Encoding class does byte-by-byte conversion
List < char > result = new List < char > ( input . Length / 2 ) ;
byte [ ] flip = input ;
2024-12-18 14:28:06 +03:00
Array . Reverse ( flip ) ; // stupid fucking BitConverter is little-endian and spits out chinese nonsense otherwise
2024-12-18 11:28:20 +03:00
for ( int i = 0 ; i < flip . Length ; i + = 2 )
{
result . Add ( BitConverter . ToChar ( flip , i ) ) ;
}
result . Reverse ( ) ;
return new string ( result . ToArray ( ) ) ;
}
2024-11-05 10:31:38 +03:00
}
2024-12-12 15:14:13 +03:00
}
2024-11-05 10:31:38 +03:00
2024-12-12 15:14:13 +03:00
public enum FunctionCode { ReadCoil = 1 , ReadDiscrete = 2 , ReadHolding = 3 , ReadInput = 4 , WriteCoil = 5 , WriteRegister = 6 , WriteMultCoils = 15 , WriteMultRegisters = 16 } ;
public enum SelectedPath { File , Folder } ;