2024-12-05 15:57:07 +03:00
using System ;
using System.Collections.Generic ;
using System.ComponentModel ;
using System.Data ;
using System.Drawing ;
2024-12-06 16:32:10 +03:00
using System.IO.Ports ;
2024-12-05 15:57:07 +03:00
using System.Linq ;
2024-12-09 16:24:02 +03:00
using System.Net ;
2024-12-06 16:32:10 +03:00
using System.Runtime.Remoting.Messaging ;
2024-12-05 15:57:07 +03:00
using System.Text ;
2024-12-24 15:11:41 +03:00
using System.Threading ;
2024-12-05 15:57:07 +03:00
using System.Threading.Tasks ;
using System.Windows.Forms ;
namespace Gidrolock_Modbus_Scanner
{
public partial class Datasheet : Form
{
2024-12-16 11:35:20 +03:00
bool isPolling = false ;
2024-12-09 16:24:02 +03:00
bool isAwaitingResponse = false ;
byte [ ] message = new byte [ 255 ] ;
2024-12-06 16:32:10 +03:00
int timeout = 3000 ;
2024-12-05 15:57:07 +03:00
int pollDelay = 250 ; // delay between each entry poll, ms
2024-12-06 16:32:10 +03:00
byte slaveID ;
2024-12-05 17:52:25 +03:00
Device device = App . device ;
2024-12-06 16:32:10 +03:00
List < Entry > entries ;
2024-12-09 16:24:02 +03:00
int activeEntryIndex ; // entry index for modbus responses
2024-12-23 16:30:35 +03:00
int activeDGVIndex ; // index for DGV rows
2024-12-12 15:14:13 +03:00
SerialPort port = Modbus . port ;
2024-12-09 16:24:02 +03:00
bool closed = false ;
2024-12-06 16:32:10 +03:00
public Datasheet ( byte slaveID )
2024-12-05 15:57:07 +03:00
{
2024-12-12 15:14:13 +03:00
Modbus . ResponseReceived + = PublishResponse ;
2024-12-06 16:32:10 +03:00
this . slaveID = slaveID ;
entries = device . entries ;
2024-12-05 17:52:25 +03:00
InitializeComponent ( ) ;
2024-12-05 15:57:07 +03:00
2024-12-16 11:35:20 +03:00
Label_DeviceName . Text = device . name ;
Label_Description . Text = device . description ;
2024-12-06 16:32:10 +03:00
DGV_Device . SelectionMode = DataGridViewSelectionMode . FullRowSelect ;
DGV_Device . MultiSelect = false ;
DGV_Device . AutoSizeRowsMode = DataGridViewAutoSizeRowsMode . DisplayedCellsExceptHeaders ;
DGV_Device . AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode . Fill ;
2024-12-05 17:52:25 +03:00
2024-12-06 16:32:10 +03:00
DGV_Device . Columns . Add ( "#" , "#" ) ;
DGV_Device . Columns [ 0 ] . FillWeight = 20 ;
2024-12-24 15:11:41 +03:00
DGV_Device . Columns [ 0 ] . ReadOnly = true ;
2024-12-06 16:32:10 +03:00
DGV_Device . Columns . Add ( "Name" , "Имя" ) ;
DGV_Device . Columns [ 1 ] . FillWeight = 40 ;
2024-12-24 15:11:41 +03:00
DGV_Device . Columns [ 1 ] . ReadOnly = true ;
2024-12-06 16:32:10 +03:00
DGV_Device . Columns . Add ( "Value" , "Значение" ) ;
DGV_Device . Columns [ 2 ] . FillWeight = 60 ;
DGV_Device . Columns . Add ( "Address" , "Адрес" ) ;
DGV_Device . Columns [ 3 ] . FillWeight = 30 ;
2024-12-24 15:11:41 +03:00
DGV_Device . Columns [ 3 ] . ReadOnly = true ;
2024-12-06 16:32:10 +03:00
DGV_Device . Columns . Add ( new DataGridViewCheckBoxColumn ( ) ) ;
DGV_Device . Columns [ 4 ] . Name = "Опрос" ;
DGV_Device . Columns [ 4 ] . FillWeight = 20 ;
2024-12-16 15:52:01 +03:00
DGV_Device . Columns [ 4 ] . ValueType = typeof ( bool ) ;
2024-12-23 16:30:35 +03:00
int rowCount = 0 ;
2024-12-24 15:11:41 +03:00
DGV_Device . CellEndEdit + = ( o , e ) = >
{
if ( e . ColumnIndex = = 2 )
{
2024-12-25 13:59:04 +03:00
int y = e . RowIndex ;
Console . WriteLine ( "Row " + y + " current value: " + DGV_Device . Rows [ y ] . Cells [ 2 ] . Value . ToString ( ) ) ;
2024-12-24 15:11:41 +03:00
}
} ;
2024-12-23 16:30:35 +03:00
foreach ( Entry e in entries )
{
if ( e . length > 1 )
{
// multi-register entry check
if ( ( e . length = = 2 & & e . dataType = = "uint32" ) | | e . dataType = = "string" )
{
DGV_Device . Rows . Add ( rowCount , e . name , "" , e . address ) ;
2024-12-24 15:11:41 +03:00
if ( e . registerType = = RegisterType . Input | | e . registerType = = RegisterType . Discrete )
DGV_Device . Rows [ rowCount ] . Cells [ 2 ] . ReadOnly = true ;
2024-12-23 16:30:35 +03:00
rowCount + + ;
}
else
{
for ( int i = 0 ; i < e . length ; i + + )
{
if ( i < e . labels . Count )
DGV_Device . Rows . Add ( rowCount , e . name + ": " + e . labels [ i ] , "" , e . address + i ) ;
else DGV_Device . Rows . Add ( rowCount , e . address + i , "" , e . address + i ) ;
2024-12-24 15:11:41 +03:00
if ( i ! = 0 ) // Hide rightmost cells for extra registers in the Entry
{
DGV_Device . Rows [ rowCount ] . Cells [ 4 ] = new DataGridViewTextBoxCell ( ) ;
DGV_Device . Rows [ rowCount ] . Cells [ 4 ] . Value = "" ;
DGV_Device . Rows [ rowCount ] . Cells [ 4 ] . Style . ForeColor = Color . DarkGray ;
DGV_Device . Rows [ rowCount ] . Cells [ 4 ] . Style . BackColor = Color . DarkGray ;
DGV_Device . Rows [ rowCount ] . Cells [ 4 ] . ReadOnly = true ;
}
if ( e . registerType = = RegisterType . Input | | e . registerType = = RegisterType . Discrete )
DGV_Device . Rows [ rowCount ] . Cells [ 2 ] . ReadOnly = true ;
2024-12-23 16:30:35 +03:00
rowCount + + ;
}
}
}
else
{
DGV_Device . Rows . Add ( rowCount , e . name , "" , e . address ) ;
2024-12-24 15:11:41 +03:00
if ( e . registerType = = RegisterType . Input | | e . registerType = = RegisterType . Discrete )
DGV_Device . Rows [ rowCount ] . Cells [ 2 ] . ReadOnly = true ;
if ( e . registerType = = RegisterType . Coil )
{
DGV_Device . Rows [ rowCount ] . Cells [ 2 ] = new DataGridViewComboBoxCell ( ) ;
var cbc = DGV_Device . Rows [ rowCount ] . Cells [ 2 ] as DataGridViewComboBoxCell ;
if ( e . valueParse ! = null )
{
if ( e . valueParse . ContainsKey ( "false" ) )
cbc . Items . Add ( e . valueParse [ "false" ] ) ;
if ( e . valueParse . ContainsKey ( "true" ) )
cbc . Items . Add ( e . valueParse [ "true" ] ) ;
}
else
{
cbc . Items . Add ( "False" ) ;
cbc . Items . Add ( "True" ) ;
}
}
2024-12-23 16:30:35 +03:00
rowCount + + ;
}
}
foreach ( DataGridViewRow row in DGV_Device . Rows )
{
row . DefaultCellStyle . Alignment = DataGridViewContentAlignment . MiddleCenter ;
2024-12-24 15:11:41 +03:00
if ( row . Cells [ 2 ] is DataGridViewComboBoxCell )
row . Cells [ 2 ] . Style . Alignment = DataGridViewContentAlignment . MiddleLeft ;
2024-12-23 16:30:35 +03:00
}
2024-12-24 15:11:41 +03:00
2024-12-23 16:30:35 +03:00
foreach ( DataGridViewColumn column in DGV_Device . Columns )
2024-12-16 15:18:05 +03:00
column . SortMode = DataGridViewColumnSortMode . NotSortable ; // disabling sorting for now
2024-12-09 16:24:02 +03:00
FormClosing + = ( s , e ) = > { closed = true ; } ;
Task . Run ( ( ) = > AutoPollAsync ( ) ) ;
2024-12-06 16:32:10 +03:00
}
2024-12-05 17:52:25 +03:00
2024-12-09 16:24:02 +03:00
public async void AutoPollAsync ( )
2024-12-06 16:32:10 +03:00
{
2024-12-09 16:24:02 +03:00
if ( ! port . IsOpen )
port . Open ( ) ;
port . ReadTimeout = timeout ;
try
2024-12-06 16:32:10 +03:00
{
2024-12-09 16:24:02 +03:00
while ( ! closed )
2024-12-06 16:32:10 +03:00
{
2024-12-16 11:35:20 +03:00
if ( isPolling )
2024-12-06 16:32:10 +03:00
{
2024-12-23 16:30:35 +03:00
// holy fuck DGV is awful
DataGridViewCheckBoxCell chbox = DGV_Device . Rows [ activeDGVIndex ] . Cells [ 4 ] as DataGridViewCheckBoxCell ;
if ( Convert . ToBoolean ( chbox . Value ) )
{
2024-12-24 15:11:41 +03:00
//Console.WriteLine("Polling for " + device.entries[activeEntryIndex].name);
await PollForEntry ( entries [ activeEntryIndex ] ) ;
Thread . Sleep ( 150 ) ;
2024-12-23 16:30:35 +03:00
}
else //need to skip multiple dgv entries without accidentaly skipping entries
2024-12-16 11:35:20 +03:00
{
2024-12-23 16:30:35 +03:00
if ( device . entries [ activeEntryIndex ] . labels is null | | device . entries [ activeEntryIndex ] . labels . Count = = 0 )
activeDGVIndex + + ;
else
2024-12-16 15:52:01 +03:00
{
2024-12-23 16:30:35 +03:00
for ( int i = 0 ; i < device . entries [ activeEntryIndex ] . labels . Count ; i + + )
{
activeDGVIndex + + ;
}
2024-12-16 15:52:01 +03:00
}
2024-12-16 11:35:20 +03:00
}
2024-12-23 16:30:35 +03:00
activeEntryIndex + + ;
if ( activeEntryIndex > = device . entries . Count )
activeEntryIndex = 0 ;
if ( activeDGVIndex > = DGV_Device . RowCount )
activeDGVIndex = 0 ;
2024-12-06 16:32:10 +03:00
}
}
}
2024-12-09 16:24:02 +03:00
catch ( Exception err )
{
2024-12-23 16:30:35 +03:00
MessageBox . Show ( err . Message , "AutoPollAsync" ) ;
2024-12-09 16:24:02 +03:00
}
2024-12-23 16:30:35 +03:00
2024-12-06 16:32:10 +03:00
}
2024-12-09 16:24:02 +03:00
public async Task PollForEntry ( Entry entry )
2024-12-06 16:32:10 +03:00
{
2024-12-23 16:30:35 +03:00
byte [ ] message = new byte [ 8 ] ;
2024-12-24 15:11:41 +03:00
var send = Modbus . ReadRegAsync ( port , slaveID , ( FunctionCode ) entry . registerType , entry . address , entry . length ) ;
2024-12-09 16:24:02 +03:00
isAwaitingResponse = true ;
2024-12-12 15:14:13 +03:00
2024-12-24 15:11:41 +03:00
Task delay = Task . WhenAny ( Task . Delay ( timeout ) , Task . Run ( ( ) = > { while ( isAwaitingResponse ) { } return true ; } ) ) . ContinueWith ( ( t ) = >
2024-12-06 16:32:10 +03:00
{
2024-12-09 16:24:02 +03:00
if ( isAwaitingResponse )
2024-12-06 16:32:10 +03:00
{
2024-12-09 16:24:02 +03:00
Console . WriteLine ( "Response timed out." ) ;
isAwaitingResponse = false ;
}
2024-12-24 15:11:41 +03:00
return false ;
2024-12-09 16:24:02 +03:00
} ) ;
await delay ;
2024-12-24 15:11:41 +03:00
2024-12-09 16:24:02 +03:00
}
2024-12-06 16:32:10 +03:00
2024-12-12 15:14:13 +03:00
void PublishResponse ( object sender , ModbusResponseEventArgs e )
2024-12-09 16:24:02 +03:00
{
if ( isAwaitingResponse )
{
2024-12-12 15:14:13 +03:00
try
2024-12-16 11:35:20 +03:00
{
2024-12-23 16:30:35 +03:00
if ( entries [ activeEntryIndex ] . readOnce )
2024-12-24 09:42:45 +03:00
{
2024-12-23 16:30:35 +03:00
DataGridViewCheckBoxCell chbox = DGV_Device . Rows [ activeDGVIndex ] . Cells [ 4 ] as DataGridViewCheckBoxCell ;
chbox . Value = false ;
}
int dbc = e . Message [ 2 ] ; // data byte count
2024-12-12 15:14:13 +03:00
switch ( entries [ activeEntryIndex ] . dataType )
2024-12-09 16:24:02 +03:00
{
2024-12-12 15:14:13 +03:00
case ( "bool" ) :
2024-12-23 16:30:35 +03:00
if ( entries [ activeEntryIndex ] . labels is null | | entries [ activeEntryIndex ] . labels . Count = = 0 ) // assume that no labels = 1 entry
{
2024-12-24 16:13:31 +03:00
//no valueParse keys
if ( entries [ activeEntryIndex ] . valueParse is null | | entries [ activeEntryIndex ] . valueParse . Keys . Count = = 0 )
2024-12-24 15:11:41 +03:00
{
2024-12-24 16:13:31 +03:00
// coil combobox
2024-12-24 15:11:41 +03:00
if ( entries [ activeEntryIndex ] . registerType = = RegisterType . Coil )
{
DGV_Device . Invoke ( ( MethodInvoker ) delegate
{
var cbc = DGV_Device . Rows [ activeDGVIndex ] . Cells [ 2 ] as DataGridViewComboBoxCell ;
cbc . Value = e . Data [ 0 ] > 0x00 ? cbc . Items [ 1 ] : cbc . Items [ 0 ] ;
} ) ;
}
2024-12-24 16:13:31 +03:00
// discrete inputs
2024-12-24 15:11:41 +03:00
else
{
DGV_Device . Invoke ( ( MethodInvoker ) delegate
{
DGV_Device . Rows [ activeDGVIndex ] . Cells [ 2 ] . Value = e . Data [ 0 ] > 0x00 ? "true" : "false" ;
} ) ;
}
}
2024-12-23 16:30:35 +03:00
else
{
2024-12-24 15:11:41 +03:00
try { DGV_Device . Rows [ activeDGVIndex ] . Cells [ 2 ] . Value = e . Data [ 0 ] > 0x00 ? entries [ activeEntryIndex ] . valueParse [ "true" ] : entries [ activeEntryIndex ] . valueParse [ "false" ] ; }
2024-12-24 09:42:45 +03:00
catch ( Exception err )
2024-12-23 16:30:35 +03:00
{
2024-12-24 15:11:41 +03:00
if ( entries [ activeEntryIndex ] . registerType = = RegisterType . Coil )
{
DGV_Device . Invoke ( ( MethodInvoker ) delegate
{
var cbc = DGV_Device . Rows [ activeDGVIndex ] . Cells [ 2 ] as DataGridViewComboBoxCell ;
cbc . Value = e . Data [ 0 ] > 0x00 ? cbc . Items [ 1 ] : cbc . Items [ 0 ] ;
} ) ;
}
else
{
DGV_Device . Invoke ( ( MethodInvoker ) delegate
{
2024-12-24 16:13:31 +03:00
DGV_Device . Rows [ activeDGVIndex ] . Cells [ 2 ] . Value = e . Data [ 0 ] > 0x00 ? "True" : "False" ;
2024-12-24 15:11:41 +03:00
} ) ;
}
2024-12-23 16:30:35 +03:00
}
}
activeDGVIndex + + ;
}
else
{
List < bool > values = new List < bool > ( ) ;
for ( int i = 0 ; i < dbc ; i + + )
{
for ( int j = 0 ; j < 8 ; j + + )
{
bool res = ( ( ( e . Data [ i ] > > j ) & 0x01 ) > = 1 ) ? true : false ;
values . Add ( res ) ;
}
}
for ( int i = 0 ; i < entries [ activeEntryIndex ] . labels . Count ; i + + )
{
2024-12-24 15:11:41 +03:00
if ( entries [ activeEntryIndex ] . registerType = = RegisterType . Coil )
{
DGV_Device . Invoke ( ( MethodInvoker ) delegate
{
2024-12-24 16:13:31 +03:00
var cbc = DGV_Device . Rows [ activeDGVIndex ] . Cells [ 2 ] as DataGridViewComboBoxCell ;
2024-12-24 15:11:41 +03:00
cbc . Value = e . Data [ 0 ] > 0x00 ? cbc . Items [ 1 ] : cbc . Items [ 0 ] ;
} ) ;
}
else
{
DGV_Device . Invoke ( ( MethodInvoker ) delegate
{
2024-12-24 16:13:31 +03:00
DGV_Device . Rows [ activeDGVIndex ] . Cells [ 2 ] . Value = e . Data [ 0 ] > 0x00 ? "true" : "false" ;
2024-12-24 15:11:41 +03:00
} ) ;
}
2024-12-23 16:30:35 +03:00
activeDGVIndex + + ;
}
}
2024-12-12 15:14:13 +03:00
break ;
case ( "uint16" ) :
2024-12-23 16:30:35 +03:00
ushort value = BitConverter . ToUInt16 ( e . Data , 0 ) ;
if ( entries [ activeEntryIndex ] . labels is null | | entries [ activeEntryIndex ] . labels . Count = = 0 ) // single value
{
if ( entries [ activeEntryIndex ] . valueParse is null | | entries [ activeEntryIndex ] . valueParse . Keys . Count = = 0 )
{
//Array.Reverse(e.Data); // this was necessary, but something changed, idk
2024-12-24 15:11:41 +03:00
//Console.WriteLine("ushort parsed value: " + value);
2024-12-23 16:30:35 +03:00
DGV_Device . Rows [ activeDGVIndex ] . Cells [ 2 ] . Value = value ;
}
else
{
try
{
DGV_Device . Rows [ activeDGVIndex ] . Cells [ 2 ] . Value = entries [ activeEntryIndex ] . valueParse [ value . ToString ( ) ] ;
}
2024-12-24 09:42:45 +03:00
catch ( Exception err )
{
DGV_Device . Rows [ activeDGVIndex ] . Cells [ 2 ] . Value = value ; MessageBox . Show ( "Error parsing uint value at address: " + entries [ activeEntryIndex ] . address + "; " + err . Message , "uint16 parse" ) ;
}
2024-12-23 16:30:35 +03:00
}
activeDGVIndex + + ;
}
else // value group
{
2024-12-24 09:42:45 +03:00
try
2024-12-23 16:30:35 +03:00
{
2024-12-24 09:42:45 +03:00
List < ushort > values = new List < ushort > ( ) ;
2024-12-24 15:11:41 +03:00
for ( int i = 0 ; i < dbc ; i + = 2 )
2024-12-24 09:42:45 +03:00
{
ushort s = BitConverter . ToUInt16 ( e . Data , i ) ;
values . Add ( s ) ;
}
2024-12-24 15:11:41 +03:00
//Console.WriteLine("ushort values count: " + values.Count);
//Console.WriteLine("entity labels count: " + entries[activeEntryIndex].labels.Count);
2024-12-24 09:42:45 +03:00
for ( int i = 0 ; i < entries [ activeEntryIndex ] . labels . Count ; i + + )
{
2024-12-25 13:59:04 +03:00
if ( device . entries [ activeEntryIndex ] . valueParse ! = null )
{
DGV_Device . Invoke ( ( MethodInvoker ) delegate
{
DGV_Device . Rows [ activeDGVIndex ] . Cells [ 2 ] . Value = device . entries [ activeEntryIndex ] . valueParse [ values [ i ] . ToString ( ) ] ;
} ) ;
}
else
2024-12-24 15:11:41 +03:00
{
DGV_Device . Rows [ activeDGVIndex ] . Cells [ 2 ] . Value = values [ i ] ;
2024-12-25 13:59:04 +03:00
}
2024-12-24 09:42:45 +03:00
activeDGVIndex + + ;
}
2024-12-23 16:30:35 +03:00
}
2024-12-24 09:42:45 +03:00
catch ( Exception err )
2024-12-23 16:30:35 +03:00
{
2024-12-24 09:42:45 +03:00
DGV_Device . Rows [ activeDGVIndex ] . Cells [ 2 ] . Value = value ; MessageBox . Show ( "Error parsing uint value at address: " + entries [ activeEntryIndex ] . address + "; " + err . Message , "uint16 group req parse" ) ;
2024-12-23 16:30:35 +03:00
}
}
2024-12-24 09:42:45 +03:00
2024-12-12 15:14:13 +03:00
break ;
case ( "uint32" ) :
2024-12-16 11:35:20 +03:00
Array . Reverse ( e . Data ) ;
2024-12-23 16:30:35 +03:00
DGV_Device . Rows [ activeDGVIndex ] . Cells [ 2 ] . Value = BitConverter . ToUInt32 ( e . Data , 0 ) ;
activeDGVIndex + + ;
2024-12-12 15:14:13 +03:00
break ;
2024-12-16 14:43:50 +03:00
case ( "string" ) :
2024-12-16 14:31:14 +03:00
List < byte > bytes = new List < byte > ( ) ;
for ( int i = 0 ; i < e . Data . Length ; i + + )
{
if ( e . Data [ i ] ! = 0 )
bytes . Add ( e . Data [ i ] ) ;
}
2024-12-18 16:49:58 +03:00
bytes . Reverse ( ) ;
2024-12-23 16:30:35 +03:00
DGV_Device . Rows [ activeDGVIndex ] . Cells [ 2 ] . Value = System . Text . Encoding . UTF8 . GetString ( bytes . ToArray ( ) ) ;
activeDGVIndex + + ;
2024-12-12 15:14:13 +03:00
break ;
default :
MessageBox . Show ( "Wrong data type set for entry " + entries [ activeEntryIndex ] . name ) ;
2024-12-23 16:30:35 +03:00
activeDGVIndex + + ;
2024-12-12 15:14:13 +03:00
break ;
2024-12-06 16:32:10 +03:00
}
2024-12-23 16:30:35 +03:00
if ( activeDGVIndex > = DGV_Device . RowCount )
activeDGVIndex = 0 ;
2024-12-12 15:14:13 +03:00
//MessageBox.Show("Получен ответ от устройства: " + dataCleaned, "Успех", MessageBoxButtons.OK);
port . DiscardInBuffer ( ) ;
2024-12-06 16:32:10 +03:00
}
2024-12-24 15:11:41 +03:00
catch ( Exception err ) { MessageBox . Show ( err . Message , "Publish response error" ) ; }
2024-12-12 15:14:13 +03:00
2024-12-05 17:52:25 +03:00
}
2024-12-23 16:30:35 +03:00
if ( activeDGVIndex > = DGV_Device . Rows . Count )
activeDGVIndex = 0 ;
isAwaitingResponse = false ;
2024-12-05 15:57:07 +03:00
}
2024-12-16 11:35:20 +03:00
private void Button_StartStop_Click ( object sender , EventArgs e )
{
isPolling = ! isPolling ;
Button_StartStop . Text = ( isPolling ? "Стоп" : "Старт" ) ;
}
2024-12-05 15:57:07 +03:00
}
2024-12-09 16:24:02 +03:00
2024-12-06 16:32:10 +03:00
}