Add project files.

This commit is contained in:
2024-11-21 07:47:06 +07:00
parent f4a14908e1
commit c7f485ef1d
16 changed files with 3659 additions and 0 deletions

9
App.xaml Normal file
View File

@@ -0,0 +1,9 @@
<Application x:Class="FAtoPA.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:FAtoPA"
StartupUri="MainWindow.xaml">
<Application.Resources>
</Application.Resources>
</Application>

14
App.xaml.cs Normal file
View File

@@ -0,0 +1,14 @@
using System.Configuration;
using System.Data;
using System.Windows;
namespace FAtoPA
{
/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App : Application
{
}
}

10
AssemblyInfo.cs Normal file
View File

@@ -0,0 +1,10 @@
using System.Windows;
[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)
)]

111
Config.cs Normal file
View File

@@ -0,0 +1,111 @@
using System;
using System.Collections.Generic;
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; } = 1;
public byte FSM_NetNode { get; set; } = 1;
public byte FSM_PNA { get; set; } = 1;
public String FSM_LocalIP { get; set; } = "0.0.0.0";
public bool FSM_UseMulticast { get; set; } = true;
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;
public Config() {
String _configpath = GetConfigPath();
if (File.Exists(_configpath)) {
// Load Config
if (Load())
{
// do nothing
} else
{
// save default config
Save();
}
} else
{
// save default config
Save();
}
}
public bool Save()
{
String _configPath = GetConfigPath();
try
{
String jsonstring = JsonSerializer.Serialize(this);
File.WriteAllText(_configPath, jsonstring);
Console.WriteLine("Config saved to [" + _configPath+"]");
return true;
}
catch (Exception ex)
{
Console.Out.WriteLine("Config Save to ["+_configPath+"] Error: " + ex.Message);
}
return false;
}
public bool Load()
{
String _configPath = GetConfigPath();
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;
Console.Out.WriteLine("Config Loaded from [" + _configPath + "]");
return true;
} else Console.Out.WriteLine("Config Load Error: File ["+ _configPath + "] is not valid Config.");
} catch(Exception ex)
{
Console.Out.WriteLine("Config Load from ["+_configPath+"] Error: " + ex.Message);
}
}
else Console.Out.WriteLine("Config Load Error: File ["+ _configPath + "] not found.");
return false;
}
private static String GetConfigPath()
{
String _configPath = System.IO.Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "FAtoPA");
_configPath = System.IO.Path.Combine(_configPath, "config.json");
return _configPath;
}
}
}

475
Database.cs Normal file
View 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
View 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);
}
}

56
FAtoPA.csproj Normal file
View File

@@ -0,0 +1,56 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net8.0-windows10.0.26100.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<UseWPF>true</UseWPF>
<Platforms>AnyCPU;x64</Platforms>
</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="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="StandardCIL">
<HintPath>..\..\..\Bosch FA\FSM5000FSI-2.0.21\Release_x64\StandardCIL.dll</HintPath>
</Reference>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Extended.Wpf.Toolkit" Version="4.6.1" />
<PackageReference Include="Microsoft.Data.Sqlite" Version="8.0.8" />
</ItemGroup>
</Project>

31
FAtoPA.sln Normal file
View File

@@ -0,0 +1,31 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.10.35027.167
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FAtoPA", "FAtoPA.csproj", "{28C460C4-E551-4653-AB20-99A9D1A1FFCB}"
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
{28C460C4-E551-4653-AB20-99A9D1A1FFCB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{28C460C4-E551-4653-AB20-99A9D1A1FFCB}.Debug|Any CPU.Build.0 = Debug|Any CPU
{28C460C4-E551-4653-AB20-99A9D1A1FFCB}.Debug|x64.ActiveCfg = Debug|x64
{28C460C4-E551-4653-AB20-99A9D1A1FFCB}.Debug|x64.Build.0 = Debug|x64
{28C460C4-E551-4653-AB20-99A9D1A1FFCB}.Release|Any CPU.ActiveCfg = Release|Any CPU
{28C460C4-E551-4653-AB20-99A9D1A1FFCB}.Release|Any CPU.Build.0 = Release|Any CPU
{28C460C4-E551-4653-AB20-99A9D1A1FFCB}.Release|x64.ActiveCfg = Release|x64
{28C460C4-E551-4653-AB20-99A9D1A1FFCB}.Release|x64.Build.0 = Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {F6A7288A-9F10-4AB3-8A26-9B933C1AF4EB}
EndGlobalSection
EndGlobal

280
FSM.cs Normal file
View File

@@ -0,0 +1,280 @@
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;
controller = FSIController.GetInstance();
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";
}
/// <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)
{
byte[] defaultip = new byte[] { 239, 192, 0, 1 };
ushort defaultport = 25000;
AdvancedFSIConfig advancedFSIConfig = new AdvancedFSIConfig();
advancedFSIConfig.IsIPMulticast = useMulticast;
if (useMulticast)
{
if (MulticastIP != null && MulticastIP.Length == 4)
advancedFSIConfig.MulticastAddress = MulticastIP;
else
advancedFSIConfig.MulticastAddress = defaultip;
if (PortNumber>1 && PortNumber<65535)
advancedFSIConfig.PortNumber = PortNumber;
else
advancedFSIConfig.PortNumber = defaultport;
}
advancedFSIConfig.NetstackTraceLevel = SourceLevels.Error;
if (config!=null) config.AdvancedFSIConfig = advancedFSIConfig;
}
/// <summary>
/// Start FSM Monitoring
/// </summary>
public void Start()
{
// 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);
Console.WriteLine("[FSM] Start result = "+result.ToString());
}
public void AddListener(FSMResultInterface listener)
{
if (listenerlist != null) listenerlist.Add(listener);
}
/// <summary>
/// Stop FSM Monitoring
/// </summary>
public void Stop()
{
if (controller != null)
{
try
{
//TODO : somehow bikin crash terus
//Result result = controller.Shutdown();
//Console.WriteLine("[FSM] Shutdown result = " + result.ToString());
} catch (Exception e)
{
Console.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)
{
if (type_dictionary != null)
{
int type = int.Parse(devItemType.FunctionalType.ToString());
if (!type_dictionary.ContainsKey(type)) type_dictionary.Add(type, devItemType.Description);
}
Console.Out.WriteLine("Item type " + devItemType.FunctionalType + " (" + 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)
{
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);
}
}
Console.Out.WriteLine("Item (" + devItem.SIID + ", " + devItem.FunctionalType + " (" + devItem.Label + ")");
}
/// <summary>
/// Akan muncul kalau suatu node hilang dari system
/// </summary>
/// <param name="address"></param>
public void RemoveItem(SIID address)
{
if (node_data != null)
{
String SIID = address.ToString();
if (node_data.ContainsKey(SIID)) node_data.Remove(SIID);
}
Console.Out.WriteLine("SI " + address + " removed.");
}
}
/// <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)
{
if (node_data != null)
{
String SIID = devItemState.SIID.ToString();
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)
{
Console.WriteLine("SIID="+SIID+" Change LogicalState from "+prev.LogicalState+" to "+nd.State.LogicalState);
} else
{
Console.Out.WriteLine("New State for SIID=" + SIID + " LogicalState=" + nd.State.LogicalState);
}
}
}
//Console.Out.WriteLine("State " + devItemState.SIID + " = " + devItemState.LogicalState);
}
/// <summary>
/// Akan muncul sekali di awal mula, sebagai informasi waktu di panel system
/// </summary>
/// <param name="mpNetTime"></param>
public void SetMPNetTime(DateTime mpNetTime)
{
Console.Out.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) Console.Out.Write(message);
}
public override void WriteLine(string? message)
{
if (message != null && message.Length > 0) Console.Out.WriteLine(message);
}
}
}

14
FSMResultInterface.cs Normal file
View 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);
}
}

280
MainWindow.xaml Normal file
View File

@@ -0,0 +1,280 @@
<Window x:Class="FAtoPA.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"
mc:Ignorable="d"
Loaded="Window_Loaded"
Closing="Window_Closing"
Title="Fire Alarm to Modbus and TOA VX3000" Height="600" Width="1024">
<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>

1392
MainWindow.xaml.cs Normal file

File diff suppressed because it is too large Load Diff

26
MappingData.cs Normal file
View 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; }
}
}

544
ModbusSlave.cs Normal file
View File

@@ -0,0 +1,544 @@
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.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
{
public void Log(String msg);
public void NewConnection(ModbusClientRecord client);
public void CloseConnection(ModbusClientRecord client);
public void ConnectStatus(bool success, string message);
public void TXRXStatusUpdate(ModbusClientRecord client);
}
}

41
NodeData.cs Normal file
View 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();
}
}
}
}

361
VX3K.cs Normal file
View 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);
}
}
}