Add project files.
This commit is contained in:
22
App.config
Normal file
22
App.config
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<configuration>
|
||||||
|
<startup>
|
||||||
|
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.8" />
|
||||||
|
</startup>
|
||||||
|
<runtime>
|
||||||
|
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
|
||||||
|
<dependentAssembly>
|
||||||
|
<assemblyIdentity name="System.Runtime.CompilerServices.Unsafe" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
|
||||||
|
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
|
||||||
|
</dependentAssembly>
|
||||||
|
<dependentAssembly>
|
||||||
|
<assemblyIdentity name="System.Memory" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
|
||||||
|
<bindingRedirect oldVersion="0.0.0.0-4.0.1.2" newVersion="4.0.1.2" />
|
||||||
|
</dependentAssembly>
|
||||||
|
<dependentAssembly>
|
||||||
|
<assemblyIdentity name="System.ValueTuple" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
|
||||||
|
<bindingRedirect oldVersion="0.0.0.0-4.0.3.0" newVersion="4.0.3.0" />
|
||||||
|
</dependentAssembly>
|
||||||
|
</assemblyBinding>
|
||||||
|
</runtime>
|
||||||
|
</configuration>
|
||||||
9
App.xaml
Normal file
9
App.xaml
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<Application x:Class="FAtoPA.Net.App"
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:local="clr-namespace:FAtoPA.Net"
|
||||||
|
StartupUri="MainWindow.xaml">
|
||||||
|
<Application.Resources>
|
||||||
|
|
||||||
|
</Application.Resources>
|
||||||
|
</Application>
|
||||||
17
App.xaml.cs
Normal file
17
App.xaml.cs
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Configuration;
|
||||||
|
using System.Data;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Windows;
|
||||||
|
|
||||||
|
namespace FAtoPA.Net
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Interaction logic for App.xaml
|
||||||
|
/// </summary>
|
||||||
|
public partial class App : Application
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
112
Config.cs
Normal file
112
Config.cs
Normal file
@@ -0,0 +1,112 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Text.Json;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace FAtoPA
|
||||||
|
{
|
||||||
|
class Config
|
||||||
|
{
|
||||||
|
// FSM settings
|
||||||
|
public byte FSM_NetGroup { get; set; } = 2;
|
||||||
|
public byte FSM_NetNode { get; set; } = 1;
|
||||||
|
public byte FSM_PNA { get; set; } = 2;
|
||||||
|
public String FSM_LocalIP { get; set; } = "192.168.10.23";
|
||||||
|
public bool FSM_UseMulticast { get; set; } = false;
|
||||||
|
public String FSM_MulticastIP { get; set; } = "239.192.0.1";
|
||||||
|
public UInt16 FSM_MulticastPort { get; set; } = 25000;
|
||||||
|
|
||||||
|
public UInt16 Modbus_ListenPort { get; set; } = 502;
|
||||||
|
public byte Modbus_DeviceID { get; set; } = 1;
|
||||||
|
public UInt16 Modbus_MaxRegister { get; set; } = 2000;
|
||||||
|
|
||||||
|
public String VX_TargetIP { get; set; } = "192.168.14.1";
|
||||||
|
public UInt16 VX_TargetPort { get; set; } = 5000;
|
||||||
|
private String _configpath = "";
|
||||||
|
|
||||||
|
public Config() {
|
||||||
|
_configpath = GetConfigPath();
|
||||||
|
// kalau belum ada, create default
|
||||||
|
if (!File.Exists(_configpath)) Save();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Boolean FileIsExist() { return File.Exists(_configpath); }
|
||||||
|
|
||||||
|
public bool Save()
|
||||||
|
{
|
||||||
|
|
||||||
|
Debug.WriteLine("Will Save Config to [" + _configpath + "]");
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
String jsonstring = JsonSerializer.Serialize(this);
|
||||||
|
File.WriteAllText(_configpath, jsonstring);
|
||||||
|
Debug.WriteLine("Config saved to [" + _configpath + "]");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Debug.WriteLine("Config Save to ["+ _configpath + "] Error: " + ex.Message);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Load()
|
||||||
|
{
|
||||||
|
|
||||||
|
if (File.Exists(_configpath))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
string jsonstring = File.ReadAllText(_configpath);
|
||||||
|
Config _loaded = JsonSerializer.Deserialize<Config>(jsonstring);
|
||||||
|
if (_loaded != null)
|
||||||
|
{
|
||||||
|
this.FSM_NetGroup = _loaded.FSM_NetGroup;
|
||||||
|
this.FSM_NetNode = _loaded.FSM_NetNode;
|
||||||
|
this.FSM_PNA = _loaded.FSM_PNA;
|
||||||
|
this.FSM_LocalIP = _loaded.FSM_LocalIP;
|
||||||
|
this.FSM_UseMulticast = _loaded.FSM_UseMulticast;
|
||||||
|
this.FSM_MulticastIP = _loaded.FSM_MulticastIP;
|
||||||
|
this.FSM_MulticastPort = _loaded.FSM_MulticastPort;
|
||||||
|
this.Modbus_ListenPort = _loaded.Modbus_ListenPort;
|
||||||
|
this.Modbus_DeviceID = _loaded.Modbus_DeviceID;
|
||||||
|
this.Modbus_MaxRegister = _loaded.Modbus_MaxRegister;
|
||||||
|
this.VX_TargetIP = _loaded.VX_TargetIP;
|
||||||
|
this.VX_TargetPort = _loaded.VX_TargetPort;
|
||||||
|
|
||||||
|
Debug.WriteLine("Config Loaded from [" + _configpath + "]");
|
||||||
|
return true;
|
||||||
|
} else Debug.WriteLine("Config Load Error: File ["+ _configpath + "] is not valid Config.");
|
||||||
|
} catch(Exception ex)
|
||||||
|
{
|
||||||
|
Debug.WriteLine("Config Load from ["+ _configpath + "] Error: " + ex.Message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else Debug.WriteLine("Config Load Error: File ["+ _configpath + "] not found.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String GetConfigPath()
|
||||||
|
{
|
||||||
|
String _xx = System.IO.Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "FAtoPA");
|
||||||
|
Debug.WriteLine("Config Path: " + _xx);
|
||||||
|
if (!Directory.Exists(_xx)) {
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Directory.CreateDirectory(_xx);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Debug.WriteLine("Config Path Error: " + ex.Message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return System.IO.Path.Combine(_xx, "config.json");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
475
Database.cs
Normal file
475
Database.cs
Normal file
@@ -0,0 +1,475 @@
|
|||||||
|
using Microsoft.Data.Sqlite;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace FAtoPA
|
||||||
|
{
|
||||||
|
class Database
|
||||||
|
{
|
||||||
|
String connectionString = "Data Source=database.db";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create Database Object
|
||||||
|
/// </summary>
|
||||||
|
public Database()
|
||||||
|
{
|
||||||
|
|
||||||
|
CreateFSMTable();
|
||||||
|
CreateModbusTable();
|
||||||
|
CreateVXTable();
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool CreateFSMTable()
|
||||||
|
{
|
||||||
|
//Debug.WriteLine("About to execute CreateFSMTable");
|
||||||
|
using (var connection = new SqliteConnection(connectionString))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
connection.Open();
|
||||||
|
var createtablecmd = connection.CreateCommand();
|
||||||
|
createtablecmd.CommandText = "CREATE TABLE IF NOT EXISTS FsmData (SIID TEXT PRIMARY KEY, Enable INTEGER, Description TEXT)";
|
||||||
|
createtablecmd.ExecuteNonQuery();
|
||||||
|
Debug.WriteLine("CreateFSMTable success");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Debug.WriteLine("Error CreateFSMTable, Exception : " + ex.Message);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool ClearFSMTable() {
|
||||||
|
using (var connection = new SqliteConnection(connectionString))
|
||||||
|
{
|
||||||
|
connection.Open();
|
||||||
|
var deleteCmd = connection.CreateCommand();
|
||||||
|
deleteCmd.CommandText = "DELETE FROM FsmData";
|
||||||
|
try
|
||||||
|
{
|
||||||
|
int result = deleteCmd.ExecuteNonQuery();
|
||||||
|
return (result > 0);
|
||||||
|
}
|
||||||
|
catch (SqliteException e)
|
||||||
|
{
|
||||||
|
Debug.WriteLine("Error deleting FSMData: " + e.Message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<FSMData> GetFSMDatas()
|
||||||
|
{
|
||||||
|
List<FSMData> fsmDatas = new List<FSMData>();
|
||||||
|
using (var connection = new SqliteConnection(connectionString))
|
||||||
|
{
|
||||||
|
connection.Open();
|
||||||
|
|
||||||
|
var selectCmd = connection.CreateCommand();
|
||||||
|
selectCmd.CommandText = "SELECT * FROM FsmData";
|
||||||
|
using (var reader = selectCmd.ExecuteReader())
|
||||||
|
{
|
||||||
|
while (reader.Read())
|
||||||
|
{
|
||||||
|
FSMData fsmData = new FSMData(reader.GetString(0), reader.GetBoolean(1), reader.GetString(2));
|
||||||
|
fsmDatas.Add(fsmData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return fsmDatas;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool AddFSMData(params FSMData[] data)
|
||||||
|
{
|
||||||
|
using(var connection = new SqliteConnection(connectionString))
|
||||||
|
{
|
||||||
|
connection.Open();
|
||||||
|
using (var transaction = connection.BeginTransaction())
|
||||||
|
{
|
||||||
|
foreach (var item in data)
|
||||||
|
{
|
||||||
|
var insertCmd = connection.CreateCommand();
|
||||||
|
insertCmd.CommandText = "INSERT INTO FsmData (SIID, Enable, Description) VALUES (@SIID, @Enable, @Description)";
|
||||||
|
insertCmd.Parameters.AddWithValue("@SIID", item.SIID);
|
||||||
|
insertCmd.Parameters.AddWithValue("@Enable", item.Enable);
|
||||||
|
insertCmd.Parameters.AddWithValue("@Description", item.Description);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
int result = insertCmd.ExecuteNonQuery();
|
||||||
|
if (result <= 0)
|
||||||
|
{
|
||||||
|
transaction.Rollback();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (SqliteException e)
|
||||||
|
{
|
||||||
|
Debug.WriteLine("Error inserting FSMData: " + e.Message);
|
||||||
|
transaction.Rollback();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
transaction.Commit();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool RemoveFSMDatabySIID(String SIID)
|
||||||
|
{
|
||||||
|
using (var conn = new SqliteConnection(connectionString))
|
||||||
|
{
|
||||||
|
conn.Open();
|
||||||
|
var deleteCmd = conn.CreateCommand();
|
||||||
|
deleteCmd.CommandText = "DELETE FROM FsmData WHERE SIID = @SIID";
|
||||||
|
deleteCmd.Parameters.AddWithValue("@SIID", SIID);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
int result = deleteCmd.ExecuteNonQuery();
|
||||||
|
return (result > 0);
|
||||||
|
}
|
||||||
|
catch (SqliteException e)
|
||||||
|
{
|
||||||
|
Debug.WriteLine("Error deleting FSMData: " + e.Message);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool CreateModbusTable()
|
||||||
|
{
|
||||||
|
//Debug.WriteLine("About to execute CreateModbusTable");
|
||||||
|
using (var connection = new SqliteConnection(connectionString))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
connection.Open();
|
||||||
|
var modbuscmd = connection.CreateCommand();
|
||||||
|
modbuscmd.CommandText = "CREATE TABLE IF NOT EXISTS ModbusData (SIID TEXT PRIMARY KEY, Register INTEGER, Description TEXT)";
|
||||||
|
modbuscmd.ExecuteNonQuery();
|
||||||
|
Debug.WriteLine("CreateModbusTable success");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Debug.WriteLine("Error CreateModbusTable, Exception : " + ex.Message);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool ClearModbusTable()
|
||||||
|
{
|
||||||
|
using (var connection = new SqliteConnection(connectionString))
|
||||||
|
{
|
||||||
|
connection.Open();
|
||||||
|
var deleteCmd = connection.CreateCommand();
|
||||||
|
deleteCmd.CommandText = "DELETE FROM ModbusData";
|
||||||
|
try
|
||||||
|
{
|
||||||
|
int result = deleteCmd.ExecuteNonQuery();
|
||||||
|
return (result > 0);
|
||||||
|
}
|
||||||
|
catch (SqliteException e)
|
||||||
|
{
|
||||||
|
Debug.WriteLine("Error deleting ModbusData: " + e.Message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Boolean AddModbusData(params ModbusData[] data)
|
||||||
|
{
|
||||||
|
using (var connection = new SqliteConnection(connectionString))
|
||||||
|
{
|
||||||
|
connection.Open();
|
||||||
|
using (var transaction = connection.BeginTransaction())
|
||||||
|
{
|
||||||
|
foreach (var item in data)
|
||||||
|
{
|
||||||
|
var insertCmd = connection.CreateCommand();
|
||||||
|
insertCmd.CommandText = "INSERT INTO ModbusData (SIID, Register, Description) VALUES (@SIID, @Register, @Description)";
|
||||||
|
insertCmd.Parameters.AddWithValue("@SIID", item.SIID);
|
||||||
|
insertCmd.Parameters.AddWithValue("@Register", item.Register);
|
||||||
|
insertCmd.Parameters.AddWithValue("@Description", item.Description);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
int result = insertCmd.ExecuteNonQuery();
|
||||||
|
if (result <= 0)
|
||||||
|
{
|
||||||
|
transaction.Rollback();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (SqliteException e)
|
||||||
|
{
|
||||||
|
Debug.WriteLine("Error inserting ModbusData: " + e.Message);
|
||||||
|
transaction.Rollback();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
transaction.Commit();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<ModbusData> GetModbusDatas()
|
||||||
|
{
|
||||||
|
List<ModbusData> modbusDatas = new List<ModbusData>();
|
||||||
|
using (var connection = new SqliteConnection(connectionString))
|
||||||
|
{
|
||||||
|
connection.Open();
|
||||||
|
|
||||||
|
var selectCmd = connection.CreateCommand();
|
||||||
|
selectCmd.CommandText = "SELECT * FROM ModbusData";
|
||||||
|
using (var reader = selectCmd.ExecuteReader())
|
||||||
|
{
|
||||||
|
while (reader.Read())
|
||||||
|
{
|
||||||
|
ModbusData modbusdata = new ModbusData(reader.GetString(0), reader.GetFieldValue<UInt16>(1), reader.GetString(2));
|
||||||
|
modbusDatas.Add(modbusdata);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return modbusDatas;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool RemoveModbusDatabySIID(String SIID)
|
||||||
|
{
|
||||||
|
using (var conn = new SqliteConnection(connectionString))
|
||||||
|
{
|
||||||
|
conn.Open();
|
||||||
|
var deleteCmd = conn.CreateCommand();
|
||||||
|
deleteCmd.CommandText = "DELETE FROM ModbusData WHERE SIID = @SIID";
|
||||||
|
deleteCmd.Parameters.AddWithValue("@SIID", SIID);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
int result = deleteCmd.ExecuteNonQuery();
|
||||||
|
return (result > 0);
|
||||||
|
}
|
||||||
|
catch (SqliteException e)
|
||||||
|
{
|
||||||
|
Debug.WriteLine("Error deleting ModbusData: " + e.Message);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool CreateVXTable()
|
||||||
|
{
|
||||||
|
//Debug.WriteLine("About to execute CreateVXTable");
|
||||||
|
using (var connection = new SqliteConnection(connectionString))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
connection.Open();
|
||||||
|
var vxlancmd = connection.CreateCommand();
|
||||||
|
vxlancmd.CommandText = "CREATE TABLE IF NOT EXISTS VxTable (SIID TEXT PRIMARY KEY, FrameID INTEGER, CIN INTEGER, Description TEXT)";
|
||||||
|
vxlancmd.ExecuteNonQuery();
|
||||||
|
Debug.WriteLine("CreateVXTable success");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Debug.WriteLine("Error CreateVXTable, Exception : " + ex.Message);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool ClearVXTable()
|
||||||
|
{
|
||||||
|
using (var connection = new SqliteConnection(connectionString))
|
||||||
|
{
|
||||||
|
connection.Open();
|
||||||
|
var deleteCmd = connection.CreateCommand();
|
||||||
|
deleteCmd.CommandText = "DELETE FROM VxTable";
|
||||||
|
try
|
||||||
|
{
|
||||||
|
int result = deleteCmd.ExecuteNonQuery();
|
||||||
|
return (result > 0);
|
||||||
|
}
|
||||||
|
catch (SqliteException e)
|
||||||
|
{
|
||||||
|
Debug.WriteLine("Error deleting VXData: " + e.Message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<VXData> GetVXDatas()
|
||||||
|
{
|
||||||
|
List<VXData> vxDatas = new List<VXData>();
|
||||||
|
using (var connection = new SqliteConnection(connectionString))
|
||||||
|
{
|
||||||
|
connection.Open();
|
||||||
|
|
||||||
|
var selectCmd = connection.CreateCommand();
|
||||||
|
selectCmd.CommandText = "SELECT * FROM VxTable";
|
||||||
|
using (var reader = selectCmd.ExecuteReader())
|
||||||
|
{
|
||||||
|
while (reader.Read())
|
||||||
|
{
|
||||||
|
VXData vxdata = new VXData(reader.GetString(0), reader.GetByte(1), reader.GetByte(2));
|
||||||
|
vxDatas.Add(vxdata);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return vxDatas;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool RemoveVXDatabySIID(String SIID)
|
||||||
|
{
|
||||||
|
using (var conn = new SqliteConnection(connectionString))
|
||||||
|
{
|
||||||
|
conn.Open();
|
||||||
|
var deleteCmd = conn.CreateCommand();
|
||||||
|
deleteCmd.CommandText = "DELETE FROM VxTable WHERE SIID = @SIID";
|
||||||
|
deleteCmd.Parameters.AddWithValue("@SIID", SIID);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
int result = deleteCmd.ExecuteNonQuery();
|
||||||
|
return (result > 0);
|
||||||
|
}
|
||||||
|
catch (SqliteException e)
|
||||||
|
{
|
||||||
|
Debug.WriteLine("Error deleting VXData: " + e.Message);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool AddVXData(params VXData[] data) {
|
||||||
|
using(var connection = new SqliteConnection(connectionString))
|
||||||
|
{
|
||||||
|
connection.Open();
|
||||||
|
using (var transaction = connection.BeginTransaction())
|
||||||
|
{
|
||||||
|
foreach (var item in data)
|
||||||
|
{
|
||||||
|
var insertCmd = connection.CreateCommand();
|
||||||
|
insertCmd.CommandText = "INSERT INTO VxTable (SIID, FrameID, CIN, Description) VALUES (@SIID, @FrameID, @CIN, @Description)";
|
||||||
|
insertCmd.Parameters.AddWithValue("@SIID", item.SIID);
|
||||||
|
insertCmd.Parameters.AddWithValue("@FrameID", item.FrameID);
|
||||||
|
insertCmd.Parameters.AddWithValue("@CIN", item.CIN);
|
||||||
|
insertCmd.Parameters.AddWithValue("@Description", item.Description);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
int result = insertCmd.ExecuteNonQuery();
|
||||||
|
if (result <= 0)
|
||||||
|
{
|
||||||
|
transaction.Rollback();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (SqliteException e)
|
||||||
|
{
|
||||||
|
Debug.WriteLine("Error inserting VXData: " + e.Message);
|
||||||
|
transaction.Rollback();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
transaction.Commit();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class FSMData
|
||||||
|
{
|
||||||
|
public String SIID { get; set; }
|
||||||
|
public Boolean Enable { get; set; }
|
||||||
|
public String Description { get; set; }
|
||||||
|
public String Value { get; set; }
|
||||||
|
public String LastUpdate { get; set; }
|
||||||
|
public FSMData(String siid, Boolean enable, String description)
|
||||||
|
{
|
||||||
|
this.SIID = siid;
|
||||||
|
this.Enable = enable;
|
||||||
|
this.Description = description;
|
||||||
|
this.Value = "";
|
||||||
|
this.LastUpdate = "";
|
||||||
|
}
|
||||||
|
public FSMData(String siid)
|
||||||
|
{
|
||||||
|
this.SIID = siid;
|
||||||
|
this.Enable = true;
|
||||||
|
this.Description = "";
|
||||||
|
this.Value = "";
|
||||||
|
this.LastUpdate = "";
|
||||||
|
}
|
||||||
|
public FSMData(String siid, Boolean enable)
|
||||||
|
{
|
||||||
|
this.SIID = siid;
|
||||||
|
this.Enable = enable;
|
||||||
|
this.Description = "";
|
||||||
|
this.LastUpdate = "";
|
||||||
|
this.Value = "";
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ModbusData
|
||||||
|
{
|
||||||
|
public String SIID { get; set; }
|
||||||
|
public UInt16 Register { get; set; }
|
||||||
|
public String Description { get; set; }
|
||||||
|
|
||||||
|
public String Value { get; set; }
|
||||||
|
public String LastUpdate { get; set; }
|
||||||
|
public ModbusData(String siid, UInt16 register, String description)
|
||||||
|
{
|
||||||
|
this.SIID = siid;
|
||||||
|
this.Register = register;
|
||||||
|
this.Description = description;
|
||||||
|
this.Value = "";
|
||||||
|
this.LastUpdate = "";
|
||||||
|
}
|
||||||
|
public ModbusData(String siid, UInt16 register)
|
||||||
|
{
|
||||||
|
this.SIID = siid;
|
||||||
|
this.Register = register;
|
||||||
|
this.Description = siid + " To " + register;
|
||||||
|
this.Value = "";
|
||||||
|
this.LastUpdate = "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class VXData
|
||||||
|
{
|
||||||
|
public String SIID { get; set; }
|
||||||
|
public Byte FrameID { get; set; }
|
||||||
|
public Byte CIN { get; set; }
|
||||||
|
public String Description { get; set; }
|
||||||
|
public String Value { get; set; }
|
||||||
|
public String LastUpdate { get; set; }
|
||||||
|
public VXData(String siid, Byte frameid, Byte cin, String Description)
|
||||||
|
{
|
||||||
|
this.SIID = siid;
|
||||||
|
this.FrameID = frameid;
|
||||||
|
this.CIN = cin;
|
||||||
|
this.Description=Description;
|
||||||
|
this.Value = "";
|
||||||
|
this.LastUpdate = "";
|
||||||
|
}
|
||||||
|
public VXData(String siid, Byte frameid, Byte cin)
|
||||||
|
{
|
||||||
|
this.SIID = siid;
|
||||||
|
this.FrameID = frameid;
|
||||||
|
this.CIN = cin;
|
||||||
|
this.Description = siid+" To "+ frameid + "." + cin;
|
||||||
|
this.Value = "";
|
||||||
|
this.LastUpdate = "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
15
EventInterface.cs
Normal file
15
EventInterface.cs
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace FAtoPA
|
||||||
|
{
|
||||||
|
interface EventInterface
|
||||||
|
{
|
||||||
|
void ConnectStatus(bool success, String message);
|
||||||
|
void StatisticUpdate(uint TXOK, uint RXOK, uint TXErr, uint RXerr, uint TXBytes, uint RXBytes);
|
||||||
|
void Log(String msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
207
FAtoPA.Net.csproj
Normal file
207
FAtoPA.Net.csproj
Normal file
@@ -0,0 +1,207 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||||
|
<PropertyGroup>
|
||||||
|
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||||
|
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||||
|
<ProjectGuid>{C158634C-A54E-41CD-A41A-6D0AFF841BE4}</ProjectGuid>
|
||||||
|
<OutputType>WinExe</OutputType>
|
||||||
|
<RootNamespace>FAtoPA.Net</RootNamespace>
|
||||||
|
<AssemblyName>FAtoPA.Net</AssemblyName>
|
||||||
|
<TargetFrameworkVersion>v4.8</TargetFrameworkVersion>
|
||||||
|
<FileAlignment>512</FileAlignment>
|
||||||
|
<ProjectTypeGuids>{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
|
||||||
|
<WarningLevel>4</WarningLevel>
|
||||||
|
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
|
||||||
|
<Deterministic>true</Deterministic>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||||
|
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||||
|
<DebugSymbols>true</DebugSymbols>
|
||||||
|
<DebugType>full</DebugType>
|
||||||
|
<Optimize>false</Optimize>
|
||||||
|
<OutputPath>bin\Debug\</OutputPath>
|
||||||
|
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||||
|
<ErrorReport>prompt</ErrorReport>
|
||||||
|
<WarningLevel>4</WarningLevel>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||||
|
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||||
|
<DebugType>pdbonly</DebugType>
|
||||||
|
<Optimize>true</Optimize>
|
||||||
|
<OutputPath>bin\Release\</OutputPath>
|
||||||
|
<DefineConstants>TRACE</DefineConstants>
|
||||||
|
<ErrorReport>prompt</ErrorReport>
|
||||||
|
<WarningLevel>4</WarningLevel>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
|
||||||
|
<DebugSymbols>true</DebugSymbols>
|
||||||
|
<OutputPath>bin\x64\Debug\</OutputPath>
|
||||||
|
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||||
|
<DebugType>full</DebugType>
|
||||||
|
<PlatformTarget>x64</PlatformTarget>
|
||||||
|
<LangVersion>7.3</LangVersion>
|
||||||
|
<ErrorReport>prompt</ErrorReport>
|
||||||
|
<Prefer32Bit>true</Prefer32Bit>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
|
||||||
|
<OutputPath>bin\x64\Release\</OutputPath>
|
||||||
|
<DefineConstants>TRACE</DefineConstants>
|
||||||
|
<Optimize>true</Optimize>
|
||||||
|
<DebugType>pdbonly</DebugType>
|
||||||
|
<PlatformTarget>x64</PlatformTarget>
|
||||||
|
<LangVersion>7.3</LangVersion>
|
||||||
|
<ErrorReport>prompt</ErrorReport>
|
||||||
|
<Prefer32Bit>true</Prefer32Bit>
|
||||||
|
</PropertyGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Reference Include="ApplicationProtocolCIL">
|
||||||
|
<HintPath>..\..\..\Bosch FA\FSM5000FSI-2.0.21\Release_x64\ApplicationProtocolCIL.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Backend">
|
||||||
|
<HintPath>..\..\..\Bosch FA\FSM5000FSI-2.0.21\Release_x64\Backend.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="DataExchangeInterfaces">
|
||||||
|
<HintPath>..\..\..\Bosch FA\FSM5000FSI-2.0.21\Release_x64\DataExchangeInterfaces.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="FSIConfig">
|
||||||
|
<HintPath>..\..\..\Bosch FA\FSM5000FSI-2.0.21\Release_x64\FSIConfig.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="FSITrace">
|
||||||
|
<HintPath>..\..\..\Bosch FA\FSM5000FSI-2.0.21\Release_x64\FSITrace.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="FSM5000FSI">
|
||||||
|
<HintPath>..\..\..\Bosch FA\FSM5000FSI-2.0.21\Release_x64\FSM5000FSI.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="FSM5000FSIAPI">
|
||||||
|
<HintPath>..\..\..\Bosch FA\FSM5000FSI-2.0.21\Release_x64\FSM5000FSIAPI.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="FSM5000FSILoader">
|
||||||
|
<HintPath>..\..\..\Bosch FA\FSM5000FSI-2.0.21\Release_x64\FSM5000FSILoader.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Microsoft.Bcl.AsyncInterfaces, Version=9.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
|
||||||
|
<HintPath>packages\Microsoft.Bcl.AsyncInterfaces.9.0.0\lib\net462\Microsoft.Bcl.AsyncInterfaces.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Microsoft.Data.Sqlite, Version=9.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
|
||||||
|
<HintPath>packages\Microsoft.Data.Sqlite.Core.9.0.0\lib\netstandard2.0\Microsoft.Data.Sqlite.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="MPNetCIL">
|
||||||
|
<HintPath>..\..\..\Bosch FA\FSM5000FSI-2.0.21\Release_x64\MPNetCIL.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="MPNetGate">
|
||||||
|
<HintPath>..\..\..\Bosch FA\FSM5000FSI-2.0.21\Release_x64\MPNetGate.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Repository">
|
||||||
|
<HintPath>..\..\..\Bosch FA\FSM5000FSI-2.0.21\Release_x64\Repository.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="SQLitePCLRaw.batteries_v2, Version=2.1.10.2445, Culture=neutral, PublicKeyToken=8226ea5df37bcae9, processorArchitecture=MSIL">
|
||||||
|
<HintPath>packages\SQLitePCLRaw.bundle_e_sqlite3.2.1.10\lib\net461\SQLitePCLRaw.batteries_v2.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="SQLitePCLRaw.core, Version=2.1.10.2445, Culture=neutral, PublicKeyToken=1488e028ca7ab535, processorArchitecture=MSIL">
|
||||||
|
<HintPath>packages\SQLitePCLRaw.core.2.1.10\lib\netstandard2.0\SQLitePCLRaw.core.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="SQLitePCLRaw.provider.dynamic_cdecl, Version=2.1.10.2445, Culture=neutral, PublicKeyToken=b68184102cba0b3b, processorArchitecture=MSIL">
|
||||||
|
<HintPath>packages\SQLitePCLRaw.provider.dynamic_cdecl.2.1.10\lib\netstandard2.0\SQLitePCLRaw.provider.dynamic_cdecl.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="StandardCIL">
|
||||||
|
<HintPath>..\..\..\Bosch FA\FSM5000FSI-2.0.21\Release_x64\StandardCIL.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System" />
|
||||||
|
<Reference Include="System.Buffers, Version=4.0.3.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
|
||||||
|
<HintPath>packages\System.Buffers.4.5.1\lib\net461\System.Buffers.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.Data" />
|
||||||
|
<Reference Include="System.IO.Pipelines, Version=9.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
|
||||||
|
<HintPath>packages\System.IO.Pipelines.9.0.0\lib\net462\System.IO.Pipelines.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.Memory, Version=4.0.1.2, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
|
||||||
|
<HintPath>packages\System.Memory.4.5.5\lib\net461\System.Memory.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.Numerics" />
|
||||||
|
<Reference Include="System.Numerics.Vectors, Version=4.1.4.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||||
|
<HintPath>packages\System.Numerics.Vectors.4.5.0\lib\net46\System.Numerics.Vectors.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.Runtime.CompilerServices.Unsafe, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||||
|
<HintPath>packages\System.Runtime.CompilerServices.Unsafe.6.0.0\lib\net461\System.Runtime.CompilerServices.Unsafe.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.Text.Encodings.Web, Version=9.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
|
||||||
|
<HintPath>packages\System.Text.Encodings.Web.9.0.0\lib\net462\System.Text.Encodings.Web.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.Text.Json, Version=9.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
|
||||||
|
<HintPath>packages\System.Text.Json.9.0.0\lib\net462\System.Text.Json.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.Threading.Tasks.Extensions, Version=4.2.0.1, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
|
||||||
|
<HintPath>packages\System.Threading.Tasks.Extensions.4.5.4\lib\net461\System.Threading.Tasks.Extensions.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.ValueTuple, Version=4.0.3.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
|
||||||
|
<HintPath>packages\System.ValueTuple.4.5.0\lib\net47\System.ValueTuple.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.Xml" />
|
||||||
|
<Reference Include="Microsoft.CSharp" />
|
||||||
|
<Reference Include="System.Core" />
|
||||||
|
<Reference Include="System.Xml.Linq" />
|
||||||
|
<Reference Include="System.Data.DataSetExtensions" />
|
||||||
|
<Reference Include="System.Net.Http" />
|
||||||
|
<Reference Include="System.Xaml">
|
||||||
|
<RequiredTargetFramework>4.0</RequiredTargetFramework>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="WindowsBase" />
|
||||||
|
<Reference Include="PresentationCore" />
|
||||||
|
<Reference Include="PresentationFramework" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ApplicationDefinition Include="App.xaml">
|
||||||
|
<Generator>MSBuild:Compile</Generator>
|
||||||
|
<SubType>Designer</SubType>
|
||||||
|
</ApplicationDefinition>
|
||||||
|
<Compile Include="VX3K.cs" />
|
||||||
|
<Page Include="MainWindow.xaml">
|
||||||
|
<Generator>MSBuild:Compile</Generator>
|
||||||
|
<SubType>Designer</SubType>
|
||||||
|
</Page>
|
||||||
|
<Compile Include="App.xaml.cs">
|
||||||
|
<DependentUpon>App.xaml</DependentUpon>
|
||||||
|
<SubType>Code</SubType>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="Config.cs" />
|
||||||
|
<Compile Include="Database.cs" />
|
||||||
|
<Compile Include="EventInterface.cs" />
|
||||||
|
<Compile Include="FSM.cs" />
|
||||||
|
<Compile Include="FSMResultInterface.cs" />
|
||||||
|
<Compile Include="MainWindow.xaml.cs">
|
||||||
|
<DependentUpon>MainWindow.xaml</DependentUpon>
|
||||||
|
<SubType>Code</SubType>
|
||||||
|
</Compile>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Compile Include="MappingData.cs" />
|
||||||
|
<Compile Include="ModbusSlave.cs" />
|
||||||
|
<Compile Include="NodeData.cs" />
|
||||||
|
<Compile Include="Properties\AssemblyInfo.cs">
|
||||||
|
<SubType>Code</SubType>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="Properties\Resources.Designer.cs">
|
||||||
|
<AutoGen>True</AutoGen>
|
||||||
|
<DesignTime>True</DesignTime>
|
||||||
|
<DependentUpon>Resources.resx</DependentUpon>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="Properties\Settings.Designer.cs">
|
||||||
|
<AutoGen>True</AutoGen>
|
||||||
|
<DependentUpon>Settings.settings</DependentUpon>
|
||||||
|
<DesignTimeSharedInput>True</DesignTimeSharedInput>
|
||||||
|
</Compile>
|
||||||
|
<EmbeddedResource Include="Properties\Resources.resx">
|
||||||
|
<Generator>ResXFileCodeGenerator</Generator>
|
||||||
|
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
|
||||||
|
</EmbeddedResource>
|
||||||
|
<None Include="packages.config" />
|
||||||
|
<None Include="Properties\Settings.settings">
|
||||||
|
<Generator>SettingsSingleFileGenerator</Generator>
|
||||||
|
<LastGenOutput>Settings.Designer.cs</LastGenOutput>
|
||||||
|
</None>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<None Include="App.config" />
|
||||||
|
</ItemGroup>
|
||||||
|
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||||
|
</Project>
|
||||||
28
FAtoPA.Net.sln
Normal file
28
FAtoPA.Net.sln
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
|
||||||
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
|
# Visual Studio Version 17
|
||||||
|
VisualStudioVersion = 17.12.35514.174 d17.12
|
||||||
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FAtoPA.Net", "FAtoPA.Net.csproj", "{C158634C-A54E-41CD-A41A-6D0AFF841BE4}"
|
||||||
|
EndProject
|
||||||
|
Global
|
||||||
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
|
Debug|Any CPU = Debug|Any CPU
|
||||||
|
Debug|x64 = Debug|x64
|
||||||
|
Release|Any CPU = Release|Any CPU
|
||||||
|
Release|x64 = Release|x64
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||||
|
{C158634C-A54E-41CD-A41A-6D0AFF841BE4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{C158634C-A54E-41CD-A41A-6D0AFF841BE4}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{C158634C-A54E-41CD-A41A-6D0AFF841BE4}.Debug|x64.ActiveCfg = Debug|x64
|
||||||
|
{C158634C-A54E-41CD-A41A-6D0AFF841BE4}.Debug|x64.Build.0 = Debug|x64
|
||||||
|
{C158634C-A54E-41CD-A41A-6D0AFF841BE4}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{C158634C-A54E-41CD-A41A-6D0AFF841BE4}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{C158634C-A54E-41CD-A41A-6D0AFF841BE4}.Release|x64.ActiveCfg = Release|x64
|
||||||
|
{C158634C-A54E-41CD-A41A-6D0AFF841BE4}.Release|x64.Build.0 = Release|x64
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
|
HideSolutionNode = FALSE
|
||||||
|
EndGlobalSection
|
||||||
|
EndGlobal
|
||||||
288
FSM.cs
Normal file
288
FSM.cs
Normal file
@@ -0,0 +1,288 @@
|
|||||||
|
using FSM5000FSI;
|
||||||
|
using FSM5000FSIAPI.Version3;
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.CodeDom;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace FAtoPA
|
||||||
|
{
|
||||||
|
class FSM
|
||||||
|
{
|
||||||
|
public static bool Started = false;
|
||||||
|
private FSIConfig config;
|
||||||
|
private FSIController controller;
|
||||||
|
private List<FSMResultInterface> listenerlist;
|
||||||
|
public Dictionary<int, String> ItemTypeDictionary { get; }
|
||||||
|
public Dictionary<String, NodeData> AvailableNodes { get; }
|
||||||
|
private EventInterface _event;
|
||||||
|
public FSM(EventInterface callback)
|
||||||
|
{
|
||||||
|
_event = callback;
|
||||||
|
ItemTypeDictionary = new Dictionary<int, string>();
|
||||||
|
AvailableNodes = new Dictionary<String, NodeData>();
|
||||||
|
listenerlist = new List<FSMResultInterface>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initialize Fire Alarm Module
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="netgroup">1 - 255</param>
|
||||||
|
/// <param name="netnode">1 - 255</param>
|
||||||
|
/// <param name="pna">1 - 255</param>
|
||||||
|
/// <param name="localip">on a system with multiple network adapter, use this to specify which adapter will be used</param>
|
||||||
|
public FSM(EventInterface callback, byte netgroup, byte netnode, byte pna, byte[] localip) : this(callback) {
|
||||||
|
SetConfig(netgroup, netnode, pna, localip);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetConfig(byte netgroup, byte netnode, byte pna, byte[] localip)
|
||||||
|
{
|
||||||
|
config = new FSIConfig();
|
||||||
|
config.MPNetGroup = netgroup;
|
||||||
|
config.MPNetNode = netnode;
|
||||||
|
config.PNA = pna;
|
||||||
|
config.LocalIPAddress = localip;
|
||||||
|
config.RepositoryLocation = ".\\repository";
|
||||||
|
config.AdvancedFSIConfig = new AdvancedFSIConfig();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Optional option if using Multicast Communication
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="useMulticast">true if want to use Multicast</param>
|
||||||
|
/// <param name="MulticastIP">target Multicast IP, if not available, default to 239.192.0.1</param>
|
||||||
|
/// <param name="PortNumber">target Port, if not available, default to 25000</param>
|
||||||
|
public void MulticastConfig(Boolean useMulticast, byte[] MulticastIP, ushort PortNumber)
|
||||||
|
{
|
||||||
|
if (config != null)
|
||||||
|
{
|
||||||
|
if (config.AdvancedFSIConfig == null) config.AdvancedFSIConfig = new AdvancedFSIConfig();
|
||||||
|
config.AdvancedFSIConfig.IsIPMulticast = useMulticast;
|
||||||
|
config.AdvancedFSIConfig.MulticastAddress = MulticastIP;
|
||||||
|
config.AdvancedFSIConfig.PortNumber = PortNumber;
|
||||||
|
config.AdvancedFSIConfig.NetstackTraceLevel = SourceLevels.Error;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Start FSM Monitoring
|
||||||
|
/// </summary>
|
||||||
|
public void Start()
|
||||||
|
{
|
||||||
|
controller = FSIController.GetInstance();
|
||||||
|
// internal Listeners
|
||||||
|
controller.AddListener(new ConfigListener(ItemTypeDictionary, AvailableNodes, listenerlist));
|
||||||
|
controller.AddListener(new StateListener(AvailableNodes, listenerlist));
|
||||||
|
TraceConsoleWritter outputter = new TraceConsoleWritter();
|
||||||
|
controller.SetTraceLevel(SourceLevels.Warning); //for support issues please set the level to SourceLevels.Verbose
|
||||||
|
Result result = controller.Startup(config);
|
||||||
|
//Debug.WriteLine("[FSM] Start result = "+result.ToString());
|
||||||
|
if (result==Result.SUCCESS)
|
||||||
|
{
|
||||||
|
Started = true;
|
||||||
|
if (_event != null) _event.ConnectStatus(true, "FSM Started");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Started = false;
|
||||||
|
if (_event != null) _event.ConnectStatus(false, "FSM Failed to Start");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AddListener(FSMResultInterface listener)
|
||||||
|
{
|
||||||
|
if (listenerlist != null) listenerlist.Add(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Stop FSM Monitoring
|
||||||
|
/// </summary>
|
||||||
|
public void Stop()
|
||||||
|
{
|
||||||
|
if (controller != null)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Result result = controller.Shutdown();
|
||||||
|
//Console.WriteLine("[FSM] Shutdown result = " + result.ToString());
|
||||||
|
if (_event!=null) _event.ConnectStatus(false, "FSM Stopped");
|
||||||
|
} catch (Exception e)
|
||||||
|
{
|
||||||
|
Debug.WriteLine("[FSM] Error on Stop: " + e.Message);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The Config Listener receives all information about the configuration
|
||||||
|
/// received from the panel network. The information must be stored in real application
|
||||||
|
/// to know the devices from the panel network and the possible commands.
|
||||||
|
/// </summary>
|
||||||
|
class ConfigListener : IPanelConfigListener
|
||||||
|
{
|
||||||
|
private Dictionary<int, String> type_dictionary;
|
||||||
|
private Dictionary<String, NodeData> node_data;
|
||||||
|
private List<FSMResultInterface> listeners;
|
||||||
|
public ConfigListener(Dictionary<int, String> type, Dictionary<String, NodeData> data, List<FSMResultInterface> listener)
|
||||||
|
{
|
||||||
|
this.type_dictionary = type;
|
||||||
|
this.node_data = data;
|
||||||
|
this.listeners = listener;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Akan muncul di pertama kali, untuk informasi bahwa device dengan FunctionalType [X} memiliki deskripsi [Y]
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="devItemType"></param>
|
||||||
|
public void SetItemType(ItemType devItemType)
|
||||||
|
{
|
||||||
|
Debug.WriteLine($"Item type {devItemType.FunctionalType} ({devItemType.Description})");
|
||||||
|
if (type_dictionary != null)
|
||||||
|
{
|
||||||
|
int type = int.Parse(devItemType.FunctionalType.ToString());
|
||||||
|
if (!type_dictionary.ContainsKey(type)) type_dictionary.Add(type, devItemType.Description);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Akan muncul di urutan kedua, untuk informasi node device apa saja yang terdeteksi di system
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="devItem"></param>
|
||||||
|
public void SetItem(Item devItem)
|
||||||
|
{
|
||||||
|
Debug.WriteLine($"Item ({devItem.SIID}, {devItem.FunctionalType} ({devItem.Label})");
|
||||||
|
if (node_data != null)
|
||||||
|
{
|
||||||
|
String SIID = devItem.SIID.ToString();
|
||||||
|
int type = int.Parse(devItem.FunctionalType.ToString());
|
||||||
|
if (!node_data.ContainsKey(SIID))
|
||||||
|
{
|
||||||
|
NodeData nodeData = new NodeData();
|
||||||
|
nodeData.SIID = devItem.SIID;
|
||||||
|
nodeData.Type = type;
|
||||||
|
nodeData.Label = devItem.Label;
|
||||||
|
nodeData.Description = type_dictionary.ContainsKey(type) ? type_dictionary[type] : "Unknown";
|
||||||
|
node_data.Add(SIID, nodeData);
|
||||||
|
// notify all listeners
|
||||||
|
if (listeners != null)
|
||||||
|
foreach (var item in listeners)
|
||||||
|
item.DiscoveredSIID(SIID, nodeData);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Akan muncul kalau suatu node hilang dari system
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="address"></param>
|
||||||
|
public void RemoveItem(SIID address)
|
||||||
|
{
|
||||||
|
Debug.WriteLine("SI " + address + " removed.");
|
||||||
|
if (node_data != null)
|
||||||
|
{
|
||||||
|
String SIID = address.ToString();
|
||||||
|
if (node_data.ContainsKey(SIID)) node_data.Remove(SIID);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The State Listener receive all states for devices send by the panel network.
|
||||||
|
/// Ini yang paling penting, untuk trigger ke Modbus dan VX3000
|
||||||
|
/// </summary>
|
||||||
|
class StateListener : IStateListener
|
||||||
|
{
|
||||||
|
private Dictionary<String,NodeData> node_data;
|
||||||
|
private List<FSMResultInterface> listeners;
|
||||||
|
public StateListener(Dictionary<String,NodeData> data, List<FSMResultInterface> listener)
|
||||||
|
{
|
||||||
|
this.node_data= data;
|
||||||
|
this.listeners = listener;
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Akan muncul ketiga dan seterusnya, ketika ada perubahan status dari node device
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="devItemState"></param>
|
||||||
|
public void SetItemState(ItemState devItemState)
|
||||||
|
{
|
||||||
|
Debug.WriteLine($"State {devItemState.SIID} = {devItemState.LogicalState}");
|
||||||
|
if (node_data != null)
|
||||||
|
{
|
||||||
|
String SIID = devItemState.SIID.ToString();
|
||||||
|
if (node_data.ContainsKey(SIID))
|
||||||
|
{
|
||||||
|
NodeData nd = node_data[SIID];
|
||||||
|
if (nd != null)
|
||||||
|
{
|
||||||
|
NodeState prev = nd.State;
|
||||||
|
nd.State = new NodeState(devItemState);
|
||||||
|
|
||||||
|
// notify all listeners
|
||||||
|
if (listeners != null)
|
||||||
|
foreach (var item in listeners)
|
||||||
|
item.NewState(SIID, prev, nd.State);
|
||||||
|
|
||||||
|
if (prev != null)
|
||||||
|
{
|
||||||
|
Debug.WriteLine("SIID=" + SIID + " Change LogicalState from " + prev.LogicalState + " to " + nd.State.LogicalState);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Debug.WriteLine("New State for SIID=" + SIID + " LogicalState=" + nd.State.LogicalState);
|
||||||
|
}
|
||||||
|
} else Debug.WriteLine("NodeData with SIID " + SIID + " is null");
|
||||||
|
} else Debug.WriteLine("node_data doesn't contain SIID " + SIID);
|
||||||
|
|
||||||
|
} else Debug.WriteLine("node_data is null");
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Akan muncul sekali di awal mula, sebagai informasi waktu di panel system
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="mpNetTime"></param>
|
||||||
|
public void SetMPNetTime(DateTime mpNetTime)
|
||||||
|
{
|
||||||
|
Debug.WriteLine("Time is " + mpNetTime + ".");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Receive the traces from the FSI and output the traces to console
|
||||||
|
/// In real application it is very useful to write the traces to files
|
||||||
|
/// for suppport issues
|
||||||
|
/// </summary>
|
||||||
|
class TraceConsoleWritter : TraceListener
|
||||||
|
{
|
||||||
|
public override void Write(string message)
|
||||||
|
{
|
||||||
|
if (message!=null && message.Length>0) Debug.Write(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void WriteLine(string message)
|
||||||
|
{
|
||||||
|
if (message != null && message.Length > 0) Debug.WriteLine(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
14
FSMResultInterface.cs
Normal file
14
FSMResultInterface.cs
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace FAtoPA
|
||||||
|
{
|
||||||
|
interface FSMResultInterface
|
||||||
|
{
|
||||||
|
void DiscoveredSIID(String SIID, NodeData type);
|
||||||
|
void NewState(String SIID, NodeState previous, NodeState current);
|
||||||
|
}
|
||||||
|
}
|
||||||
279
MainWindow.xaml
Normal file
279
MainWindow.xaml
Normal file
@@ -0,0 +1,279 @@
|
|||||||
|
<Window x:Class="FAtoPA.Net.MainWindow"
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
|
xmlns:local="clr-namespace:FAtoPA.Net"
|
||||||
|
mc:Ignorable="d"
|
||||||
|
Loaded="Window_Loaded"
|
||||||
|
Closing="Window_Closing"
|
||||||
|
Title="MainWindow" Height="450" Width="800">
|
||||||
|
<Grid>
|
||||||
|
<DockPanel>
|
||||||
|
<StatusBar DockPanel.Dock="Bottom" Height="30">
|
||||||
|
<StatusBar.ItemsPanel>
|
||||||
|
<ItemsPanelTemplate>
|
||||||
|
<Grid>
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="auto"/>
|
||||||
|
<ColumnDefinition Width="auto"/>
|
||||||
|
<ColumnDefinition Width="auto"/>
|
||||||
|
<ColumnDefinition Width="*"/>
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
</Grid>
|
||||||
|
</ItemsPanelTemplate>
|
||||||
|
</StatusBar.ItemsPanel>
|
||||||
|
<StatusBarItem Grid.Column="0" HorizontalContentAlignment="Center" BorderBrush="Black" BorderThickness="1">
|
||||||
|
<TextBlock x:Name="firealarmstatusbar" Text="Fire Alarm Status" />
|
||||||
|
</StatusBarItem>
|
||||||
|
|
||||||
|
<StatusBarItem Grid.Column="1" HorizontalContentAlignment="Center" BorderBrush="Black" BorderThickness="1">
|
||||||
|
<TextBlock x:Name="modbusstatusbar" Text="Modbus Status" />
|
||||||
|
</StatusBarItem>
|
||||||
|
<StatusBarItem Grid.Column="2" HorizontalContentAlignment="Center" BorderBrush="Black" BorderThickness="1">
|
||||||
|
<TextBlock x:Name="vxstatusbar" Text="VX-3000 Status" />
|
||||||
|
</StatusBarItem>
|
||||||
|
<StatusBarItem Grid.Column="3" HorizontalContentAlignment="Center" BorderBrush="Black" BorderThickness="1">
|
||||||
|
<TextBlock x:Name="datetimebar" Text="Date and Time" />
|
||||||
|
</StatusBarItem>
|
||||||
|
</StatusBar>
|
||||||
|
<TabControl>
|
||||||
|
<TabItem Header="Fire Alarm">
|
||||||
|
<DockPanel>
|
||||||
|
<Grid DockPanel.Dock="Top" Height="75">
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="150"/>
|
||||||
|
<ColumnDefinition Width="500"/>
|
||||||
|
<ColumnDefinition Width="*"/>
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<Button Margin="5,5,5,5" x:Name="btnStartStopFSM" Content="Start FSM Connection" Grid.Column="0" Click="btnStartStopFSM_Click" />
|
||||||
|
<Grid Grid.Column="1">
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="*"/>
|
||||||
|
<ColumnDefinition Width="*"/>
|
||||||
|
<ColumnDefinition Width="*"/>
|
||||||
|
<ColumnDefinition Width="*"/>
|
||||||
|
<ColumnDefinition Width="*"/>
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<Grid Grid.Column="0">
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="*"/>
|
||||||
|
<RowDefinition Height="*"/>
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
<Label Content="NetGroup" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" Grid.Row="0"/>
|
||||||
|
<ComboBox x:Name="netGroupNumber" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" Grid.Row="1" Margin="5,0,5,5" ItemsSource="{Binding NetGroupList, IsAsync=True}" SelectedIndex="0"/>
|
||||||
|
</Grid>
|
||||||
|
<Grid Grid.Column="1">
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="*"/>
|
||||||
|
<RowDefinition Height="*"/>
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
<Label Content="NetNode" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" Grid.Row="0"/>
|
||||||
|
<ComboBox x:Name="netNodeNumber" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" Grid.Row="1" Margin="5,0,5,5" ItemsSource="{Binding NetNodeList, IsAsync=True}" SelectedIndex="0"/>
|
||||||
|
</Grid>
|
||||||
|
<Grid Grid.Column="2">
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="*"/>
|
||||||
|
<RowDefinition Height="*"/>
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
<Label Content="SI Type" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" Grid.Row="0"/>
|
||||||
|
<ComboBox x:Name="siType" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" Grid.Row="1" Margin="5,0,5,5" ItemsSource="{Binding SIType, IsAsync=True}" SelectedIndex="0"/>
|
||||||
|
</Grid>
|
||||||
|
<Grid Grid.Column="3">
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="*"/>
|
||||||
|
<RowDefinition Height="*"/>
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
<Label Content="SI Number" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" Grid.Row="0"/>
|
||||||
|
<ComboBox x:Name="siNumber" Text="1" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" Grid.Row="1" Margin="5,0,5,5" ItemsSource="{Binding SINumberList, IsAsync=True}" SelectedIndex="0"/>
|
||||||
|
</Grid>
|
||||||
|
<Grid Grid.Column="4">
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="*"/>
|
||||||
|
<RowDefinition Height="*"/>
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
<Label Content="SI Sub" Grid.Row="0" HorizontalContentAlignment="Center" VerticalContentAlignment="Center"/>
|
||||||
|
<ComboBox x:Name="siSub" Text="1" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" Grid.Row="1" Margin="5,0,5,5" ItemsSource="{Binding SISubList, IsAsync=True}" SelectedIndex="0"/>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
<Grid Grid.Column="2">
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="*"/>
|
||||||
|
<ColumnDefinition Width="*"/>
|
||||||
|
<ColumnDefinition Width="*"/>
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<Button Grid.Column="0" Margin="5" x:Name="btnAddSIID" Content="Add To Table" Click="btnAddSIID_Click"/>
|
||||||
|
<Button Grid.Column="1" Margin="5" x:Name="btnDelSIID" Content="Remove From Table" Click="btnDelSIID_Click"/>
|
||||||
|
<Button Grid.Column="2" Margin="5" x:Name="btnClearSIID" Content="Clear Table" Click="btnClearSIID_Click"/>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
<DockPanel DockPanel.Dock="Left" Width="200">
|
||||||
|
<Label Content="Detected SIID" DockPanel.Dock="Top" HorizontalContentAlignment="Center" VerticalContentAlignment="Center"/>
|
||||||
|
<Label Content="Count : 0" DockPanel.Dock="Bottom" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" x:Name="DetectedSIIDCount"/>
|
||||||
|
<ListBox x:Name="DetectedSIID" />
|
||||||
|
</DockPanel>
|
||||||
|
<DataGrid MinRowHeight="50" x:Name="FSMTable" AutoGenerateColumns="True" AutoGeneratingColumn="FSMTable_AutoGeneratingColumn" />
|
||||||
|
|
||||||
|
|
||||||
|
</DockPanel>
|
||||||
|
</TabItem>
|
||||||
|
<TabItem Header="Modbus">
|
||||||
|
<DockPanel>
|
||||||
|
<Grid DockPanel.Dock="Top" Height="75">
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="150"/>
|
||||||
|
<ColumnDefinition Width="500"/>
|
||||||
|
<ColumnDefinition Width="*"/>
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<Button Margin="5" x:Name="btnStartStopModbus" Content="Start Modbus Connection" Grid.Column="0" Click="btnStartStopModbus_Click"/>
|
||||||
|
<Grid Grid.Column="1">
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="75"/>
|
||||||
|
<ColumnDefinition/>
|
||||||
|
<ColumnDefinition Width="6*"/>
|
||||||
|
<ColumnDefinition Width="75"/>
|
||||||
|
<ColumnDefinition Width="7*"/>
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<Label Grid.Column="0" Content="SIID" HorizontalContentAlignment="Center" VerticalContentAlignment="Center"/>
|
||||||
|
<ComboBox Grid.Column="1" x:Name="ModbusSIIDComboBox" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" Margin="5,0,5,0" ItemsSource="{Binding FSMSIID, IsAsync=True}" Grid.ColumnSpan="2" SelectedIndex="0"/>
|
||||||
|
<Label Grid.Column="3" Content="Register" HorizontalContentAlignment="Center" VerticalContentAlignment="Center"/>
|
||||||
|
<ComboBox Grid.Column="4" x:Name="ModbusRegister" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" Margin="5,0,5,0" ItemsSource="{Binding ModbusRegisters, IsAsync=True}" SelectedIndex="0" />
|
||||||
|
</Grid>
|
||||||
|
<Grid Grid.Column="2">
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="*"/>
|
||||||
|
<ColumnDefinition Width="*"/>
|
||||||
|
<ColumnDefinition Width="*"/>
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<Button Grid.Column="0" Margin="5" x:Name="btnAddModbus" Content="Add To Table" Click="btnAddModbus_Click"/>
|
||||||
|
<Button Grid.Column="1" Margin="5" x:Name="btnDelModbus" Content="Remove From Table" Click="btnDelModbus_Click"/>
|
||||||
|
<Button Grid.Column="2" Margin="5" x:Name="btnClearModbus" Content="Clear Table" Click="btnClearModbus_Click"/>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
<DockPanel DockPanel.Dock="Left" Width="200">
|
||||||
|
<Label Content="Connected Modbus Client" DockPanel.Dock="Top" HorizontalContentAlignment="Center" VerticalContentAlignment="Center"/>
|
||||||
|
<Label Content="Count : 0" DockPanel.Dock="Bottom" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" x:Name="ConnectedModbusCount"/>
|
||||||
|
<ListBox x:Name="ConnectedModbusClients" />
|
||||||
|
</DockPanel>
|
||||||
|
<DataGrid x:Name="ModbusTable" AutoGenerateColumns="True" AutoGeneratingColumn="ModbusTable_AutoGeneratingColumn" />
|
||||||
|
</DockPanel>
|
||||||
|
</TabItem>
|
||||||
|
<TabItem Header="TOA VX-3000">
|
||||||
|
<DockPanel>
|
||||||
|
<Grid DockPanel.Dock="Top" Height="75">
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="150"/>
|
||||||
|
<ColumnDefinition Width="500"/>
|
||||||
|
<ColumnDefinition Width="*"/>
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<Button Margin="5,5,5,5" x:Name="btnStartStopVX" Content="Start VX-3000 Connection" Grid.Column="0" Click="btnStartStopVX_Click" />
|
||||||
|
<Grid Grid.Column="1">
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="50"/>
|
||||||
|
<ColumnDefinition Width="*"/>
|
||||||
|
<ColumnDefinition Width="50"/>
|
||||||
|
<ColumnDefinition Width="*"/>
|
||||||
|
<ColumnDefinition Width="50"/>
|
||||||
|
<ColumnDefinition Width="*"/>
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<Label Grid.Column="0" Content="SIID" HorizontalContentAlignment="Center" VerticalContentAlignment="Center"/>
|
||||||
|
<ComboBox Grid.Column="1" x:Name="VXSIIDComboBox" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" Margin="5,0" ItemsSource="{Binding FSMSIID, IsAsync=True}" SelectedIndex="0"/>
|
||||||
|
<Label Grid.Column="2" Content="Frame" HorizontalContentAlignment="Center" VerticalContentAlignment="Center"/>
|
||||||
|
<ComboBox Grid.Column="3" x:Name="VXFrame" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" Margin="5,0" ItemsSource="{Binding VX3KID, IsAsync=True}" SelectedIndex="0"/>
|
||||||
|
<Label Grid.Column="4" Content="C-IN" HorizontalContentAlignment="Center" VerticalContentAlignment="Center"/>
|
||||||
|
<ComboBox Grid.Column="5" x:Name="VXCIN" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" Margin="5,0" ItemsSource="{Binding VX3KCIN, IsAsync=True}" SelectedIndex="0" />
|
||||||
|
</Grid>
|
||||||
|
<Grid Grid.Column="2">
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="*"/>
|
||||||
|
<ColumnDefinition Width="*"/>
|
||||||
|
<ColumnDefinition Width="*"/>
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<Button Grid.Column="0" Margin="5" x:Name="btnAddVX" Content="Add To Table" Click="btnAddVX_Click"/>
|
||||||
|
<Button Grid.Column="1" Margin="5" x:Name="btnDelVX" Content="Remove From Table" Click="btnDelVX_Click"/>
|
||||||
|
<Button Grid.Column="2" Margin="5" x:Name="btnClearVX" Content="Clear Table" Click="btnClearVX_Click"/>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
<DataGrid x:Name="VXTable" AutoGenerateColumns="True" AutoGeneratingColumn="VXTable_AutoGeneratingColumn" />
|
||||||
|
</DockPanel>
|
||||||
|
|
||||||
|
</TabItem>
|
||||||
|
<TabItem Header="Settings">
|
||||||
|
<StackPanel Orientation="Vertical">
|
||||||
|
<GroupBox Header="Fire Alarm Settings">
|
||||||
|
<DockPanel>
|
||||||
|
<Button x:Name="btnApplyFSMConfig" Content="Apply Config" Click="ApplyFSMConfig" DockPanel.Dock="Right" Margin="5"/>
|
||||||
|
<StackPanel Orientation="Vertical">
|
||||||
|
<StackPanel Orientation="Horizontal">
|
||||||
|
<Label Content="NetGroup" Width="150"/>
|
||||||
|
<TextBox x:Name="FSMConfig_NetGroup" Text="1" MinWidth="30" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" PreviewTextInput="FSMConfig_NetGroup_PreviewTextInput" TextChanged="FSMConfig_NetGroup_TextChanged"/>
|
||||||
|
</StackPanel>
|
||||||
|
<StackPanel Orientation="Horizontal">
|
||||||
|
<Label Content="NetNode" Width="150"/>
|
||||||
|
<TextBox x:Name="FSMConfig_NetNode" Text="1" MinWidth="30" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" PreviewTextInput="FSMConfig_NetNode_PreviewTextInput" TextChanged="FSMConfig_NetNode_TextChanged"/>
|
||||||
|
</StackPanel>
|
||||||
|
<StackPanel Orientation="Horizontal">
|
||||||
|
<Label Content="PNA" Width="150"/>
|
||||||
|
<TextBox x:Name="FSMConfig_PNA" Text="1" MinWidth="30" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" PreviewTextInput="FSMConfig_PNA_PreviewTextInput" TextChanged="FSMConfig_PNA_TextChanged"/>
|
||||||
|
</StackPanel>
|
||||||
|
<StackPanel Orientation="Horizontal">
|
||||||
|
<Label Content="Local IP Address" Width="150"/>
|
||||||
|
<TextBox x:Name="FSMConfig_LocalIP" Text="0.0.0.0" MinWidth="75" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" PreviewTextInput="FSMConfig_LocalIP_PreviewTextInput" TextChanged="FSMConfig_LocalIP_TextChanged"/>
|
||||||
|
</StackPanel>
|
||||||
|
<StackPanel Orientation="Horizontal">
|
||||||
|
<Label Content="Use Multicast" Width="150"/>
|
||||||
|
<CheckBox x:Name="FSM_UseMulticast" Content="No" IsChecked="False" VerticalAlignment="Center"/>
|
||||||
|
</StackPanel>
|
||||||
|
<StackPanel Orientation="Horizontal">
|
||||||
|
<Label Content="Multicast Address" Width="150"/>
|
||||||
|
<TextBox x:Name="FSMConfig_MulticastAddress" Text="239.192.0.1" MinWidth="75" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" PreviewTextInput="FSMConfig_MulticastAddress_PreviewTextInput" TextChanged="FSMConfig_MulticastAddress_TextChanged"/>
|
||||||
|
</StackPanel>
|
||||||
|
<StackPanel Orientation="Horizontal">
|
||||||
|
<Label Content="Multicast Port" Width="150"/>
|
||||||
|
<TextBox x:Name="FSMConfig_MulticastPort" Text="25000" MinWidth="50" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" PreviewTextInput="FSMConfig_MulticastPort_PreviewTextInput" TextChanged="FSMConfig_MulticastPort_TextChanged"/>
|
||||||
|
</StackPanel>
|
||||||
|
</StackPanel>
|
||||||
|
</DockPanel>
|
||||||
|
|
||||||
|
</GroupBox>
|
||||||
|
<GroupBox Header="Modbus Setting">
|
||||||
|
<DockPanel>
|
||||||
|
<Button x:Name="btnApplyModbusConfig" Content="Apply Config" Click="ApplyModbusConfig" DockPanel.Dock="Right" Margin="5"/>
|
||||||
|
<StackPanel Orientation="Vertical">
|
||||||
|
<StackPanel Orientation="Horizontal">
|
||||||
|
<Label Width="150" Content="Listen Port"/>
|
||||||
|
<TextBox x:Name="ModbusListenPort" Text="502" MinWidth="50" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" PreviewTextInput="ModbusListenPort_PreviewTextInput" TextChanged="ModbusListenPort_TextChanged"/>
|
||||||
|
</StackPanel>
|
||||||
|
<StackPanel Orientation="Horizontal">
|
||||||
|
<Label Width="150" Content="Device ID"/>
|
||||||
|
<TextBox x:Name="ModbusDeviceID" Text="1" MinWidth="50" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" PreviewTextInput="ModbusDeviceID_PreviewTextInput" TextChanged="ModbusDeviceID_TextChanged"/>
|
||||||
|
</StackPanel>
|
||||||
|
<StackPanel Orientation="Horizontal">
|
||||||
|
<Label Width="150" Content="Max. Register"/>
|
||||||
|
<TextBox x:Name="ModbusMaxRegister" Text="2000" MinWidth="50" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" PreviewTextInput="ModbusMaxRegister_PreviewTextInput" TextChanged="ModbusMaxRegister_TextChanged"/>
|
||||||
|
</StackPanel>
|
||||||
|
</StackPanel>
|
||||||
|
</DockPanel>
|
||||||
|
</GroupBox>
|
||||||
|
<GroupBox Header="VX-3000">
|
||||||
|
<DockPanel Margin="-1,0,1,0">
|
||||||
|
<Button x:Name="btnApplyVX3KConfig" Content="Apply Config" Margin="5" Click="ApplyVX3KConfig" DockPanel.Dock="Right"/>
|
||||||
|
<StackPanel Orientation="Vertical">
|
||||||
|
<StackPanel Orientation="Horizontal">
|
||||||
|
<Label Width="150" Content="VX-3000 IP" />
|
||||||
|
<TextBox x:Name="VX3K_IP" Text="192.168.14.1" MinWidth="75" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" PreviewTextInput="VX3K_IP_PreviewTextInput" TextChanged="VX3K_IP_TextChanged"/>
|
||||||
|
</StackPanel>
|
||||||
|
<StackPanel Orientation="Horizontal">
|
||||||
|
<Label Width="150" Content="VX-3000 Port" />
|
||||||
|
<TextBox x:Name="VX3K_Port" Text="5000" MinWidth="50" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" PreviewTextInput="VX3K_Port_PreviewTextInput" TextChanged="VX3K_Port_TextChanged" />
|
||||||
|
</StackPanel>
|
||||||
|
</StackPanel>
|
||||||
|
</DockPanel>
|
||||||
|
</GroupBox>
|
||||||
|
</StackPanel>
|
||||||
|
</TabItem>
|
||||||
|
</TabControl>
|
||||||
|
|
||||||
|
</DockPanel>
|
||||||
|
</Grid>
|
||||||
|
</Window>
|
||||||
1417
MainWindow.xaml.cs
Normal file
1417
MainWindow.xaml.cs
Normal file
File diff suppressed because it is too large
Load Diff
26
MappingData.cs
Normal file
26
MappingData.cs
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace FAtoPA
|
||||||
|
{
|
||||||
|
class MappingData
|
||||||
|
{
|
||||||
|
// key is SIID
|
||||||
|
public String SIID { get; set; }
|
||||||
|
// if enable, will trigger with Modbus Register
|
||||||
|
public Boolean enableModbus { get; set; }
|
||||||
|
// if SIID active, link with which Modbus Register
|
||||||
|
public UInt16 ModbusRegister { get; set; }
|
||||||
|
// if SIID active, modify which bit from Modbus Register
|
||||||
|
public UInt16 ModbusBit { get; set; }
|
||||||
|
// if enable, will trigger with Virtual Contact
|
||||||
|
public Boolean enableVirtualContactInput { get; set; }
|
||||||
|
// if SIID active, Link with Virtual Contact Input from which Frame ID
|
||||||
|
public Byte VXFrame { get; set; }
|
||||||
|
// if SIID active, trigger which Virtual Contact Input from Frame ID
|
||||||
|
public Byte VXCin { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
545
ModbusSlave.cs
Normal file
545
ModbusSlave.cs
Normal file
@@ -0,0 +1,545 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Net.Sockets;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Windows.Input;
|
||||||
|
|
||||||
|
namespace FAtoPA
|
||||||
|
{
|
||||||
|
// Source : https://www.simplymodbus.ca/TCP.htm
|
||||||
|
// Source : https://www.modbustools.com/modbus.html
|
||||||
|
|
||||||
|
internal class ModbusSlave
|
||||||
|
{
|
||||||
|
public static bool Started = false;
|
||||||
|
TcpListener listener;
|
||||||
|
Dictionary<String, ModbusClientRecord> clientmap;
|
||||||
|
UInt16[] Registers;
|
||||||
|
Boolean running = false;
|
||||||
|
public byte ID { get; set; } = 1;
|
||||||
|
|
||||||
|
private ModbusSlaveEvent _event;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Modbus Slave is actually a server (TCPListener),<br/>
|
||||||
|
/// that contain data and can be read or written by <br/>
|
||||||
|
/// a Modbus Master (which actually a TCPClient)
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="maxregister">Maximum Register that can be read or written</param>
|
||||||
|
public ModbusSlave(ModbusSlaveEvent callback, UInt16 maxregister)
|
||||||
|
{
|
||||||
|
_event = callback;
|
||||||
|
clientmap = new Dictionary<String, ModbusClientRecord>();
|
||||||
|
Registers = new UInt16[maxregister];
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get Register Count available in Modbus Slave
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>Register Count</returns>
|
||||||
|
public UInt16 GetRegisterCount()
|
||||||
|
{
|
||||||
|
return (UInt16)Registers.Length;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Set Register Value
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="address">start from 0 until GetRegisterCount() - 1</param>
|
||||||
|
/// <param name="value">between 0 until 65535</param>
|
||||||
|
public void SetRegister(UInt16 address, UInt16 value)
|
||||||
|
{
|
||||||
|
if (address < Registers.Length)
|
||||||
|
{
|
||||||
|
Registers[address] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get Register Value
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="address">start from 0 until GetRegisterCount() - 1 </param>
|
||||||
|
/// <returns>between 0 until 65535</returns>
|
||||||
|
public UInt16 GetRegister(UInt16 address)
|
||||||
|
{
|
||||||
|
if (address < Registers.Length)
|
||||||
|
{
|
||||||
|
return Registers[address];
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Set Bit Value in Register
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="address">start from 0 until GetRegisterCount() -1 </param>
|
||||||
|
/// <param name="bit">start from 0 until 15</param>
|
||||||
|
/// <param name="value">true = ON, false = OFF</param>
|
||||||
|
public void SetBit(UInt16 address, UInt16 bit, Boolean value)
|
||||||
|
{
|
||||||
|
if (address < Registers.Length)
|
||||||
|
{
|
||||||
|
UInt16 reg = Registers[address];
|
||||||
|
if (value)
|
||||||
|
{
|
||||||
|
reg |= (UInt16)(1 << bit);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
reg &= (UInt16)~(1 << bit);
|
||||||
|
}
|
||||||
|
Registers[address] = reg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get Bit Value in Register
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="address">start from 0 until 65535</param>
|
||||||
|
/// <param name="bit"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public Boolean GetBit(UInt16 address, UInt16 bit)
|
||||||
|
{
|
||||||
|
if (address < Registers.Length)
|
||||||
|
{
|
||||||
|
UInt16 reg = Registers[address];
|
||||||
|
return (reg & (1 << bit)) != 0;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Start Modbus Slave
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="port">Listening Port</param>
|
||||||
|
/// <returns>true if success</returns>
|
||||||
|
public bool Start(UInt16 port)
|
||||||
|
{
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
TcpListener newlistener = new TcpListener(System.Net.IPAddress.Any, port);
|
||||||
|
newlistener.Start();
|
||||||
|
|
||||||
|
new Thread(()=> DoListening(newlistener, port)).Start();
|
||||||
|
if (_event != null) _event.ConnectStatus(true, "Modbus Listener Started at port "+port);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
if (_event!=null) _event.ConnectStatus(false, "Modbus Listener failed to start at port "+port+", Message : "+ e.Message);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Stop Modbus Slave
|
||||||
|
/// </summary>
|
||||||
|
public void Stop()
|
||||||
|
{
|
||||||
|
running = false;
|
||||||
|
if (listener != null)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
|
||||||
|
listener.Stop();
|
||||||
|
if (_event != null)
|
||||||
|
{
|
||||||
|
_event.ConnectStatus(true, "ModbusSlave: Stop Success");
|
||||||
|
}
|
||||||
|
} catch (Exception e)
|
||||||
|
{
|
||||||
|
if (_event != null)
|
||||||
|
{
|
||||||
|
_event.ConnectStatus(false, "ModbusSlave: Stop failed, exception: " + e.Message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
listener = null;
|
||||||
|
}
|
||||||
|
foreach (ModbusClientRecord client in clientmap.Values)
|
||||||
|
{
|
||||||
|
CloseConnection(client.Client);
|
||||||
|
if (_event != null)
|
||||||
|
{
|
||||||
|
_event.CloseConnection(client);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CloseConnection(TcpClient client)
|
||||||
|
{
|
||||||
|
if (client != null)
|
||||||
|
{
|
||||||
|
client.Close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get Transaction ID from Modbus Command<br/>
|
||||||
|
/// TransactionID is 2 bytes at index [0,1]
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="cmd">Modbus Command</param>
|
||||||
|
/// <returns>TransactionID or 0 if failed</returns>
|
||||||
|
private UInt16 GetTransactionID(byte[] cmd)
|
||||||
|
{
|
||||||
|
if (cmd.Length >= 2)
|
||||||
|
{
|
||||||
|
return (UInt16)((cmd[0] << 8) + cmd[1]);
|
||||||
|
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get Protocol ID from Modbus Command<br/>
|
||||||
|
/// ProtocolID is 2 bytes at index [2,3]<br/>
|
||||||
|
/// For TCP Modbus, ProtocolID is always 0
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="cmd">Modbus Command</param>
|
||||||
|
/// <returns>ProtocolID</returns>
|
||||||
|
private UInt16 GetProtocolID(byte[] cmd)
|
||||||
|
{
|
||||||
|
if (cmd.Length >= 4)
|
||||||
|
{
|
||||||
|
return (UInt16)((cmd[2] << 8) + cmd[3]);
|
||||||
|
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get Length from Modbus Command<br/>
|
||||||
|
/// Length is 2 bytes at index [4,5]<br/>
|
||||||
|
/// Length is Modbus Payload Length + 2 (Unit ID and Function Code)
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="cmd">Modbus Command</param>
|
||||||
|
/// <returns>Length or 0 if failed</returns>
|
||||||
|
private UInt16 GetLength(byte[] cmd)
|
||||||
|
{
|
||||||
|
if (cmd.Length >= 6)
|
||||||
|
{
|
||||||
|
return (UInt16)((cmd[4] << 8) + cmd[5]);
|
||||||
|
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get Unit ID from Modbus Command<br/>
|
||||||
|
/// UnitID is 1 byte at index [6]
|
||||||
|
/// For Modbus Slave, UnitID is always 1
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="cmd">Modbus Command</param>
|
||||||
|
/// <returns>Unit ID or 0 if failed</returns>
|
||||||
|
private Byte GetUnitID(byte[] cmd)
|
||||||
|
{
|
||||||
|
if (cmd.Length >= 7)
|
||||||
|
{
|
||||||
|
return cmd[6];
|
||||||
|
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get Function Code from Modbus Command<br/>
|
||||||
|
/// Function Code is 1 byte at index [7]
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="cmd">Modbus Command</param>
|
||||||
|
/// <returns>Function Code or 0 if failed</returns>
|
||||||
|
private Byte GetFunctionCode(byte[] cmd)
|
||||||
|
{
|
||||||
|
if (cmd.Length >= 8)
|
||||||
|
{
|
||||||
|
return cmd[7];
|
||||||
|
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get Modbus Payload from Modbus Command<br/>
|
||||||
|
/// Modbus Payload begins at index 8
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="cmd">Modbus Command</param>
|
||||||
|
/// <returns>Modbus Payload or empty array if failed</returns>
|
||||||
|
private byte[] GetModbusPayload(byte[] cmd)
|
||||||
|
{
|
||||||
|
if (cmd.Length > 8)
|
||||||
|
{
|
||||||
|
byte[] data = new byte[cmd.Length - 8];
|
||||||
|
Array.Copy(cmd,8, data, 0, data.Length);
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
return Array.Empty<byte>();
|
||||||
|
}
|
||||||
|
|
||||||
|
async void DoListening(TcpListener listener, int port)
|
||||||
|
{
|
||||||
|
running = true;
|
||||||
|
if (_event != null) _event.Log("ModbusSlave: Start Accepting Connections on port " + port);
|
||||||
|
while (running)
|
||||||
|
{
|
||||||
|
TcpClient client = null;
|
||||||
|
String key = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
client = await listener.AcceptTcpClientAsync();
|
||||||
|
key = client.Client.RemoteEndPoint?.ToString();
|
||||||
|
if (key == null)
|
||||||
|
{
|
||||||
|
CloseConnection(client);
|
||||||
|
client = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
if (_event != null) _event.Log("ModbusSlave: Exception " + e.Message);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (key != null && client != null)
|
||||||
|
{
|
||||||
|
if (_event!=null) _event.Log("ModbusSlave: New Connection from " + key);
|
||||||
|
ModbusClientRecord prev = clientmap[key];
|
||||||
|
|
||||||
|
if (prev != null)
|
||||||
|
{
|
||||||
|
CloseConnection(prev.Client);
|
||||||
|
if (_event != null)
|
||||||
|
{
|
||||||
|
_event.CloseConnection(prev);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ModbusClientRecord newclient = new ModbusClientRecord(client);
|
||||||
|
clientmap[key] = newclient;
|
||||||
|
new Thread(() => DoCommunication(newclient)).Start();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
if (_event!=null) _event.Log("ModbusSlave: Stop Accepting Connections on port " + port);
|
||||||
|
}
|
||||||
|
|
||||||
|
async void DoCommunication(ModbusClientRecord client)
|
||||||
|
{
|
||||||
|
// komunikasi di sini
|
||||||
|
String key = client.remoteEP ?? "";
|
||||||
|
if (_event != null)
|
||||||
|
{
|
||||||
|
_event.NewConnection(client);
|
||||||
|
_event.Log("ModbusSlave: Start Communication with " + key);
|
||||||
|
}
|
||||||
|
NetworkStream stream = client.GetStream();
|
||||||
|
while (running && stream != null)
|
||||||
|
{
|
||||||
|
// spare 12 bytes untuk protocol modbus Read Register :
|
||||||
|
// 2 bytes Transaction ID, index [0,1]
|
||||||
|
// 2 bytes Protocol ID (always 0) , index [2,3]
|
||||||
|
// 2 bytes Length , index [4,5]
|
||||||
|
// 1 byte Unit ID, index [6]
|
||||||
|
// 1 byte Function Code, index [7]
|
||||||
|
// (3 = Read Holding Register)
|
||||||
|
// 2 byte Register Address, index [8,9]
|
||||||
|
// 2 byte Register Count, index [10,11]
|
||||||
|
byte[] cmd = new byte[12];
|
||||||
|
try {
|
||||||
|
int readcount = await stream.ReadAsync(new byte[12], 0, 12);
|
||||||
|
if (readcount >= 12)
|
||||||
|
{
|
||||||
|
client.RXBytes += (uint)readcount;
|
||||||
|
UInt16 transactionID = GetTransactionID(cmd);
|
||||||
|
UInt16 protocolID = GetProtocolID(cmd);
|
||||||
|
UInt16 length = GetLength(cmd);
|
||||||
|
Byte unitID = GetUnitID(cmd);
|
||||||
|
Byte functionCode = GetFunctionCode(cmd);
|
||||||
|
byte[] payload = GetModbusPayload(cmd);
|
||||||
|
|
||||||
|
if (protocolID == 0 && length == payload.Length + 2 && unitID == 1)
|
||||||
|
{
|
||||||
|
if (functionCode == 3)
|
||||||
|
{
|
||||||
|
//Implement Read Register
|
||||||
|
UInt16 registerAddress = (UInt16)((payload[0] << 8) + payload[1]);
|
||||||
|
UInt16 registerCount = (UInt16)((payload[2] << 8) + payload[3]);
|
||||||
|
Boolean valid = (registerAddress + registerCount <= Registers.Length);
|
||||||
|
byte[] response;
|
||||||
|
if (valid)
|
||||||
|
{
|
||||||
|
response = new byte[9 + registerCount * 2];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Register Address out of range
|
||||||
|
response = new byte[9];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2 bytes Transaction ID, index [0,1]
|
||||||
|
response[0] = (byte)(transactionID >> 8);
|
||||||
|
response[1] = (byte)(transactionID & 0xFF);
|
||||||
|
// 2 bytes Protocol ID (always 0) , index [2,3]
|
||||||
|
response[2] = 0;
|
||||||
|
response[3] = 0;
|
||||||
|
// 2 bytes Length , index [4,5]
|
||||||
|
if (valid)
|
||||||
|
{
|
||||||
|
UInt16 _length = (UInt16)(3 + registerCount * 2);
|
||||||
|
response[4] = (byte)(_length >> 8);
|
||||||
|
response[5] = (byte)(_length & 0xFF);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
response[4] = 0;
|
||||||
|
response[5] = 3;
|
||||||
|
}
|
||||||
|
// Unit ID, index [6]
|
||||||
|
response[6] = unitID;
|
||||||
|
// Function Code, index [7]
|
||||||
|
response[7] = valid ? functionCode : (byte)(functionCode + 0x80);
|
||||||
|
|
||||||
|
// Number of Register or Exception Code, index [8]
|
||||||
|
if (valid)
|
||||||
|
{
|
||||||
|
response[8] = (byte)(registerCount * 2);
|
||||||
|
// Register values, 2 bytes each , start from index [9]
|
||||||
|
for (int i = 0; i < registerCount; i++)
|
||||||
|
{
|
||||||
|
UInt16 value = GetRegister((UInt16)(registerAddress + i));
|
||||||
|
response[9 + i * 2] = (byte)(value >> 8);
|
||||||
|
response[10 + i * 2] = (byte)(value & 0xFF);
|
||||||
|
}
|
||||||
|
client.RXValidRequest++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Exception Code 2 : Illegal Data Address
|
||||||
|
response[8] = 2;
|
||||||
|
client.RXInvalidRequest++;
|
||||||
|
}
|
||||||
|
// Send Response
|
||||||
|
await stream.WriteAsync(response, 0, response.Length);
|
||||||
|
client.TXBytes += (uint)response.Length;
|
||||||
|
client.TXResponse++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
client.RXInvalidRequest++;
|
||||||
|
// exception, invalid function code
|
||||||
|
if (_event != null) _event.Log("ModbusSlave: Invalid Function Code " + functionCode);
|
||||||
|
byte[] response = new byte[9];
|
||||||
|
// 2 bytes Transaction ID, index [0,1]
|
||||||
|
response[0] = (byte)(transactionID >> 8);
|
||||||
|
response[1] = (byte)(transactionID & 0xFF);
|
||||||
|
// 2 bytes Protocol ID (always 0) , index [2,3]
|
||||||
|
response[2] = (byte)(protocolID >> 8);
|
||||||
|
response[3] = (byte)(protocolID & 0xFF);
|
||||||
|
// 2 bytes Length , index [4,5]
|
||||||
|
response[4] = 0;
|
||||||
|
response[5] = 3;
|
||||||
|
// Unit ID, index [6]
|
||||||
|
response[6] = unitID;
|
||||||
|
// Function Code, index [7]
|
||||||
|
response[7] = (byte)(functionCode + 0x80);
|
||||||
|
// Exception Code 1 : Illegal Function
|
||||||
|
response[8] = 1;
|
||||||
|
await stream.WriteAsync(response, 0, response.Length);
|
||||||
|
client.TXBytes += (uint)response.Length;
|
||||||
|
client.TXResponse++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
client.RXInvalidRequest++;
|
||||||
|
// exception, invalid protocol ID, length, or unit ID
|
||||||
|
if (_event != null) _event.Log("ModbusSlave: Invalid Protocol ID, Length, or Unit ID");
|
||||||
|
byte[] response = new byte[9];
|
||||||
|
// 2 bytes Transaction ID, index [0,1]
|
||||||
|
response[0] = (byte)(transactionID >> 8);
|
||||||
|
response[1] = (byte)(transactionID & 0xFF);
|
||||||
|
// 2 bytes Protocol ID (always 0) , index [2,3]
|
||||||
|
response[2] = (byte)(protocolID >> 8);
|
||||||
|
response[3] = (byte)(protocolID & 0xFF);
|
||||||
|
// 2 bytes Length , index [4,5]
|
||||||
|
response[4] = 0;
|
||||||
|
response[5] = 3;
|
||||||
|
// Unit ID, index [6]
|
||||||
|
response[6] = unitID;
|
||||||
|
// Function Code, index [7]
|
||||||
|
response[7] = (byte)(functionCode + 0x80);
|
||||||
|
// Exception Code 3 : Illegal Query
|
||||||
|
response[8] = 3;
|
||||||
|
await stream.WriteAsync(response, 0, response.Length);
|
||||||
|
client.TXBytes += (uint)response.Length;
|
||||||
|
client.TXResponse++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
if (_event!=null) _event.Log("ModbusSlave: Exception " + e.Message);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
if (_event != null) _event.Log("ModbusSlave: Stop Communication with " + key);
|
||||||
|
CloseConnection(client.Client);
|
||||||
|
clientmap.Remove(key);
|
||||||
|
if (_event != null) _event.CloseConnection(client);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class ModbusClientRecord
|
||||||
|
{
|
||||||
|
public TcpClient Client;
|
||||||
|
public UInt32 TXBytes, RXBytes;
|
||||||
|
public UInt32 RXValidRequest, RXInvalidRequest, TXResponse;
|
||||||
|
public String remoteEP;
|
||||||
|
public ModbusClientRecord(TcpClient client)
|
||||||
|
{
|
||||||
|
Client = client;
|
||||||
|
remoteEP = "";
|
||||||
|
TXBytes = 0;
|
||||||
|
RXBytes = 0;
|
||||||
|
RXValidRequest = 0;
|
||||||
|
RXInvalidRequest = 0;
|
||||||
|
TXResponse = 0;
|
||||||
|
if (Client != null)
|
||||||
|
{
|
||||||
|
if (Client.Connected)
|
||||||
|
{
|
||||||
|
if (Client.Client.RemoteEndPoint != null)
|
||||||
|
{
|
||||||
|
remoteEP = Client.Client.RemoteEndPoint.ToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public NetworkStream GetStream()
|
||||||
|
{
|
||||||
|
return Client?.GetStream() ?? null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ModbusSlaveEvent
|
||||||
|
{
|
||||||
|
void Log(String msg);
|
||||||
|
void NewConnection(ModbusClientRecord client);
|
||||||
|
void CloseConnection(ModbusClientRecord client);
|
||||||
|
void ConnectStatus(bool success, string message);
|
||||||
|
void TXRXStatusUpdate(ModbusClientRecord client);
|
||||||
|
}
|
||||||
|
}
|
||||||
41
NodeData.cs
Normal file
41
NodeData.cs
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
using FSM5000FSIAPI.Version3;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace FAtoPA
|
||||||
|
{
|
||||||
|
class NodeData
|
||||||
|
{
|
||||||
|
public SIID SIID { get; set; }
|
||||||
|
public int Type { get; set; }
|
||||||
|
public String Label { get; set; }
|
||||||
|
public String Description { get; set; }
|
||||||
|
public NodeState State { get; set; }
|
||||||
|
public NodeData() { }
|
||||||
|
}
|
||||||
|
|
||||||
|
class NodeState
|
||||||
|
{
|
||||||
|
public DateTime TimeStamp { get; set; }
|
||||||
|
public String AdminState { get; set; }
|
||||||
|
public String LogicalState { get; set; }
|
||||||
|
public String CompoundState { get; set; }
|
||||||
|
public String StateOfHandling { get; set; }
|
||||||
|
public NodeState() { }
|
||||||
|
public NodeState(ItemState itemState)
|
||||||
|
{
|
||||||
|
if (itemState != null)
|
||||||
|
{
|
||||||
|
this.TimeStamp = itemState.TimeStamp;
|
||||||
|
this.AdminState = itemState.AdminState.ToString();
|
||||||
|
this.LogicalState = itemState.LogicalState.ToString();
|
||||||
|
this.StateOfHandling = itemState.StateOfHandling.ToString();
|
||||||
|
this.CompoundState = itemState.CompoundState.ToString();
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
52
Properties/AssemblyInfo.cs
Normal file
52
Properties/AssemblyInfo.cs
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
using System.Reflection;
|
||||||
|
using System.Resources;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Windows;
|
||||||
|
|
||||||
|
// General Information about an assembly is controlled through the following
|
||||||
|
// set of attributes. Change these attribute values to modify the information
|
||||||
|
// associated with an assembly.
|
||||||
|
[assembly: AssemblyTitle("FAtoPA.Net")]
|
||||||
|
[assembly: AssemblyDescription("")]
|
||||||
|
[assembly: AssemblyConfiguration("")]
|
||||||
|
[assembly: AssemblyCompany("")]
|
||||||
|
[assembly: AssemblyProduct("FAtoPA.Net")]
|
||||||
|
[assembly: AssemblyCopyright("Copyright © 2024")]
|
||||||
|
[assembly: AssemblyTrademark("")]
|
||||||
|
[assembly: AssemblyCulture("")]
|
||||||
|
|
||||||
|
// Setting ComVisible to false makes the types in this assembly not visible
|
||||||
|
// to COM components. If you need to access a type in this assembly from
|
||||||
|
// COM, set the ComVisible attribute to true on that type.
|
||||||
|
[assembly: ComVisible(false)]
|
||||||
|
|
||||||
|
//In order to begin building localizable applications, set
|
||||||
|
//<UICulture>CultureYouAreCodingWith</UICulture> in your .csproj file
|
||||||
|
//inside a <PropertyGroup>. For example, if you are using US english
|
||||||
|
//in your source files, set the <UICulture> to en-US. Then uncomment
|
||||||
|
//the NeutralResourceLanguage attribute below. Update the "en-US" in
|
||||||
|
//the line below to match the UICulture setting in the project file.
|
||||||
|
|
||||||
|
//[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)]
|
||||||
|
|
||||||
|
|
||||||
|
[assembly: ThemeInfo(
|
||||||
|
ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
|
||||||
|
//(used if a resource is not found in the page,
|
||||||
|
// or application resource dictionaries)
|
||||||
|
ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
|
||||||
|
//(used if a resource is not found in the page,
|
||||||
|
// app, or any theme specific resource dictionaries)
|
||||||
|
)]
|
||||||
|
|
||||||
|
|
||||||
|
// Version information for an assembly consists of the following four values:
|
||||||
|
//
|
||||||
|
// Major Version
|
||||||
|
// Minor Version
|
||||||
|
// Build Number
|
||||||
|
// Revision
|
||||||
|
//
|
||||||
|
[assembly: AssemblyVersion("1.0.0.0")]
|
||||||
|
[assembly: AssemblyFileVersion("1.0.0.0")]
|
||||||
71
Properties/Resources.Designer.cs
generated
Normal file
71
Properties/Resources.Designer.cs
generated
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// <auto-generated>
|
||||||
|
// This code was generated by a tool.
|
||||||
|
// Runtime Version:4.0.30319.42000
|
||||||
|
//
|
||||||
|
// Changes to this file may cause incorrect behavior and will be lost if
|
||||||
|
// the code is regenerated.
|
||||||
|
// </auto-generated>
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
namespace FAtoPA.Net.Properties
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A strongly-typed resource class, for looking up localized strings, etc.
|
||||||
|
/// </summary>
|
||||||
|
// This class was auto-generated by the StronglyTypedResourceBuilder
|
||||||
|
// class via a tool like ResGen or Visual Studio.
|
||||||
|
// To add or remove a member, edit your .ResX file then rerun ResGen
|
||||||
|
// with the /str option, or rebuild your VS project.
|
||||||
|
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
|
||||||
|
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||||
|
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||||
|
internal class Resources
|
||||||
|
{
|
||||||
|
|
||||||
|
private static global::System.Resources.ResourceManager resourceMan;
|
||||||
|
|
||||||
|
private static global::System.Globalization.CultureInfo resourceCulture;
|
||||||
|
|
||||||
|
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
|
||||||
|
internal Resources()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the cached ResourceManager instance used by this class.
|
||||||
|
/// </summary>
|
||||||
|
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||||
|
internal static global::System.Resources.ResourceManager ResourceManager
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if ((resourceMan == null))
|
||||||
|
{
|
||||||
|
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("FAtoPA.Net.Properties.Resources", typeof(Resources).Assembly);
|
||||||
|
resourceMan = temp;
|
||||||
|
}
|
||||||
|
return resourceMan;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Overrides the current thread's CurrentUICulture property for all
|
||||||
|
/// resource lookups using this strongly typed resource class.
|
||||||
|
/// </summary>
|
||||||
|
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||||
|
internal static global::System.Globalization.CultureInfo Culture
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return resourceCulture;
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
resourceCulture = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
117
Properties/Resources.resx
Normal file
117
Properties/Resources.resx
Normal file
@@ -0,0 +1,117 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<root>
|
||||||
|
<!--
|
||||||
|
Microsoft ResX Schema
|
||||||
|
|
||||||
|
Version 2.0
|
||||||
|
|
||||||
|
The primary goals of this format is to allow a simple XML format
|
||||||
|
that is mostly human readable. The generation and parsing of the
|
||||||
|
various data types are done through the TypeConverter classes
|
||||||
|
associated with the data types.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
... ado.net/XML headers & schema ...
|
||||||
|
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||||
|
<resheader name="version">2.0</resheader>
|
||||||
|
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||||
|
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||||
|
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||||
|
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||||
|
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||||
|
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||||
|
</data>
|
||||||
|
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||||
|
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||||
|
<comment>This is a comment</comment>
|
||||||
|
</data>
|
||||||
|
|
||||||
|
There are any number of "resheader" rows that contain simple
|
||||||
|
name/value pairs.
|
||||||
|
|
||||||
|
Each data row contains a name, and value. The row also contains a
|
||||||
|
type or mimetype. Type corresponds to a .NET class that support
|
||||||
|
text/value conversion through the TypeConverter architecture.
|
||||||
|
Classes that don't support this are serialized and stored with the
|
||||||
|
mimetype set.
|
||||||
|
|
||||||
|
The mimetype is used for serialized objects, and tells the
|
||||||
|
ResXResourceReader how to depersist the object. This is currently not
|
||||||
|
extensible. For a given mimetype the value must be set accordingly:
|
||||||
|
|
||||||
|
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||||
|
that the ResXResourceWriter will generate, however the reader can
|
||||||
|
read any of the formats listed below.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.binary.base64
|
||||||
|
value : The object must be serialized with
|
||||||
|
: System.Serialization.Formatters.Binary.BinaryFormatter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.soap.base64
|
||||||
|
value : The object must be serialized with
|
||||||
|
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||||
|
value : The object must be serialized into a byte array
|
||||||
|
: using a System.ComponentModel.TypeConverter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
-->
|
||||||
|
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||||
|
<xsd:element name="root" msdata:IsDataSet="true">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:choice maxOccurs="unbounded">
|
||||||
|
<xsd:element name="metadata">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" type="xsd:string" />
|
||||||
|
<xsd:attribute name="type" type="xsd:string" />
|
||||||
|
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="assembly">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:attribute name="alias" type="xsd:string" />
|
||||||
|
<xsd:attribute name="name" type="xsd:string" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="data">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||||
|
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" type="xsd:string" msdata:Ordinal="1" />
|
||||||
|
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||||
|
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="resheader">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
</xsd:choice>
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
</xsd:schema>
|
||||||
|
<resheader name="resmimetype">
|
||||||
|
<value>text/microsoft-resx</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="version">
|
||||||
|
<value>2.0</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="reader">
|
||||||
|
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="writer">
|
||||||
|
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
|
</resheader>
|
||||||
|
</root>
|
||||||
30
Properties/Settings.Designer.cs
generated
Normal file
30
Properties/Settings.Designer.cs
generated
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// <auto-generated>
|
||||||
|
// This code was generated by a tool.
|
||||||
|
// Runtime Version:4.0.30319.42000
|
||||||
|
//
|
||||||
|
// Changes to this file may cause incorrect behavior and will be lost if
|
||||||
|
// the code is regenerated.
|
||||||
|
// </auto-generated>
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
namespace FAtoPA.Net.Properties
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||||
|
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")]
|
||||||
|
internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase
|
||||||
|
{
|
||||||
|
|
||||||
|
private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
|
||||||
|
|
||||||
|
public static Settings Default
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return defaultInstance;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
7
Properties/Settings.settings
Normal file
7
Properties/Settings.settings
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
<?xml version='1.0' encoding='utf-8'?>
|
||||||
|
<SettingsFile xmlns="uri:settings" CurrentProfile="(Default)">
|
||||||
|
<Profiles>
|
||||||
|
<Profile Name="(Default)" />
|
||||||
|
</Profiles>
|
||||||
|
<Settings />
|
||||||
|
</SettingsFile>
|
||||||
361
VX3K.cs
Normal file
361
VX3K.cs
Normal file
@@ -0,0 +1,361 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Net.Sockets;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace FAtoPA
|
||||||
|
{
|
||||||
|
class VX3K
|
||||||
|
{
|
||||||
|
public static bool Started = false;
|
||||||
|
private String targetip = "192.168.14.1";
|
||||||
|
private UInt16 targetport = 5000;
|
||||||
|
private TcpClient socket;
|
||||||
|
|
||||||
|
// Statistics
|
||||||
|
public uint TXBytes { get; private set; } = 0;
|
||||||
|
public uint RXBytes { get; private set; } = 0;
|
||||||
|
public uint TXOK { get; private set; } = 0;
|
||||||
|
public uint RXOK { get; private set; } = 0;
|
||||||
|
public uint TXErr { get; private set; } = 0;
|
||||||
|
public uint RXErr { get; private set; } = 0;
|
||||||
|
|
||||||
|
private readonly EventInterface _event;
|
||||||
|
|
||||||
|
public VX3K(EventInterface callback){
|
||||||
|
this._event = callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Connect to VX-3000
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="ipaddress">target IP address, default to 192.168.14.1</param>
|
||||||
|
/// <param name="port">target port, default to 5000</param>
|
||||||
|
public Boolean Connect(String ipaddress, UInt16 port)
|
||||||
|
{
|
||||||
|
TXBytes = 0;
|
||||||
|
RXBytes = 0;
|
||||||
|
TXOK = 0;
|
||||||
|
RXOK = 0;
|
||||||
|
TXErr = 0;
|
||||||
|
RXErr = 0;
|
||||||
|
|
||||||
|
raise_statistic();
|
||||||
|
|
||||||
|
if (ipaddress!=null && ipaddress.Length>0)
|
||||||
|
{
|
||||||
|
targetip=ipaddress;
|
||||||
|
raise_log("Target IP changed to " + targetip);
|
||||||
|
}
|
||||||
|
if (port>0 && port<65535)
|
||||||
|
{
|
||||||
|
targetport=port;
|
||||||
|
raise_log("Target Port changed to " + targetport);
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
TcpClient newclient = new TcpClient();
|
||||||
|
bool success = newclient.ConnectAsync(targetip, targetport).Wait(1000);
|
||||||
|
if (success && newclient.Connected)
|
||||||
|
{
|
||||||
|
socket = newclient;
|
||||||
|
raise_connect_status(true, "Connected to " + targetip + ":" + targetport);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else raise_connect_status(false, "Failed to connect to " + targetip + ":" + targetport);
|
||||||
|
|
||||||
|
} catch(Exception e)
|
||||||
|
{
|
||||||
|
raise_connect_status(false, "Error: " + e.Message);
|
||||||
|
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Check if connected to VX-3000
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>true if connected</returns>
|
||||||
|
public Boolean IsConnected()
|
||||||
|
{
|
||||||
|
if (socket != null)
|
||||||
|
{
|
||||||
|
return socket.Connected;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Disconnect from VX-3000
|
||||||
|
/// </summary>
|
||||||
|
public void Disconnect()
|
||||||
|
{
|
||||||
|
if (socket != null)
|
||||||
|
{
|
||||||
|
socket.Close();
|
||||||
|
socket = null;
|
||||||
|
raise_connect_status(false, "Disconnected from " + targetip + ":" + targetport);
|
||||||
|
}
|
||||||
|
else raise_connect_status(false, "Not connected yet");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Set Virtual Contact Input ON/OFF <br/>
|
||||||
|
/// Contact Input ID : <br/>
|
||||||
|
/// 0 - 15 : Contact Input 1 - 16 <br/>
|
||||||
|
/// 16 : Emergency Contact input 1 <br/>
|
||||||
|
/// 17 : Emergency Contact input 2 <br/>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="vxID">VX Frame ID</param>
|
||||||
|
/// <param name="CinID">Contact Input ID</param>
|
||||||
|
/// <param name="isON">true = ON, false = OFF</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public Boolean Virtual_Contact_Input(UInt16 vxID, UInt16 CinID, Boolean isON)
|
||||||
|
{
|
||||||
|
byte[] payload = new byte[6];
|
||||||
|
payload[0] = HighByte(vxID);
|
||||||
|
payload[1] = LowByte(vxID);
|
||||||
|
payload[2] = HighByte(CinID);
|
||||||
|
payload[3] = LowByte(CinID);
|
||||||
|
payload[4] = 0x00;
|
||||||
|
payload[5] = (byte)(isON ? 0x01 : 0x00);
|
||||||
|
|
||||||
|
byte[] cmd = make_header_request_command(0x1001, payload);
|
||||||
|
|
||||||
|
if (SendCommand(cmd))
|
||||||
|
{
|
||||||
|
byte[] response = ReadResponse();
|
||||||
|
if (GetCommandCode(response) == 0x1001)
|
||||||
|
{
|
||||||
|
if (GetResponseCode(response) == 0x0000)
|
||||||
|
{
|
||||||
|
raise_log("Virtual Contact Input Command Success");
|
||||||
|
return true;
|
||||||
|
} else raise_log("Virtual Contact Input Command Failed, Invalid Response Code");
|
||||||
|
} else raise_log("Virtual Contact Input Command Failed, Invalid Command Code");
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sending command to VX-3000
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="cmd">Array of bytes containing command</param>
|
||||||
|
/// <returns>true if success</returns>
|
||||||
|
private Boolean SendCommand(byte[] cmd)
|
||||||
|
{
|
||||||
|
if (IsConnected() && socket != null)
|
||||||
|
{
|
||||||
|
if (cmd != null && cmd.Length > 0)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
NetworkStream stream = socket.GetStream();
|
||||||
|
stream.Write(cmd, 0, cmd.Length);
|
||||||
|
TXBytes += (uint)cmd.Length;
|
||||||
|
TXOK++;
|
||||||
|
raise_statistic();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
raise_log("SendCommand failed, Error : "+e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else raise_log("SendCommand failed, Invalid Command");
|
||||||
|
}
|
||||||
|
else raise_log("SendCommand failed, Not Connected to VX-3000");
|
||||||
|
|
||||||
|
TXErr++;
|
||||||
|
raise_statistic();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reading response from VX-3000
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>array of bytes containing response , or empty array if failed</returns>
|
||||||
|
private byte[] ReadResponse()
|
||||||
|
{
|
||||||
|
if (IsConnected() && socket != null)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
NetworkStream stream = socket.GetStream();
|
||||||
|
byte[] response = new byte[1500];
|
||||||
|
int bytes = stream.Read(response, 0, response.Length);
|
||||||
|
RXBytes += (uint)bytes;
|
||||||
|
RXOK++;
|
||||||
|
raise_statistic();
|
||||||
|
byte[] result = new byte[bytes];
|
||||||
|
Array.Copy(response, result, bytes);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
raise_log("ReadResponse failed, Error : " + e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else raise_log("ReadResponse failed, Not Connected to VX-3000");
|
||||||
|
RXErr++;
|
||||||
|
raise_statistic();
|
||||||
|
return Array.Empty<byte>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create VX-3000 command bytes
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="command">command code</param>
|
||||||
|
/// <param name="payload">payload to send</param>
|
||||||
|
/// <returns>array of bytes</returns>
|
||||||
|
private byte[] make_header_request_command(UInt16 command, byte[] payload)
|
||||||
|
{
|
||||||
|
UInt16 cmdlen = 8;
|
||||||
|
if (payload != null && payload.Length > 0) cmdlen += (UInt16)payload.Length;
|
||||||
|
|
||||||
|
byte[] header = new byte[cmdlen];
|
||||||
|
header[0] = HighByte(command);
|
||||||
|
header[1] = LowByte(command);
|
||||||
|
header[2] = 0x00;
|
||||||
|
header[3] = 0x00;
|
||||||
|
header[4] = HighByte(cmdlen);
|
||||||
|
header[5] = LowByte(cmdlen);
|
||||||
|
header[6] = 0x80; // request to VX3000
|
||||||
|
header[7] = 0x00;
|
||||||
|
|
||||||
|
if (payload == null) return header; // no payload
|
||||||
|
if (payload.Length == 0) return header; // no payload
|
||||||
|
|
||||||
|
for(int i=0; i<payload.Length; i++)
|
||||||
|
{
|
||||||
|
header[8 + i] = payload[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
return header;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Return High Byte of a 16-bit value
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">16 bit value</param>
|
||||||
|
/// <returns>Byte value</returns>
|
||||||
|
private byte HighByte(UInt16 value)
|
||||||
|
{
|
||||||
|
return (byte)((value >> 8) & 0xFF);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Return Low Byte of a 16-bit value
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">16 bit value</param>
|
||||||
|
/// <returns> Byte value</returns>
|
||||||
|
private byte LowByte(UInt16 value)
|
||||||
|
{
|
||||||
|
return (byte)(value & 0xFF);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get Command Code from response
|
||||||
|
/// Command Code value is Big-Endian UInt16 from index 0 and 1
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">array of byte from Response</param>
|
||||||
|
/// <returns>Command Code or 0 if failed</returns>
|
||||||
|
private UInt16 GetCommandCode(byte[] value)
|
||||||
|
{
|
||||||
|
if (value != null && value.Length >= 2)
|
||||||
|
{
|
||||||
|
return (UInt16)((value[0] << 8) | value[1]);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get Response Code from response
|
||||||
|
/// Response Code value is Big-Endian UInt16 from index 2 and 3
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">array of byte from Response</param>
|
||||||
|
/// <returns>Response Code or 0 if failed</returns>
|
||||||
|
private UInt16 GetResponseCode(byte[] value)
|
||||||
|
{
|
||||||
|
if (value != null && value.Length >= 4)
|
||||||
|
{
|
||||||
|
return (UInt16)((value[2] << 8) | value[3]);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get Command Length from response
|
||||||
|
/// Command Length value is Big-Endian UInt16 from index 4 and 5
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">array of byte from Response</param>
|
||||||
|
/// <returns>Command Length or 0 if failed</returns>
|
||||||
|
private UInt16 GetCommandLength(byte[] value)
|
||||||
|
{
|
||||||
|
if (value != null && value.Length >= 6)
|
||||||
|
{
|
||||||
|
return (UInt16)((value[4] << 8) | value[5]);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get Command Flag from response
|
||||||
|
/// Command Flag value is Byte at index 6
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">array of byte from Response</param>
|
||||||
|
/// <returns>Command Flag or 0 if failed</returns>
|
||||||
|
private byte GetCommandFlag(byte[] value)
|
||||||
|
{
|
||||||
|
if (value != null && value.Length >= 7)
|
||||||
|
{
|
||||||
|
return value[6];
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get Payload from Response
|
||||||
|
/// Payload is located at index 8 to end of array
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">array of byte from Response</param>
|
||||||
|
/// <returns>Payload bytes or empty array if failed</returns>
|
||||||
|
private byte[] GetPayload(byte[] value)
|
||||||
|
{
|
||||||
|
if (value != null && value.Length > 8)
|
||||||
|
{
|
||||||
|
if ((GetCommandFlag(value) & 0x40) != 0)
|
||||||
|
{
|
||||||
|
byte[] payload = new byte[value.Length - 8];
|
||||||
|
Array.Copy(value, 8, payload, 0, payload.Length);
|
||||||
|
return payload;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Array.Empty<byte>();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void raise_log(String msg)
|
||||||
|
{
|
||||||
|
if (_event!=null) _event.Log(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void raise_statistic()
|
||||||
|
{
|
||||||
|
if (_event != null) _event.StatisticUpdate(TXOK,RXOK, TXErr,RXErr,TXBytes,RXBytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void raise_connect_status(bool success, String message)
|
||||||
|
{
|
||||||
|
if (_event != null) _event.ConnectStatus(success, message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
19
packages.config
Normal file
19
packages.config
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<packages>
|
||||||
|
<package id="Microsoft.Bcl.AsyncInterfaces" version="9.0.0" targetFramework="net48" />
|
||||||
|
<package id="Microsoft.Data.Sqlite" version="9.0.0" targetFramework="net48" />
|
||||||
|
<package id="Microsoft.Data.Sqlite.Core" version="9.0.0" targetFramework="net48" />
|
||||||
|
<package id="SQLitePCLRaw.bundle_e_sqlite3" version="2.1.10" targetFramework="net48" />
|
||||||
|
<package id="SQLitePCLRaw.core" version="2.1.10" targetFramework="net48" />
|
||||||
|
<package id="SQLitePCLRaw.lib.e_sqlite3" version="2.1.10" targetFramework="net48" />
|
||||||
|
<package id="SQLitePCLRaw.provider.dynamic_cdecl" version="2.1.10" targetFramework="net48" />
|
||||||
|
<package id="System.Buffers" version="4.5.1" targetFramework="net48" />
|
||||||
|
<package id="System.IO.Pipelines" version="9.0.0" targetFramework="net48" />
|
||||||
|
<package id="System.Memory" version="4.5.5" targetFramework="net48" />
|
||||||
|
<package id="System.Numerics.Vectors" version="4.5.0" targetFramework="net48" />
|
||||||
|
<package id="System.Runtime.CompilerServices.Unsafe" version="6.0.0" targetFramework="net48" />
|
||||||
|
<package id="System.Text.Encodings.Web" version="9.0.0" targetFramework="net48" />
|
||||||
|
<package id="System.Text.Json" version="9.0.0" targetFramework="net48" />
|
||||||
|
<package id="System.Threading.Tasks.Extensions" version="4.5.4" targetFramework="net48" />
|
||||||
|
<package id="System.ValueTuple" version="4.5.0" targetFramework="net48" />
|
||||||
|
</packages>
|
||||||
Reference in New Issue
Block a user