2020-10-21 01:13:40 +02:00
#nullable enable
using System ;
using System.Collections.Generic ;
using System.Diagnostics ;
using System.IO ;
using System.IO.Pipes ;
2020-12-02 19:08:46 +01:00
using System.Linq ;
2020-10-21 01:13:40 +02:00
namespace alterNERDtive.util
{
2020-12-02 19:08:46 +01:00
public class Configuration
{
private readonly dynamic VA ;
private readonly string ID ;
private readonly VoiceAttackLog Log ;
2020-12-02 20:28:09 +01:00
private readonly VoiceAttackCommands Commands ;
2020-12-02 19:08:46 +01:00
private static readonly Dictionary < string , OptDict < string , Option > > Defaults = new Dictionary < string , OptDict < string , Option > >
{
{
"alterNERDtive-base" ,
new OptDict < string , Option > {
2021-01-25 12:04:28 +01:00
{ new Option < decimal > ( "delays.keyPressDuration" , ( decimal ) 0.01 , voiceTrigger : "key press duration" , description : "The time keys will be held down for." ) } ,
{ new Option < decimal > ( "delays.quitToDesktop" , ( decimal ) 10.0 , voiceTrigger : "quit to desktop delay" ,
description : "The delay before restarting the game after hitting “Exit to Desktop”.\nDefault: 10.0 seconds. (Used by the `restart from desktop` command)" ) } ,
2021-01-25 01:09:46 +01:00
{ new Option < bool > ( "eddi.quietMode" , true , voiceTrigger : "eddi quiet mode" , description : "Make EDDI shut up. Disables all built-in speech responders." ) } ,
2021-01-16 03:39:40 +01:00
{ new Option < string > ( "elite.pasteKey" , "v" , voiceTrigger : "elite paste key" ,
description : "The key used to paste in conjunction with CTRL. The physical key in your layout that would be 'V' on QWERTY." ) } ,
2021-01-25 12:04:28 +01:00
{ new Option < bool > ( "enableAutoUpdateCheck" , true , voiceTrigger : "auto update check" , description : "Automatically check Github for profiles updates." ) } ,
2021-01-16 03:39:40 +01:00
{ new Option < string > ( "log.logLevel" , "NOTICE" , voiceTrigger : "log level" , validValues : new List < string > { "ERROR" , "WARN" , "NOTICE" , "INFO" , "DEBUG" } ,
2020-12-07 21:45:17 +01:00
description : @"The level of detail for logging to the VoiceAttack log.\nValid levels are ""ERROR"", ""WARN"", ""NOTICE"", ""INFO"" and ""DEBUG"".\nDefault: ""NOTICE""." ) } ,
2020-12-02 19:08:46 +01:00
}
} ,
{
"EliteAttack" ,
new OptDict < string , Option > {
2021-01-16 03:39:40 +01:00
{ new Option < bool > ( "announceEdsmSystemStatus" , true , voiceTrigger : "edsm system status" ,
description : "Pull system data from EDSM and compare it against your discovery scan." ) } ,
{ new Option < bool > ( "announceMappingCandidates" , true , voiceTrigger : "mapping candidates" ,
description : "Announce bodies worth mapping when you’ ve finished scanning a system.\n(Terraformables, Water Worlds, Earth-Like Worlds and Ammonia Worlds that have not been mapped yet.)" ) } ,
{ new Option < bool > ( "announceOutdatedStationData" , true , voiceTrigger : "outdated stations" , description : "Announce stations with outdated data in the online databases." ) } ,
2021-01-25 12:04:28 +01:00
{ new Option < bool > ( "announceR2RMappingCandidates" , false , voiceTrigger : "road to riches" ,
2021-01-16 03:39:40 +01:00
description : "Announce bodies worth scanning if you are looking for some starting cash on the Road to Riches." ) } ,
{ new Option < bool > ( "autoHonkAllSystems" , false , voiceTrigger : "auto honk all systems" , description : "Automatically honk upon entering a system, each jump without constraints." ) } ,
{ new Option < bool > ( "autoHonkNewSystems" , true , voiceTrigger : "auto honk new systems" , description : "Automatically honk upon entering as system if it is your first visit." ) } ,
{ new Option < bool > ( "autoRestock" , true , voiceTrigger : "auto restock" , description :
2021-01-25 12:04:28 +01:00
"Automatically restock after docking at a station.\nYou will always refuel, repair and enter the Station Services menu." ) } ,
2021-01-16 03:39:40 +01:00
{ new Option < bool > ( "flightAssistOff" , false , voiceTrigger : "flight assist off" , description : "Permanent Flight Assist off mode. You should really do that, it’ s great." ) } ,
2021-01-25 12:04:28 +01:00
{ new Option < bool > ( "hyperspaceDethrottle" , true , voiceTrigger : "hyper space dethrottle" ,
description : "Throttle down after a jump and when dropping from SC. Like the SC Assist module does." ) } ,
{ new Option < int > ( "outdatedStationThreshold" , 365 , voiceTrigger : "outdated station threshold" ,
description : "The threshold for station data to count as “outdated”, in days.\nDefault: 365." ) } ,
2021-01-16 03:39:40 +01:00
{ new Option < int > ( "scannerFireGroup" , 0 , voiceTrigger : "scanner fire group" , description : "The fire group your discovery scanner is assigned to.\nDefault: 0 (the first one)." ) } ,
{ new Option < bool > ( "usePrimaryFireForDiscoveryScan" , false , voiceTrigger : "discovery scan on primary fire" , description : "Use primary fire for honking instead of secondary." ) } ,
2020-12-02 19:08:46 +01:00
}
} ,
{
"RatAttack" ,
new OptDict < string , Option > {
2021-01-16 03:39:40 +01:00
{ new Option < bool > ( "autoCloseCase" , false , voiceTrigger : "auto close fuel rat case" , description : "Automatically close a rat case when sending “fuel+” via voice command or ingame chat." ) } ,
{ new Option < bool > ( "announceNearestCMDR" , false , voiceTrigger : "nearest commander to fuel rat case" , description : "Announce the nearest commander to incoming rat cases." ) } ,
2021-01-16 04:30:47 +01:00
{ new Option < bool > ( "announcePlatform" , false , voiceTrigger : "platform for fuel rat case" , description : "Announce the platform for incoming rat cases." ) } ,
2021-01-16 03:39:40 +01:00
{ new Option < string > ( "CMDRs" , "" , voiceTrigger : "fuel rat commanders" ,
description : "All your CMDRs that are ready to take rat cases.\nUse ‘ ;’ as separator, e.g. “Bud Spencer;Terrence Hill”." ) } ,
2021-01-16 04:30:47 +01:00
{ new Option < bool > ( "confirmCalls" , true , voiceTrigger : "fuel rat call confirmation" , description : "Only make calls in #fuelrats after vocal confirmation to prevent mistakes." ) } ,
{ new Option < bool > ( "onDuty" , true , voiceTrigger : "fuel rat duty" , description : "Set FuelRats duty to active when the profile is loaded." ) } ,
2021-01-16 03:39:40 +01:00
{ new Option < string > ( "platforms" , "PC" , voiceTrigger : "fuel rat platforms" , validValues : new List < string > { "PC" , "Xbox" , "Playstation" } ,
description : "The platform(s) you want to get case announcements for (PC, Xbox, Playstation).\nUse ‘ ;’ as separator, e.g. “PC;Xbox”." ) } ,
2020-12-02 19:08:46 +01:00
}
} ,
{
"SpanshAttack" ,
new OptDict < string , Option > {
2021-01-25 12:04:28 +01:00
{ new Option < string > ( "announceJumpsLeft" , ";1;3;5;10;15;20;30;50;75;100;" , voiceTrigger : "announce jumps left" ,
description : "Estimated jumps left to announce when reached.\nNEEDS to have leading and trailing “;”." ) } ,
2021-01-16 03:39:40 +01:00
{ new Option < bool > ( "announceWaypoints" , true , voiceTrigger : "waypoint announcements" , description : "Announce each waypoint by name." ) } ,
2021-01-25 14:34:14 +01:00
{ new Option < bool > ( "autoJumpAfterScooping" , true , voiceTrigger : "auto jump after scooping" , description : "Automatically jump out when fuel scooping is complete." ) } ,
2021-01-25 12:04:28 +01:00
{ new Option < bool > ( "autoPlot" , true , voiceTrigger : "auto plot" , description : "Automatically plot to the next waypoint after supercharging." ) } ,
2021-01-16 03:39:40 +01:00
{ new Option < bool > ( "clearOnShutdown" , true , voiceTrigger : "clear neutron route on shutdown" , description : "Clear an active neutron route when the game is shut down." ) } ,
{ new Option < bool > ( "copyWaypointToClipboard" , false , voiceTrigger : "copy neutron waypoints to clipboard" , description : "Copy each neutron waypoint into the Windows clipboard." ) } ,
{ new Option < bool > ( "defaultToLadenRange" , false , voiceTrigger : "default to laden range" ,
description : "Default to the current ship’ s laden range as reported by EDDI instead of prompting for input." ) } ,
{ new Option < bool > ( "timeTrip" , false , voiceTrigger : "time neutron route" , description : "Keep track of how long a neutron route takes you to complete." ) } ,
2020-12-02 19:08:46 +01:00
}
} ,
{
"StreamAttack" ,
new OptDict < string , Option > {
2021-01-16 03:39:40 +01:00
{ new Option < string > ( "outputDir" , @"%appdata%\StreamAttack\" , voiceTrigger : "StreamAttack output directory" , description : "The directory the status files are written to." ) }
2020-12-02 19:08:46 +01:00
}
}
} ;
2020-12-08 10:54:39 +01:00
public abstract class Option
2020-12-02 19:08:46 +01:00
{
2020-12-08 10:54:39 +01:00
#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
public string Name { get ; }
public dynamic DefaultValue { get ; }
public List < dynamic > ? ValidValues { get ; }
public string VoiceTrigger { get ; }
public string TtsDescription { get ; }
public string Description { get ; }
public Type Type { get ; }
public string? TypeString { get ; }
public override string ToString ( ) = > DefaultValue ! . ToString ( ) ;
#pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
}
public class Option < T > : Option
{
public new string Name { get ; }
public new T DefaultValue { get ; }
public new List < T > ? ValidValues { get ; }
public new string VoiceTrigger { get ; }
public new string TtsDescription { get = > ttsDescription ? ? VoiceTrigger ; }
2020-12-02 19:08:46 +01:00
private readonly string? ttsDescription ;
2020-12-08 10:54:39 +01:00
public new string Description { get = > description ? ? "No description available." ; }
private readonly string? description ;
2020-12-02 19:08:46 +01:00
2020-12-08 10:54:39 +01:00
public new Type Type { get = > typeof ( T ) ; }
public new string? TypeString
{
get
{
string? type = null ;
if ( typeof ( T ) = = typeof ( bool ) )
{
type = "boolean" ;
}
else if ( typeof ( T ) = = typeof ( DateTime ) )
{
type = "date" ;
}
else if ( typeof ( T ) = = typeof ( decimal ) )
{
type = "decimal" ;
}
else if ( typeof ( T ) = = typeof ( int ) )
{
type = "int" ;
}
else if ( typeof ( T ) = = typeof ( short ) )
{
type = "smallint" ;
}
else if ( typeof ( T ) = = typeof ( string ) )
{
type = "text" ;
}
return type ;
}
}
public Option ( string name , T defaultValue , string voiceTrigger , List < T > ? validValues = null , string? ttsDescription = null , string? description = null )
2020-12-07 21:45:17 +01:00
= > ( Name , DefaultValue , VoiceTrigger , ValidValues , this . ttsDescription , this . description ) = ( name , defaultValue , voiceTrigger , validValues , ttsDescription , description ) ;
2020-12-02 19:08:46 +01:00
2020-12-08 10:54:39 +01:00
public static implicit operator ( string , Option ) ( Option < T > o ) = > ( o . Name , o ) ;
public static explicit operator T ( Option < T > o ) = > o . DefaultValue ;
2020-12-02 19:08:46 +01:00
}
private class OptDict < TKey , TValue > : Dictionary < TKey , TValue >
{
public OptDict ( ) : base ( ) { }
public OptDict ( int capacity ) : base ( capacity ) { }
public void Add ( ( TKey , TValue ) tuple )
{
base . Add ( tuple . Item1 , tuple . Item2 ) ;
}
}
2020-12-02 20:28:09 +01:00
public Configuration ( dynamic vaProxy , VoiceAttackLog log , VoiceAttackCommands commands , string id ) = > ( VA , Log , Commands , ID ) = ( vaProxy , log , commands , id ) ;
2020-12-02 19:08:46 +01:00
public dynamic GetDefault ( string name )
{
return GetDefault ( ID , name ) ;
}
public static dynamic GetDefault ( string id , string name )
{
return Defaults [ id ] [ name ] ;
}
2020-12-07 21:45:17 +01:00
public Option GetOption ( string name )
{
return GetOption ( ID , name ) ;
}
public Option GetOption ( string id , string name )
{
return Defaults [ id ] [ name ] ;
}
2020-12-02 19:08:46 +01:00
public void SetVoiceTriggers ( System . Type type )
{
List < string > triggers = new List < string > ( ) ;
foreach ( Dictionary < string , Option > options in Defaults . Values )
{
2020-12-08 10:54:39 +01:00
foreach ( dynamic option in options . Values )
2020-12-02 19:08:46 +01:00
{
if ( option . DefaultValue . GetType ( ) = = type )
{
if ( triggers . Contains ( option . VoiceTrigger ) )
{
throw new ArgumentException ( $"Voice trigger '{option.VoiceTrigger}' is not unique, aborting …" ) ;
}
triggers . Add ( option . VoiceTrigger ) ;
}
}
}
if ( triggers . Count > 0 )
{
string triggerString = string . Join ( ";" , triggers ) ;
VA . SetText ( $"alterNERDtive-base.triggers.{type.Name}" , triggerString ) ;
Log . Debug ( $"Voice triggers for {type.Name}: '{triggerString}'" ) ;
}
else
{
// make sure we don’ t accidentally have weird things happening with empty config voice triggers
string triggerString = $"tenuiadafesslejüsljlejutlesuivle{type.Name}" ;
VA . SetText ( $"alterNERDtive-base.triggers.{type.Name}" , triggerString ) ;
Log . Debug ( $"No voice triggers found for {type.Name}" ) ;
}
}
public void SetVariablesForTrigger ( dynamic vaProxy , string trigger )
{
_ = trigger ? ? throw new ArgumentNullException ( "trigger" ) ;
foreach ( KeyValuePair < string , OptDict < string , Option > > options in Defaults )
{
try
{
2020-12-08 10:54:39 +01:00
dynamic option = options . Value . First ( item = > ( ( dynamic ) item . Value ) . VoiceTrigger . ToLower ( ) = = trigger ) . Value ;
2020-12-02 19:08:46 +01:00
vaProxy . SetText ( "~name" , $"{options.Key}.{option.Name}" ) ;
vaProxy . SetText ( "~ttsDescription" , option . TtsDescription ) ;
vaProxy . SetText ( "~description" , option . Description ) ;
break ;
}
catch ( InvalidOperationException ) { }
}
}
public bool HasDefault ( string name )
{
return HasDefault ( ID , name ) ;
}
public static bool HasDefault ( string id , string name )
{
return Defaults [ id ] . ContainsKey ( name ) ;
}
2020-12-02 20:28:09 +01:00
public void LoadFromProfile ( )
{
foreach ( KeyValuePair < string , OptDict < string , Option > > options in Defaults )
{
LoadFromProfile ( options . Key ) ;
}
}
public void LoadFromProfile ( string id )
{
2020-12-08 10:54:39 +01:00
foreach ( dynamic option in Defaults [ id ] . Values )
2020-12-02 20:28:09 +01:00
{
string name = $"{id}.{option.Name}" ;
2020-12-08 10:54:39 +01:00
string type = option . TypeString ? ? throw new InvalidDataException ( $"Invalid data type for option '{name}': '{option}'" ) ;
2020-12-02 20:28:09 +01:00
Log . Debug ( $"Loading value for option '{name}' from profile …" ) ;
2021-01-08 16:46:10 +01:00
Commands . Run ( "alterNERDtive-base.loadVariableFromProfile" , wait : true , parameters : new dynamic [ ] { new string [ ] { $"{name}#" , type } } ) ;
2020-12-02 20:28:09 +01:00
}
}
2020-12-02 19:08:46 +01:00
public dynamic? ApplyDefault ( string name )
{
return ApplyDefault ( ID , name ) ;
}
public dynamic? ApplyDefault ( string id , string name )
{
if ( ! HasDefault ( id , name ) )
{
Log . Warn ( $"No default configuration value found for '{id}.{name}'" ) ;
return null ;
}
2020-12-08 10:54:39 +01:00
dynamic option = Defaults [ id ] [ name ] ;
dynamic value = option . DefaultValue ;
2020-12-02 19:08:46 +01:00
Log . Debug ( $"Loading default configuration value, '{id}.{name}': '{value}' …" ) ;
string variable = $"{id}.{name}#" ;
if ( value is bool )
{
VA . SetBoolean ( variable , value ) ;
}
else if ( value is DateTime )
{
VA . SetDate ( variable , value ) ;
}
else if ( value is decimal )
{
VA . SetDecimal ( variable , value ) ;
}
else if ( value is int )
{
VA . SetInt ( variable , value ) ;
}
else if ( value is short )
{
VA . SetSmallInt ( variable , value ) ;
}
else if ( value is string )
{
VA . SetText ( variable , value ) ;
}
else
{
throw new InvalidDataException ( $"Invalid data type for option '{id}.{name}': '{value}'" ) ;
}
return value ;
}
public void ApplyDefaults ( )
{
ApplyDefaults ( ID ) ;
}
public void ApplyAllDefaults ( )
{
foreach ( string id in Defaults . Keys )
{
ApplyDefaults ( id ) ;
}
}
public void ApplyDefaults ( string id )
{
foreach ( string key in Defaults [ id ] . Keys )
{
ApplyDefault ( id , key ) ;
}
}
2020-12-07 16:29:07 +01:00
public void DumpConfig ( )
{
foreach ( string id in Defaults . Keys )
{
DumpConfig ( id ) ;
}
}
public void DumpConfig ( string id )
{
Log . Notice ( $"===== {id} configuration: =====" ) ;
foreach ( string name in Defaults [ id ] . Keys )
{
DumpConfig ( id , name ) ;
}
}
public void DumpConfig ( string id , string name )
{
2020-12-08 10:54:39 +01:00
dynamic option = Defaults [ id ] [ name ] ;
2020-12-07 16:29:07 +01:00
dynamic defaultValue = option . DefaultValue ;
2020-12-08 10:54:39 +01:00
string variable = $"{id}.{option.Name}#" ;
2020-12-07 16:29:07 +01:00
dynamic value ;
if ( defaultValue is bool )
{
value = VA . GetBoolean ( variable ) ;
}
else if ( defaultValue is DateTime )
{
value = VA . GetDate ( variable ) ;
}
else if ( defaultValue is decimal )
{
value = VA . GetDecimal ( variable ) ;
}
else if ( defaultValue is int )
{
value = VA . GetInt ( variable ) ;
}
else if ( defaultValue is short )
{
value = VA . GetSmallInt ( variable ) ;
}
else if ( defaultValue is string )
{
value = VA . GetText ( variable ) ;
}
else
{
throw new InvalidDataException ( $"Invalid data type for option '{id}.{name}': '{defaultValue}'" ) ;
}
2021-01-16 03:56:58 +01:00
Log . Notice ( $"{variable} = {value}{(value == defaultValue ? " ( default ) " : " ")}" ) ;
}
public void ListConfig ( )
{
foreach ( string id in Defaults . Keys )
{
ListConfig ( id ) ;
}
}
public void ListConfig ( string id )
{
Log . Notice ( $"===== {id} settings: =====" ) ;
foreach ( string name in Defaults [ id ] . Keys )
{
ListConfig ( id , name ) ;
}
}
public void ListConfig ( string id , string name )
{
dynamic option = Defaults [ id ] [ name ] ;
Log . Notice ( $"“{option.VoiceTrigger}”: {option.Description}" ) ;
2020-12-07 16:29:07 +01:00
}
2020-12-02 19:08:46 +01:00
}
2020-10-21 01:13:40 +02:00
public class PythonProxy
{
public static Process SetupPythonScript ( string path , string arguments )
{
Process p = new Process ( ) ;
p . StartInfo . FileName = path ;
p . StartInfo . Arguments = arguments ;
p . StartInfo . WindowStyle = ProcessWindowStyle . Hidden ;
p . StartInfo . RedirectStandardOutput = true ;
2020-10-24 16:28:24 +02:00
p . StartInfo . RedirectStandardError = true ;
2020-10-21 01:13:40 +02:00
p . StartInfo . UseShellExecute = false ;
p . StartInfo . CreateNoWindow = true ;
return p ;
}
}
public enum LogLevel
{
ERROR ,
WARN ,
NOTICE ,
INFO ,
DEBUG
}
public class VoiceAttackCommands
{
private readonly dynamic VA ;
private readonly VoiceAttackLog Log ;
public VoiceAttackCommands ( dynamic vaProxy , VoiceAttackLog log ) = > ( VA , Log ) = ( vaProxy , log ) ;
public void Run ( string command , bool logMissing = true , bool wait = false , bool subcommand = false , Action < Guid ? > ? callback = null , dynamic [ ] ? parameters = null )
{
_ = command ? ? throw new ArgumentNullException ( "command" ) ;
if ( VA . Command . Exists ( command ) )
{
Log . Debug ( $"Parsing arguments for command '{command}' …" ) ;
string [ ] ? strings = null ;
int [ ] ? integers = null ;
decimal [ ] ? decimals = null ;
bool [ ] ? booleans = null ;
DateTime [ ] ? dates = null ; // this might not work!
if ( parameters ! = null )
{
foreach ( var values in parameters )
{
if ( values . GetType ( ) = = typeof ( string [ ] ) )
strings = values as string [ ] ;
else if ( values . GetType ( ) = = typeof ( int [ ] ) )
integers = values as int [ ] ;
else if ( values . GetType ( ) = = typeof ( decimal [ ] ) )
decimals = values as decimal [ ] ;
else if ( values . GetType ( ) = = typeof ( bool [ ] ) )
booleans = values as bool [ ] ;
else if ( values . GetType ( ) = = typeof ( DateTime [ ] ) )
dates = values as DateTime [ ] ;
}
}
Log . Debug ( $"Running command '{command}' …" ) ;
VA . Command . Execute (
CommandPhrase : command ,
WaitForReturn : wait ,
AsSubcommand : subcommand ,
CompletedAction : callback ,
2020-12-02 19:08:46 +01:00
PassedText : strings = = null ? null : $@"""{String.Join<string>(@""" ; "" ", strings)}" "" ,
2020-10-21 01:13:40 +02:00
PassedIntegers : integers = = null ? null : String . Join < int > ( ";" , integers ) ,
PassedDecimals : decimals = = null ? null : String . Join < decimal > ( ";" , decimals ) ,
PassedBooleans : booleans = = null ? null : String . Join < bool > ( ";" , booleans ) ,
PassedDates : dates = = null ? null : String . Join < DateTime > ( ";" , dates )
) ;
}
else
{
if ( logMissing )
Log . Warn ( $"Tried running missing command '{command}'." ) ;
}
}
public void RunAll ( IEnumerable < string > prefixes , string command , bool logMissing = true , bool wait = false , bool subcommand = false , Action < Guid ? > ? callback = null , dynamic [ ] ? parameters = null )
{
foreach ( string prefix in prefixes )
Run ( $"{prefix}.{command}" , logMissing , wait , subcommand , callback , parameters ) ;
}
public void TriggerEvent ( string name , bool logMissing = true , bool wait = false , bool subcommand = false , Action < Guid ? > ? callback = null , dynamic [ ] ? parameters = null )
{
Run ( $"(({name}))" , logMissing , wait , subcommand , callback , parameters ) ;
}
public void TriggerEventAll ( IEnumerable < string > prefixes , string name , bool logMissing = true , bool wait = false , bool subcommand = false , Action < Guid ? > ? callback = null , dynamic [ ] ? parameters = null )
{
foreach ( string prefix in prefixes )
Run ( $"(({prefix}.{name}))" , logMissing , wait , subcommand , callback , parameters ) ;
}
}
public class VoiceAttackLog
2020-10-25 10:13:14 +01:00
{
2020-10-21 01:13:40 +02:00
private readonly dynamic VA ;
private readonly string ID ;
2021-01-20 12:31:13 +01:00
//private static readonly string LogFolder = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "alterNERDtive-logs");
//private static readonly TextWriterTraceListener LogListener = new TextWriterTraceListener(Path.Combine(LogFolder, "VoiceAttack.log"), "error");
//private static readonly TextWriterTraceListener DebugListener = new TextWriterTraceListener(Path.Combine(LogFolder, "VoiceAttack-debug.log"), "debug");
//private readonly TraceSource TraceSource;
2020-12-29 19:08:14 +01:00
2020-10-21 01:13:40 +02:00
private static readonly string [ ] LogColour = { "red" , "yellow" , "green" , "blue" , "gray" } ;
2020-10-25 10:13:14 +01:00
public LogLevel ? CurrentLogLevel
{
2020-10-21 01:13:40 +02:00
get = > currentLogLevel ? ? LogLevel . NOTICE ;
set
2020-10-25 10:13:14 +01:00
{
2020-10-21 01:13:40 +02:00
currentLogLevel = value ;
Notice ( $"Log level set to {value ?? LogLevel.NOTICE}." ) ;
}
}
public void SetCurrentLogLevel ( string level )
{
if ( level = = null )
CurrentLogLevel = null ;
else
CurrentLogLevel = ( LogLevel ) Enum . Parse ( typeof ( LogLevel ) , level . ToUpper ( ) ) ;
}
private static LogLevel ? currentLogLevel ;
2021-01-20 12:31:13 +01:00
//static VoiceAttackLog()
//{
// Directory.CreateDirectory(LogFolder);
// string path = Path.Combine(LogFolder, $"VoiceAttack");
// foreach (string name in new String[] { "", "-debug" })
// {
// File.Delete($"{path}{name}-5.log");
// for (int i = 4; i > 0; i--)
// {
// if (File.Exists($"{path}{name}-{i}.log"))
// {
// File.Move($"{path}{name}-{i}.log", $"{path}{name}-{i + 1}.log");
// }
// }
// File.Move($"{path}{name}.log", $"{path}{name}-1.log");
// }
// File.Delete(Path.Combine(LogFolder, "VoiceAttack.log"));
// File.Delete(Path.Combine(LogFolder, "VoiceAttack-debug.log"));
// DebugListener.TraceOutputOptions = LogListener.TraceOutputOptions = TraceOptions.DateTime;
// LogListener.Filter = new EventTypeFilter(SourceLevels.Information);
// DebugListener.Filter = new EventTypeFilter(SourceLevels.All);
//}
2020-12-29 19:08:14 +01:00
public VoiceAttackLog ( dynamic vaProxy , string id )
2020-10-21 01:13:40 +02:00
{
2020-12-29 19:08:14 +01:00
VA = vaProxy ;
ID = id ;
2021-01-20 12:31:13 +01:00
//TraceSource = new TraceSource(ID);
//TraceSource.Listeners.Add(LogListener);
//TraceSource.Listeners.Add(DebugListener);
//TraceSource.Switch.Level = SourceLevels.All;
2020-10-21 01:13:40 +02:00
}
2020-12-29 19:08:14 +01:00
public void Log ( string message , LogLevel level = LogLevel . INFO )
2020-10-21 01:13:40 +02:00
{
_ = message ? ? throw new ArgumentNullException ( "message" ) ;
if ( level < = CurrentLogLevel )
2020-12-29 19:08:14 +01:00
{
VA . WriteToLog ( $"{level} | {ID}: {message}" , LogColour [ ( int ) level ] ) ;
}
2021-01-20 12:31:13 +01:00
//switch(level)
//{
// case LogLevel.ERROR:
// TraceSource.TraceEvent(TraceEventType.Error, 0, message);
// break;
// case LogLevel.WARN:
// TraceSource.TraceEvent(TraceEventType.Warning, 1, message);
// break;
// case LogLevel.NOTICE:
// case LogLevel.INFO:
// TraceSource.TraceEvent(TraceEventType.Information, 2, message);
// break;
// case LogLevel.DEBUG:
// TraceSource.TraceEvent(TraceEventType.Verbose, 3, message);
// break;
//}
//TraceSource.Flush();
2020-10-21 01:13:40 +02:00
}
public void Error ( string message ) = > Log ( message , LogLevel . ERROR ) ;
public void Warn ( string message ) = > Log ( message , LogLevel . WARN ) ;
public void Notice ( string message ) = > Log ( message , LogLevel . NOTICE ) ;
public void Info ( string message ) = > Log ( message , LogLevel . INFO ) ;
public void Debug ( string message ) = > Log ( message , LogLevel . DEBUG ) ;
}
public interface IPipable
{
public void ParseString ( string serialization ) ;
public string ToString ( ) ;
}
public class PipeServer < Thing > where Thing : IPipable , new ( )
{
private readonly string PipeName ;
private readonly SignalHandler Handler ;
private readonly VoiceAttackLog Log ;
private bool Running = false ;
2020-10-25 10:13:14 +01:00
private NamedPipeServerStream ? Server ;
2020-10-21 01:13:40 +02:00
public PipeServer ( VoiceAttackLog log , string name , SignalHandler handler )
= > ( Log , PipeName , Handler ) = ( log , name , handler ) ;
public delegate void SignalHandler ( Thing thing ) ;
public PipeServer < Thing > Run ( )
{
2020-12-02 19:08:46 +01:00
Log . Debug ( $"Starting '{PipeName}' pipe …" ) ;
2020-10-21 01:13:40 +02:00
if ( ! Running )
{
Running = true ;
2020-10-25 10:13:14 +01:00
WaitForConnection ( ) ;
2020-10-21 01:13:40 +02:00
}
return this ;
}
public PipeServer < Thing > Stop ( )
{
2020-12-02 19:08:46 +01:00
Log . Debug ( $"Stopping '{PipeName}' pipe …" ) ;
2020-10-21 01:13:40 +02:00
if ( Running )
{
Running = false ;
2020-10-25 10:13:14 +01:00
Server ! . Close ( ) ;
2020-10-21 01:13:40 +02:00
}
return this ;
}
2020-10-25 10:13:14 +01:00
private void WaitForConnection ( )
2020-10-21 01:13:40 +02:00
{
2020-10-25 10:13:14 +01:00
try
2020-10-21 01:13:40 +02:00
{
2020-10-25 10:13:14 +01:00
Server = new NamedPipeServerStream ( PipeName , PipeDirection . In , NamedPipeServerStream . MaxAllowedServerInstances , PipeTransmissionMode . Message , PipeOptions . Asynchronous ) ;
Server . BeginWaitForConnection ( OnConnect , Server ) ;
}
catch ( Exception e )
{
Log . Error ( $"Error setting up pipe: {e.Message}" ) ;
}
}
private void OnConnect ( IAsyncResult ar )
{
NamedPipeServerStream server = ( NamedPipeServerStream ) ar . AsyncState ;
try
{
server . EndWaitForConnection ( ar ) ;
WaitForConnection ( ) ;
using StreamReader reader = new StreamReader ( server ) ;
Thing thing = new Thing ( ) ;
thing . ParseString ( reader . ReadToEnd ( ) ) ;
Handler ( thing ) ;
}
catch ( ObjectDisposedException )
{
2020-12-02 19:08:46 +01:00
Log . Debug ( $"'{PipeName}' pipe has been closed." ) ;
2020-10-25 10:13:14 +01:00
}
catch ( Exception e )
{
Log . Error ( $"Error reading pipe: {e.Message}" ) ;
2020-10-21 01:13:40 +02:00
}
}
}
}