2024-11-05 10:31:38 +03:00
using System ;
using System.Collections.Generic ;
using System.Threading.Tasks ;
using System.Windows.Forms ;
using System.IO.Ports ;
namespace Gidrolock_Modbus_Scanner
{
public partial class App : Form
{
2024-12-06 16:32:10 +03:00
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 ;
2025-01-24 11:46:36 +03:00
ModbusResponseEventArgs latestMessage ;
2024-12-05 15:57:07 +03:00
2025-01-14 11:31:23 +03:00
public Dictionary < string , string > models = new Dictionary < string , string > ( ) ;
2025-01-21 14:38:59 +03:00
2025-01-24 11:46:36 +03:00
DateTime dateTime ;
2025-01-21 14:38:59 +03:00
Datasheet datasheet ;
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 ;
2025-01-27 11:23:57 +03:00
this . upDownModbusID . Value = 0 ;
2024-11-05 16:57:05 +03:00
TextBox_Log . Text = "Приложение готово к работе." ;
2025-01-24 17:54:04 +03:00
cBoxSpeed . Items . Add ( "1200" ) ;
cBoxSpeed . Items . Add ( "2400" ) ;
cBoxSpeed . Items . Add ( "4800" ) ;
cBoxSpeed . Items . Add ( "9600" ) ;
cBoxSpeed . Items . Add ( "14400" ) ;
cBoxSpeed . Items . Add ( "19200" ) ;
cBoxSpeed . Items . Add ( "38400" ) ;
cBoxSpeed . Items . Add ( "57600" ) ;
cBoxSpeed . Items . Add ( "115200" ) ;
cBoxSpeed . SelectedIndex = 3 ;
2025-01-27 11:23:57 +03:00
upDownModbusID . Value = 30 ;
upDownModbusID . Minimum = 1 ;
upDownModbusID . Maximum = 247 ;
2025-01-14 11:31:23 +03:00
2025-02-14 10:22:15 +03:00
models . Add ( "Standard Wi-Fi" , "STW485" ) ;
models . Add ( "Standard Radio" , "STR485" ) ;
2025-01-24 11:46:36 +03:00
models . Add ( "Premium Plus" , "PRPLS1" ) ;
2025-01-14 11:31:23 +03:00
models . Add ( "Inteli" , "INTELI" ) ;
models . Add ( "Premium" , "BUP485" ) ;
2024-12-23 16:30:35 +03:00
/* - Version Check - */
System . Reflection . Assembly assembly = System . Reflection . Assembly . GetExecutingAssembly ( ) ;
System . Diagnostics . FileVersionInfo fvi = System . Diagnostics . FileVersionInfo . GetVersionInfo ( assembly . Location ) ;
string version = fvi . FileVersion ;
Console . WriteLine ( "Version: " + version ) ;
2025-01-21 14:38:59 +03:00
Modbus . ResponseReceived + = ( sndr , msg ) = >
{
2025-01-24 11:46:36 +03:00
latestMessage = msg ;
isAwaitingResponse = false ;
2025-01-21 14:38:59 +03:00
} ;
2024-12-12 15:14:13 +03:00
}
2024-12-23 16:30:35 +03:00
2024-11-05 10:31:38 +03:00
void App_FormClosed ( object sender , FormClosedEventArgs e )
{
port . Close ( ) ;
}
void Form1_Load ( object sender , EventArgs e )
{
2025-01-24 17:54:04 +03:00
cBoxPorts . Items . AddRange ( SerialPort . GetPortNames ( ) ) ;
if ( cBoxPorts . Items . Count > 0 )
cBoxPorts . SelectedIndex = 0 ;
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-11-05 16:57:05 +03:00
private async void ButtonConnect_Click ( object sender , EventArgs e )
{
2025-01-24 17:54:04 +03:00
if ( cBoxPorts . 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 ;
}
2025-01-27 11:23:57 +03:00
if ( upDownModbusID . Value = = 0 )
2024-12-19 10:41:52 +03:00
{
2025-01-14 11:31:23 +03:00
DialogResult res = MessageBox . Show ( "Указан Modbus ID 0 — глобальное вещание. Устройства не смогут отвечать на сообщения. Продолжить?" , "Внимание" , MessageBoxButtons . OKCancel ) ;
2024-12-19 10:41:52 +03:00
if ( res = = DialogResult . Cancel )
return ;
}
2025-01-14 11:31:23 +03:00
try
2024-12-12 15:14:13 +03:00
{
2025-01-14 11:31:23 +03:00
/* - Port Setup - */
if ( port . IsOpen )
port . Close ( ) ;
2024-12-06 16:32:10 +03:00
2025-01-14 11:31:23 +03:00
port . Handshake = Handshake . None ;
2025-01-24 17:54:04 +03:00
port . PortName = cBoxPorts . Text ;
port . BaudRate = Int32 . Parse ( cBoxSpeed . Items [ cBoxSpeed . SelectedIndex ] . ToString ( ) ) ;
2025-01-14 11:31:23 +03:00
port . Parity = Parity . None ;
port . DataBits = 8 ;
port . StopBits = StopBits . One ;
2024-12-16 14:31:14 +03:00
2025-01-14 11:31:23 +03:00
port . ReadTimeout = 3000 ;
port . WriteTimeout = 3000 ;
port . ReadBufferSize = 8192 ;
2024-12-12 15:14:13 +03:00
2025-01-14 11:31:23 +03:00
port . Open ( ) ;
// send read request for 6 registers starting from 200
// if we've got error or a timeout, show appropriate errors
// else parse response to unicode and go through every .json
// if matching model is found, instantiate device window
2024-12-12 15:14:13 +03:00
2025-01-24 11:46:36 +03:00
await Task . Run ( async ( ) = >
2025-01-14 11:31:23 +03:00
{
2025-01-24 11:46:36 +03:00
// send message
latestMessage = null ;
isAwaitingResponse = true ;
2025-01-27 11:23:57 +03:00
var send = Modbus . ReadRegAsync ( ( byte ) upDownModbusID . Value , FunctionCode . ReadInput , 200 , 6 ) ;
2025-01-24 11:46:36 +03:00
await Task . Delay ( 2000 ) . ContinueWith ( _ = >
{
if ( isAwaitingResponse )
{
isAwaitingResponse = false ;
MessageBox . Show ( "Истекло время ожидания ответа от устройства." ) ;
}
return ;
} ) ;
while ( isAwaitingResponse ) { continue ; }
if ( latestMessage . Status = = ModbusStatus . Error )
return ;
2025-01-14 11:31:23 +03:00
// confirm the model
2025-01-24 11:46:36 +03:00
string response = ByteArrayToUnicode ( latestMessage . Data ) ;
2025-01-27 11:23:57 +03:00
int index = 0 ;
string match = "" ;
string model = "" ;
foreach ( string key in models . Keys )
2024-12-16 14:31:14 +03:00
{
2025-01-27 11:23:57 +03:00
if ( models [ key ] = = response )
2024-12-16 14:31:14 +03:00
{
2025-01-27 11:23:57 +03:00
match = models [ key ] ;
model = key ;
break ;
2024-12-16 14:31:14 +03:00
}
2025-01-27 11:23:57 +03:00
index + + ;
}
2024-12-18 16:49:58 +03:00
2025-01-27 11:23:57 +03:00
if ( match = = "" ) // if no matches found
MessageBox . Show ( "Подключенное устройство не соответствует ни одной из поддерживаемых моделей." ) ;
2025-01-21 14:38:59 +03:00
2025-01-27 11:23:57 +03:00
else // match found
2024-12-16 14:33:13 +03:00
{
2025-01-27 11:23:57 +03:00
// instantiate panel
AddLog ( "Открываю панель конфигурации." ) ;
Device device = GetDevice ( ( DeviceType ) index ) ;
StartDatasheet ( device ) ;
2024-12-16 14:33:13 +03:00
}
2025-01-24 11:46:36 +03:00
} ) ;
2025-01-14 11:31:23 +03:00
}
catch ( Exception ex )
{
MessageBox . Show ( ex . Message ) ;
2024-12-12 15:14:13 +03:00
}
2024-12-05 11:10:53 +03:00
}
2025-01-21 14:38:59 +03:00
void StartDatasheet ( Device device )
{
2025-01-27 11:23:57 +03:00
datasheet = new Datasheet ( ( byte ) upDownModbusID . Value , device ) ;
2025-01-24 11:46:36 +03:00
Application . Run ( datasheet ) ;
2025-01-21 14:38:59 +03:00
}
2024-12-05 11:10:53 +03:00
2024-12-12 15:14:13 +03:00
2024-11-05 10:31:38 +03:00
void CBox_Ports_Click ( object sender , EventArgs e )
{
2025-01-24 17:54:04 +03:00
cBoxPorts . Items . Clear ( ) ;
cBoxPorts . Items . AddRange ( SerialPort . GetPortNames ( ) ) ;
2024-11-05 10:31:38 +03:00
}
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 ) ) ;
2025-01-14 11:31:23 +03:00
switch ( e . Status )
2024-12-18 11:28:20 +03:00
{
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 ) ) ;
2025-01-14 11:31:23 +03:00
}
2024-12-18 11:28:20 +03:00
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-23 16:30:35 +03:00
TextBox_Log . Invoke ( ( MethodInvoker ) delegate { TextBox_Log . AppendText ( Environment . NewLine + "[" + dateTime . Hour . ToString ( ) . PadLeft ( 2 , '0' ) + ":" + dateTime . Minute . ToString ( ) . PadLeft ( 2 , '0' ) + ":" + dateTime . Second . ToString ( ) . PadLeft ( 2 , '0' ) + "] " + message ) ; } ) ;
2024-11-05 16:57:05 +03:00
}
2025-01-14 11:31:23 +03:00
public static string ByteArrayToUnicode ( byte [ ] input )
2024-12-18 11:28:20 +03:00
{
// stupid fucking WinForm textbox breaks from null symbols
// stupid fucking Encoding class does byte-by-byte conversion
2025-01-14 11:31:23 +03:00
List < char > result = new List < char > ( input . Length / 2 ) ;
2024-12-18 11:28:20 +03:00
byte [ ] flip = input ;
2025-01-24 11:46:36 +03:00
//Array.Reverse(flip); // stupid fucking BitConverter is little-endian and spits out chinese nonsense otherwise
for ( int i = 0 ; i < flip . Length ; i + + )
2024-12-18 11:28:20 +03:00
{
2025-01-24 11:46:36 +03:00
if ( flip [ i ] = = 0x00 )
continue ;
else result . Add ( Convert . ToChar ( flip [ i ] ) ) ;
2024-12-18 11:28:20 +03:00
}
2025-01-24 11:46:36 +03:00
//result.Reverse();
2024-12-18 11:28:20 +03:00
return new string ( result . ToArray ( ) ) ;
}
2025-01-14 11:31:23 +03:00
2025-01-21 14:38:59 +03:00
public Device GetDevice ( DeviceType dt )
{
Device d = new Device ( ) ;
switch ( dt )
{
2025-02-14 10:22:15 +03:00
case DeviceType . StandardWifi :
2025-01-24 11:46:36 +03:00
d . name = "Standard Wi-Fi RS485" ;
2025-01-21 14:38:59 +03:00
d . id = 30 ;
d . modelName = "STW485" ;
2025-01-28 09:35:16 +03:00
d . firmware = new Entry ( RegisterType . Input , 250 , 6 ) ;
2025-01-27 11:23:57 +03:00
d . baudRate = new Entry ( RegisterType . Holding , 110 ) ;
2025-01-21 14:38:59 +03:00
d . valveStatus = new Entry ( RegisterType . Coil , 1202 ) ;
d . alarmStatus = new Entry ( RegisterType . Coil , 1201 ) ;
d . hasCleaningMode = true ;
d . cleaningMode = new Entry ( RegisterType . Coil , 3 ) ;
d . hasBattery = false ;
d . wiredSensors = 2 ;
d . hasScenarioSensor = true ;
2025-01-24 17:54:04 +03:00
d . sensorAlarm = new Entry ( RegisterType . Discrete , 1343 , 24 ) ;
2025-01-21 14:38:59 +03:00
d . radioStatus = new Entry ( RegisterType . Input , 1215 , 21 ) ;
2025-02-14 10:22:15 +03:00
break ;
case DeviceType . StandardRadio :
d . name = "Standard Radio RS485" ;
d . id = 30 ;
d . modelName = "STR485" ;
d . firmware = new Entry ( RegisterType . Input , 250 , 6 ) ;
d . baudRate = new Entry ( RegisterType . Holding , 110 ) ;
d . valveStatus = new Entry ( RegisterType . Coil , 1202 ) ;
d . alarmStatus = new Entry ( RegisterType . Coil , 1201 ) ;
d . hasCleaningMode = true ;
d . cleaningMode = new Entry ( RegisterType . Coil , 3 ) ;
d . hasBattery = false ;
d . wiredSensors = 2 ;
d . hasScenarioSensor = false ;
d . sensorAlarm = new Entry ( RegisterType . Discrete , 1343 , 23 ) ;
d . radioStatus = new Entry ( RegisterType . Input , 1215 , 21 ) ;
2025-01-21 14:38:59 +03:00
break ;
case DeviceType . Inteli :
d . modelName = "Inteli" ;
2025-01-28 09:35:16 +03:00
d . id = 26 ;
d . modelName = "INTELI" ;
d . baudRate = new Entry ( RegisterType . Holding , 129 ) ;
2025-01-21 14:38:59 +03:00
break ;
case DeviceType . PremiumPlus :
2025-01-24 11:46:36 +03:00
d . name = "Premium Plus Wi-Fi" ;
d . id = 30 ;
d . modelName = "PRPLS1" ;
2025-01-28 09:35:16 +03:00
d . firmware = new Entry ( RegisterType . Input , 250 , 6 ) ;
2025-01-27 11:23:57 +03:00
d . baudRate = new Entry ( RegisterType . Holding , 110 ) ;
2025-01-24 11:46:36 +03:00
d . valveStatus = new Entry ( RegisterType . Coil , 1202 ) ;
d . alarmStatus = new Entry ( RegisterType . Coil , 1201 ) ;
d . hasCleaningMode = true ;
d . cleaningMode = new Entry ( RegisterType . Coil , 3 ) ;
d . hasBattery = true ;
d . batteryCharge = new Entry ( RegisterType . Input , 1207 ) ;
d . wiredSensors = 7 ;
d . hasScenarioSensor = true ;
2025-01-24 17:54:04 +03:00
d . sensorAlarm = new Entry ( RegisterType . Discrete , 1343 , 29 ) ;
d . radioStatus = new Entry ( RegisterType . Input , 1215 , 21 ) ;
2025-01-21 14:38:59 +03:00
break ;
case DeviceType . Premium :
d . modelName = "Premium" ;
2025-01-27 11:23:57 +03:00
d . id = 26 ;
d . modelName = "BUP485" ;
d . baudRate = new Entry ( RegisterType . Holding , 110 ) ;
2025-01-21 14:38:59 +03:00
break ;
default :
break ;
}
return d ;
}
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 } ;
2025-01-21 14:38:59 +03:00
//public enum SelectedPath { File, Folder };
2025-01-24 17:54:04 +03:00
2025-02-14 10:22:15 +03:00
public enum DeviceType { StandardWifi , StandardRadio , PremiumPlus , Inteli , Premium } ;