#c# #linux #mono #daemon #systemd
#c# #linux #mono #демон #systemd
Вопрос:
У меня есть скрипт на C #, который я модифицировал. Я хочу запустить его при запуске, используя демон и systemctl (оригинал — это скрипт автоматического контроллера вентилятора NZXT). Код создал повторяющийся таймер для выполнения фрагмента кода каждые 4 секунды и блокирует выход из скрипта с помощью Console.ReadLine();
. Я скомпилировал и протестировал скрипт, используя msc и mono. Он работает так, как задумано в терминале (он не выходит напрямую).
Однако при запуске как демон Console.ReadLine();
не ожидает, и скрипт напрямую завершает основной метод и завершает работу.
using System;
using System.IO;
using System.Timers;
using System.Threading.Tasks;
using System.Diagnostics;
using System.Text.RegularExpressions;
public class Program
{
private static string root;
private static string confFile = "/home/mohammadah/scripts/grid v2/conf.d";
private static System.Timers.Timer aTimer;
private static int maxSpeed = 100;
private static int minSpeed = 35;
// do not change this. all fan speeds have to rounded to the nearest multiple of 5
private static int minimumRound = 5;
private static int timerTime = 4000;
private static int defaultFanSpeed = 40;
// fans:
// 1: empty
// 2: empty
// 3: bottom
// 4: top
// 5: CPU
// 6: Back
private static int[] gpuFans;
private static int gpuMaxTemp;
private static int gpuMinTemp;
private static int[] cpuFans;
private static int cpuMaxTemp;
private static int cpuMinTemp;
private static float lastCPUSpeed = 0;
private static float lastGPUSpeed = 0;
public static void Main()
{
// Console.WriteLine(System.IO.Path.GetDirectoryName(Application.ExecutablePath));
ParseConfigFile(confFile);
if(!Regex.IsMatch(DoGridCommand("get fan 1"), @"Fan 1: d{1,5} RPM")){
DoGridCommand("init");
}
DoGridCommand("set fans all speed " defaultFanSpeed);
SetTimer();
Console.WriteLine("nPress the Enter key to exit the application...n");
// Process.GetCurrentProcess().WaitForExit();
Console.ReadLine();
aTimer.Stop();
aTimer.Dispose();
Console.WriteLine("Terminating the application...");
}
private static float GetGPUTemp(){
ProcessStartInfo procStartInfo = new ProcessStartInfo("/bin/bash", "-c nvidia-smi -q -d temperature");
procStartInfo.RedirectStandardOutput = true;
procStartInfo.UseShellExecute = false;
procStartInfo.CreateNoWindow = true;
System.Diagnostics.Process proc = new System.Diagnostics.Process();
proc.StartInfo = procStartInfo;
proc.Start();
string result = proc.StandardOutput.ReadToEnd();
char first = '-';
char second = '-';
string temp = "";
foreach (char c in result)
{
if(c=='1' || c == '2'|| c == '3'|| c == '4'|| c == '5'|| c == '6'|| c == '7'|| c == '8'|| c == '9'|| c == '0'|| c == 'C'){
if(first == '-'){
first = c;
}else if(second == '-'){
second = c;
}else if(c == 'C'){
temp = "" first second;
break;
}
}else{
first = '-';
second = '-';
}
}
return float.Parse(temp);
}
private static int GetCPUTemp(){
string tempLineStart = "Packageid0: ";
ProcessStartInfo procStartInfo = new ProcessStartInfo("/bin/bash", "-c sensors");
procStartInfo.RedirectStandardOutput = true;
procStartInfo.UseShellExecute = false;
procStartInfo.CreateNoWindow = true;
System.Diagnostics.Process proc = new System.Diagnostics.Process();
proc.StartInfo = procStartInfo;
proc.Start();
int temp = 0;
string line = "";
while ((line = proc.StandardOutput.ReadLine()) != null) {
line = Regex.Replace(line, @"s ", "");
if(Regex.IsMatch(line, tempLineStart)){
line = line.Trim().Replace(tempLineStart.Trim(), "");
line = line.Substring(0, 6);
line = Regex.Replace(line, "[^0-9.]", "");
float t = float.Parse(line);
temp = (int)t;
break;
}
}
return temp;
}
private static void SetTimer()
{
// Create a timer with a two second interval.
aTimer = new System.Timers.Timer(timerTime);
// Hook up the Elapsed event for the timer.
aTimer.Elapsed = OnTimedEvent;
aTimer.AutoReset = true;
aTimer.Enabled = true;
}
private static void ParseConfigFile(string fileName){
string gridfanLocation = "GridfanLocation:";
string gpuFanLineStart = "GPUFans:";
string gpuMaxLineStart = "GPUMaxTemp:";
string gpuMinLineStart = "GPUMinTemp:";
string cpuFanLineStart = "CPUFans:";
string cpuMinLineStart = "CPUMinTemp:";
string cpuMaxLineStart = "CPUMaxTemp:";
StreamReader reader = File.OpenText(fileName);
string line;
while ((line = reader.ReadLine()) != null) {
line = line.Trim();
// Regex.Replace(line, @"s ", "");
// ignore commented out (starts with "//") or empty lines
if(line.StartsWith("//") || line.Equals("")) continue;
// set gpu temp at which gpu fans will be set at 100% speed
else if(Regex.IsMatch(line, gridfanLocation)){
root = line.Trim().Replace(gridfanLocation, "");
// set gpu fan list using the numbering found on gridfan git repo
}else if(Regex.IsMatch(line, gpuFanLineStart)){
line = line.Trim().Replace(gpuFanLineStart, "");
string[] fans = line.Split(",");
gpuFans = new int[fans.Length];
for (int i =0;i<fans.Length;i ){
gpuFans[i] = int.Parse(fans[i].Trim());
}
// set gpu temp at which gpu fans will be set at 100% speed
} else if(Regex.IsMatch(line, gpuMaxLineStart)){
line = line.Trim().Replace(gpuMaxLineStart, "");
gpuMaxTemp = int.Parse(line);
// set gpu temp at which gpu fans will be set at lowest speed
} else if(Regex.IsMatch(line, gpuMinLineStart)){
line = line.Trim().Replace(gpuMinLineStart, "");
gpuMinTemp = int.Parse(line);
// set cpu fan list using the numbering found on gridfan git repo
} else if(Regex.IsMatch(line, cpuFanLineStart)){
line = line.Trim().Replace(cpuFanLineStart, "");
string[] fans = line.Split(",");
cpuFans = new int[fans.Length];
for (int i =0;i<fans.Length;i ){
cpuFans[i] = int.Parse(fans[i].Trim());
}
// set cpu temp at which gpu fans will be set at 100% speed
} else if(Regex.IsMatch(line, cpuMaxLineStart)){
line = line.Trim().Replace(cpuMaxLineStart, "");
cpuMaxTemp = int.Parse(line);
// set cpu temp at which gpu fans will be set at lowest speed
} else if(Regex.IsMatch(line, cpuMinLineStart)){
line = line.Trim().Replace(cpuMinLineStart, "");
cpuMinTemp = int.Parse(line);
}
else throw new Exception ("Something went wrong while parsing config file "./conf.d"");
}
}
private static void OnTimedEvent(Object source, ElapsedEventArgs e)
{
Console.WriteLine("====================================================");
float gpuTemp = GetGPUTemp();
Console.WriteLine("Graphics (GPU) temp: " gpuTemp "`C");
float gpuPercentage = 100 * ((gpuTemp - gpuMinTemp) / (gpuMaxTemp - gpuMinTemp));
Console.WriteLine("Graphics (GPU) percentage: " gpuPercentage "`%");
float gpuFanSpeed = gpuPercentage > maxSpeed ? maxSpeed : gpuPercentage < minSpeed ? minSpeed : gpuPercentage;
gpuFanSpeed = (int) Math.Ceiling(gpuFanSpeed / minimumRound) * minimumRound;
if(lastGPUSpeed != gpuFanSpeed){
if(!Regex.IsMatch(DoGridCommand("get fan 1"), @"Fan 1: d{1,5} RPM")){
DoGridCommand("init");
}
foreach (int gpuFan in gpuFans){
DoGridCommand("set fans " gpuFan " speed " gpuFanSpeed);
}
}
float cpuTemp = GetCPUTemp();
Console.WriteLine("Central (CPU) temp: " cpuTemp "`C");
float cpuPercentage = 100 * ((cpuTemp - cpuMinTemp) / (cpuMaxTemp - cpuMinTemp));
Console.WriteLine("Central (CPU) percentage: " cpuPercentage "`%");
Console.WriteLine("");
float cpuFanSpeed = (cpuPercentage > maxSpeed ? maxSpeed : cpuPercentage < minSpeed ? minSpeed : cpuPercentage);
cpuFanSpeed = (int) Math.Ceiling(cpuFanSpeed / minimumRound) * minimumRound;
if(lastCPUSpeed != gpuFanSpeed){
if(!Regex.IsMatch(DoGridCommand("get fan 1"), @"Fan 1: d{1,5} RPM")){
DoGridCommand("init");
}
foreach (int cpuFan in cpuFans){
DoGridCommand("set fans " cpuFan " speed " cpuFanSpeed);
}
}
lastCPUSpeed = cpuFanSpeed;
lastGPUSpeed = gpuFanSpeed;
Console.WriteLine("====================================================");
Console.WriteLine();
}
private static string DoGridCommand(string command){
ProcessStartInfo procStartInfo = new ProcessStartInfo("/bin/bash", root "/gridfan " command);
procStartInfo.RedirectStandardOutput = true;
procStartInfo.UseShellExecute = false;
procStartInfo.CreateNoWindow = true;
System.Diagnostics.Process proc = new System.Diagnostics.Process();
proc.StartInfo = procStartInfo;
proc.Start();
string result = proc.StandardOutput.ReadToEnd();
Console.WriteLine("Command result: " result.Trim());
// System.Threading.Thread.Sleep(500);
return resu<
}
}
conf.d используется в основном коде (не очень важно, но для полноты):
// gridFan repo = https://github.com/CapitalF/gridfan
// root of gridFan script location
GridfanLocation:/home/mohammadah/scripts/grid v2
// GPU fans id/ports as per the schema in gridFan repo readme
GPUFans: 3
// GPU temp at which GPU fans will be set at 100% speed
GPUMaxTemp: 75
// GPU temp at which GPU fans will be set at lowest speed
GPUMinTemp: 35
// GPU fans id/port as per the schema in gridFan repo readme
CPUFans: 4, 5, 6
// CPU temp at which CPU fans will be set at 100% speed
CPUMaxTemp: 75
// CPU temp at which CPU fans will be set at lowest speed
CPUMinTemp: 35
файл daemon .service
[Unit]
Description=Grid v2 automatic fan controller
[Service]
ExecStart=/usr/bin/mono /home/mohammadah/scripts/grid v2/LinuxGrid-v2Controller.exe
Restart=on-failure
[Install]
WantedBy=multi-user.target
Комментарии:
1. Не уверен здесь, никогда не запускал программу C # как демон Linux, но может ли быть так
Console.ReadLine
, что при запуске в качестве демона она недоступна? В этом случае вам понадобится другая стратегия для «ожидания» некоторого сообщения от ОС / пользователя2. Службы предназначены для запуска без взаимодействия с пользователем, как и демоны. В значительной степени идентичный службе Windows, вам следует рассмотреть возможность адаптации вашей программы, чтобы избежать
Console
команд. Я бы предложил и бесконечный циклwhile(true) { }
.3. Несвязанное примечание: я бы предложил изменить ваш
c=='1' || c == '2'|| c == '3'...
с помощью Char . IsNumber() . Более короткий код и более простой для чтения / понимания