I am trying to create an application that pings the devices configured by the user and reflects the status online or offline. A user can specify IP and Ping Interval for a device and based on that configuration the application needs to ping the device. For example, if the user specifies IP 198.67.227.143 and Ping Interval as 5 seconds, then the application will ping 198.67.227.143 every 5 seconds and reflect the status of whether the device is online or offline.
There can be multiple devices with different ping intervals configured. For example, Device A has a ping interval of 10 seconds and Device B has a ping interval of 20 seconds.
I want to know what is the best way to check whether a device should be pinged or not. Currently, the solution I can come up with is setting up a process that runs every second and loops through all the configured devices and checks if the device should be pinged or not based on the LastPingTime of the device.
Is there a better way of approaching this problem? I am trying to accomplish this in C# and .NET 4.6.2.
One option you can try to have different timers for reach devices, you don't need to ping every second for each device.
I have done simple basic implantation for this, you can use this concept in your code.
//each device representation
public class Device
{
public string IpAddress { get; set; }
public int PingInterval { get; set; }
public Timer PingTimer { get; set; }
public bool IsOnline { get; set; }
}
public class DeviceManager
{
private List<Device> _devices;
public DeviceManager()
{
_devices = new List<Device>();
}
public void AddDevice(string ipAddress, int pingInterval)
{
var device = new Device
{
IpAddress = ipAddress,
PingInterval = pingInterval
};
device.PingTimer = new Timer(OnPingTimerElapsed, device, TimeSpan.Zero, TimeSpan.FromSeconds(pingInterval));
_devices.Add(device);
}
private void OnPingTimerElapsed(object state)
{
//you can log the device state at this place
var device = (Device)state;
// ping and update the status of the device
device.IsOnline = SendPing(device.IpAddress);
}
private bool SendPing(string ipAddress)
{
//ping logic implementation;
}
}
you can call like this.
DeviceManager deviceManager = new DeviceManager();
deviceManager.AddDevice("10.220.63.36", 5);
deviceManager.AddDevice("10.220.63.37", 10);
Related
I am currently working on a blazor server project which will display information from modbus tcp/ip devices. I have a class called "DeviceModel" which models a Modbus device. A simplified example is shown below.
public string DeviceName {get;set;}
public string IpAddress {get;set;}
public string Port {get;set;}
public int[] Registers {get;set;}
public string Alarm1 {get;set;}
The device model class also contains methods to parse information from the Registers. For example the snippet below will check the value at a certain index in the Registers array. Based on that value it will set the Alarm1 property to ON or OFF.
public void CheckAlarm1(){
int status = Registers[4];
Alarm1 = status == 1 ? "ON" : "OFF";
}
I have another class called "NetworkAccess" which handles the TCP/IP connection to a device. A simplified example is shown below
// ModbusClient is a package which handles the reading/writing to TCP/IP Modbus
private ModbusClient _client;
public string IPAddress {get;set;}
public string Port {get;set;}
public DeviceModel Device {get;set;}
public NetworkAccess(DeviceModel dev){
IPAddress = dev.IPAddress;
Port = dev.Port;
_client = new ModbusClient(IPAddress,Port);
_client.Connect();
}
The NetworkAccess class handles reading and writing data to/from the device on the network. An example method which would write data to a single register on the Modbus device is below.
public void WriteSingleRegister(int address,int dataToAdd){
_client.WriteSingleRegister(address,dataToAdd);
}
Within my Razor Component for the webpage, within the OnInitialized() method I get a List containing DeviceModels from a database which fills in information such as IPAddress,Port, and Name for each device. To read information to the device, I have another method "GetData()" shown below
public async void GetData(){
foreach(var device in Devices){
NetworkAccess network = new NetworkAccess(dev);
var dataUpdate = await network.ReadRegistersAsync(0,20);
dev.Registers= dataUpdate;
}
}
The way I currently have this setup works fine. In order to write to a device I would do something like this in my Razor Component
NetworkAccess network = new NetworkAccess(dev);
network.WriteRegistersAsync(0,new int[] {0,0,0,...}};
Where I am having trouble is I am not sure the of the correct (or best) way to handle my situation. In my head it makes more sense to me if I had methods within my DeviceModel class for specific operations such as "ResetAlarm1" or "ClearRegisters". That way I could do
dev.ResetAlarm1();
rather than doing this in my razor component below
NetworkAccess net = new NetworkAccess(dev)
dev.WriteRegister(6,0); // where 6 is the register to write to and 0 is value to write
I guess my question is should I add "NetworkAccess" to the device model and handle creating the connection and reading/writing to the device within that? Or does it make more sense to keep NetworkAccess and DeviceModel seperate?
I hope this post makes sense. This is more a question about design than it is about fixing a problem. While my current solution is working fine, I want to better understand if this is the correct approach or if I am way off.
Thanks for any help!
Or does it make more sense to keep NetworkAccess and DeviceModel separate?
As single responsibility principle of SOLID says:
The single-responsibility principle (SRP) is a computer-programming
principle that states that every module, class or function in a
computer program should have responsibility over a single part of that
program's functionality, and it should encapsulate that part. All of
that module, class or function's services should be narrowly aligned
with that responsibility.
Read more about single responsibility principle of SOLID here.
So making separate method dev.ResetAlarm1() in Device class is more preferable for me.
It is hard to say whether my refactoring code is appropriate to you, but I tried to do my best:
public class Device
{
public string Name { get; set; }
public string IpAddress { get; set; }
public string Port { get; set; }
public int[] Registers { get; set; }
public string[] Alarms { get; set; }
public void CheckAlarm(int registerIndex)
{
int status = Registers[registerIndex];
Alarms[registerIndex] = status == 1 ? "ON" : "OFF";
}
}
I am in a situation where "I don't know what I don't know" so I am unsure if this is even the correct way of approaching this problem, apologies if this comes off as plain ignorant.
I have a program which connects to Ethernet controllers. The program allows users to configure what is connected to the system and set up I/O communication.
Each controller is its own device, and may have different IO depending on what model it is. Controllers have their own API.
The program saves the configuration to an XML config file which is read at startup. I need to then connect to each unknown device and set up a connection to each, leaving me with a method of referring to each device at a later time.
Here is what I am trying to achieve:
using Brainboxes.IO;
public class BrainBoxes
{
public string[] Devices = new string[] { "192.168.16.147", "192.168.16.148", "192.168.16.149", "192.168.16.150" };
List<string> EDDeviceList = new List<string>();
public BrainBoxes() // set up devices and connections to all devices connected in the constructor
{
foreach (string Device in Devices)
{
EDDevice BB400 = EDDevice.Create("192.168.16.147");
// BB400 is a typical name but how do I make this dynamic at the same time making it
// available for other members of the class?
EDDeviceList.Add(BB400); // add the device to a list to refer to later in the constructor
}
for (int i = 0; i < EDDeviceList.Count - 1; i++) { BB400.Connect()}; // connect to each device in sequence.
}
public void Outputs(int Relay)
{
// this would be a switch statement
BB400.Outputs[Relay].Value = 1;
Thread.Sleep(75);
BB400.Outputs[Relay].Value = 0;
}
~BrainBoxes()
{
BB400.Disconnect();
}
}
It sounds like you're trying to do quite a few things at once. To paraphrase what you want: to achieve (looking at both your question, your sample code and your comment)
When your application starts, you want it to connect to a collection of different devices automatically
When running, users can connect to and configure (the right) device
Ensure that connections are closed when the application stops
Also your question is rather open ended and from your first statement, I'm going to assume that you're a beginner. I know that it's quite dry, but you are going to have to look up the documentation for the hardware you're using. Luckily, it looks quite comprehensive
You need to give your class a more representative name. E.g. BrainboxController or BrainboxManager as, by the sounds of it, that is what it's for.
Looks like BB400 is one of the possible hardware devices, it is part of an inheritance hierarchy, so you don't want to restrict yourself to just that
I would avoid doing a lot of work in the constructor, it makes it harder to find problems
Use a dictionary to store your devices, that's how you'll "refer to each device at a later time"
public class BrainboxController : IDisposable
{
private readonly HashSet<string> _deviceIps; // potentially you can get away without having this if you call InitialiseDevices() in the constructor
private Dictionary<string, EDDevice> _devices = new Dictionary<string, EDDevice>(); // possibly use IDevice<C, P> instead of EDDevice
public BrainboxController(IEnumerable<string> devices)
{
_deviceIps = new HashSet<string>(devices);
}
public void InitialiseDevices()
{
foreach (string ip in _deviceIps)
_devices.Add(ip, EDDevice.Create(ip));
}
public void AddDevice(string ip)
{
if (_deviceIps.Add(ip))
_devices.Add(ip, EDDevice.Create(ip));
}
public void RemoveDevice(string ip)
{
if(_devices.ContainsKey(ip))
{
var device = _devices[ip];
device.Disconnect();
device.Dispose();
_devices.Remove(ip);
_deviceIps.Remove(ip);
}
}
public EDDevice GetDevice(string deviceIp)
{
if (_devices.ContainsKey(deviceIp))
return _devices[deviceIp];
return null;
}
public string GetConfiguration(string deviceIp)
{
if (_devices.ContainsKey(deviceIp))
return _devices[deviceIp].Describe(); // I'm assuming that this gets the config data
return "Device not found";
}
public bool SetConfiguration(string deviceIp, string xml)
{
if (_devices.ContainsKey(deviceIp))
{
_devices[deviceIp].SendCommand(xml); // I'm assuming this is how the config data is set
return true;
}
// log device not found
return false;
}
public IOList<IOLine> GetOutputs(string deviceIp, int relay)
{
if (_devices.ContainsKey(deviceIp))
return _devices[deviceIp].Outputs[relay];
// log device not found
return new IOList<IOLine>();
}
public void Dispose()
{
foreach(var device in _devices.Values)
{
device.Disconnect();
device.Dispose();
}
}
}
Strictly speaking, if you follow the single responsibility principle, this class should just be managing your devices and their connections. The methods GetConfiguration(), SetConfiguration() and GetOutputs() are shown as examples and really should live somewhere else.
Your calling code could be look like this (without dependency injection):
var deviceAddresses = new[] { "192.168.16.147", "192.168.16.148", "192.168.16.149", "192.168.16.150" };
var controller = new BrainboxController(deviceAddresses);
controller.InitialiseDevices();
var currentDevice = controller.GetDevice("192.168.16.147");
// do something with currentDevice
Finally, whatever it is you're trying to do with your Outputs method, that looks like business logic and this also should live somewhere else.
I'm setting up a new client-server-network with SuperSocket and can't connenct an AppSession to the server.
I found this question and tried it for my program.
The server is running fine, but at the moment I can only connect with the 'AsynTcpSession'. When I check the connected sessions it is shown as an 'AppSession' at the server. I want to use 'AppSession', because you can give them custom parameter.
My created AppSession:
public class MyAppSession : AppSession<MyAppSession, MyRequestInfo>
{
// those parameter
public int ClientKey { get; set; }
public string HashKey { get; set; }
}
Server:
MyAppServer _server = new MyAppServer();
ServerConfig _socketServerConfig = new ServerConfig { ReceiveBufferSize = 5000, MaxRequestLength = 5000, Name = "Test- Server", SendingQueueSize = 5000, ServerType = "MyAppServer", Port = 6000};
if (_server.Setup(_socketServerConfig))
{
DoStuff();
}
Client:
ClientSession _gateway = new AsyncTcpSession();
_gateway.Connect(6000);
On receiving telegram from Client:
private void ReceivedDataFromClient(MyAppSession session, MyRequestInfo requestinfo)
{
// session.SocketSession contains the Client AsynTcpSession
}
EDIT:
The AppSession has an void Initialize(IAppServer<TAppSession, TRequestInfo> appServer, ISocketSession socketSession)-Function. How do I use it? The session only knows the server ip and port.
AsynTcpSession and AppSession are different things, althought they are all called 'session'.
Any client connection packages / classes have no matter with AppSession. (eg. AsynTcpSession)
The AppSession just a temporary storage of client connection for AppServer.
Let the AppServer controlls the client connections, identify each clients, controll the connections pool...etc.
You can define many variables in the AppSession, But to assign values will still by your own codes (and client should send these informations).
I'm developing a multiple players game with photon server and unity, have some questions on how server send messages to clients.
I know that I need a List of clientpeer to save the connecting clients, but I dont know how to save a peer to the List,and which send-message code should I use?
//here is the List that saving the clientpeers
public class ClientCollection
{
public List<PeerBase> clients;
public ClientCollection() { }
public void AddNewClient(PeerBase peer)
//is it right that I send in a PeerBase type argument?
{
clients.Add(peer);
}
public List<PeerBase> GetClients(PeerBase peer)
{
return clients;
}
}
I have table in the database like this:
What is the best way to implement trigger of event somewhere (in SQL server database or c# application) on time of event field in table.
Edit:
Traditionally I would have done something like this:
while(true)
{
DataTable tbl = getRows("select * from table where event=" + DateTime.now());
if(tbl.rows.Count()>0)
{
//do some thing
}
Thread.Sleep(1000);
}
Is there a more efficient way to achieve this?
(I don't want periodically check Database)
I would go like this:
public class Notify
{
public int ID { get; set; }
public string Name { get; set; }
public DateTime Time { get; set; }
}
public Timer tData = new Timer();
public List<Notify> Notifications = new List<Notify>();
public void Main()
{
// 5 minutes
tData.Interval = 1000;
tData.Tick += new EventHandler(CheckEvents);
}
public void GetAllEvents()
{
DataTable results = new DataTable();
/* results = YourDatabase(Select id, name, event FROM...); */
Notifications.Clear();
foreach(DataRow row in results.Rows)
{
Notifications.Add
(
new Notify
{
ID = int.Parse(row[0].ToString()),
Name = row[1].ToString(),
Time = DateTime.Parse(row[2].ToString())
}
);
}
}
public void CheckEvents(object sender, EventArgs e)
{
IEnumerable<Notify> eventsElapsed = Notifications.Where(notify => notify.Time == DateTime.Now);
foreach(Notify notify in eventsElapsed)
{
// Send your sms
var id = notify.ID;
var name = notify.Name;
var time = notify.Time;
}
}
To-Do's:
Care for the correct format when getting the DateTime out of your database. It might look different.
You would have to think about how to get new events. I would do it either by a button-click or by setting up another timer with an interval of something around 5-10 minutes. You can just call GetAllEvents().
I dont think you are inserting new events that will have their time in between the next 30 seconds.
Another more advanced way:
You could also setup 2 apps for this.
First app:
Get all Events from Database
Optionally create an UI that allows to filter which events should be get
Link every event to Scheduled Tasks with start arguments like secondApp.exe "2016-06-26 10:13:56".
Second app:
On startup, fetch the passed arguments and send the SMS.
If your table has lots of data, its not bad to split the time consuming process of hooking up events (1st app) from the simple process of sending out a SMS (2nd app).
Don't mess with triggers at the database for this purpose.
In C# you write the code for your logic - the thing you need to be done. Next designate an object which stores the input parameters + the time to start this logic. E.g. in SMS system we need the subscriber number, the message and the time of sending.
At your system startup (for example) you read the data to obtain the times. Start some threads and make them wait (Sleep) as long as needed, then execute the target method. Something like this:
class SmsDetails
{
public string Subscriber { get; set; }
public string Message { get; set; }
public DateTime SendOn { get; set; }
}
class Program
{
static void Main(string[] args)
{
SqlDataReader schedule = null; // Initialize the reader as appropriate.
while (schedule.Read())
{
var det = new SmsDetails();
//.
//.
//.
det.SendOn = schedule.GetDateTime(2);
ThreadPool.QueueUserWorkItem(ScheduleSending, det);
}
}
static void ScheduleSending(object Details)
{
var smsd = Details as SmsDetails;
if (smsd.SendOn > DateTime.Now)
{
var waitInterval = DateTime.Now - smsd.SendOn;
Thread.Sleep(waitInterval);
SendSms(smsd.Subscriber, smsd.Message);
}
}
static void SendSms(string PhoneNumber, string Message)
{
// Send it out
}
}
Of course this is not the complete code for such a solution but I hope you get the idea. You need to take care to signal (in the DB?) if the message was sent. You can also employ wait handles to interrupt the threads from the calling code.
Depending on the volume of the records in the reader you may want to poll the DB for the message about to be send in the next 1, 6, 12 and so on. E.g. if you need to dispatch 10000 messages over the next 3 days don't schedule them now. Having too many threads can degrade the performance.