Skip to content

Instantly share code, notes, and snippets.

@krypt-lynx
Created June 25, 2022 10:37
Show Gist options
  • Save krypt-lynx/29f3c17cdd283c3f88af515f1c330aee to your computer and use it in GitHub Desktop.
Save krypt-lynx/29f3c17cdd283c3f88af515f1c330aee to your computer and use it in GitHub Desktop.
// PBDiagnostics
// Simple tool to manipulate programmable block's Storage variable.
ParamsRouter paramsRouter;
public Program()
{
IsolatedRun(() =>
{
Log.NewFrame();
paramsRouter = new ParamsRouter();
paramsRouter.Cases = new Dictionary<string, Action<string>>
{
{ "read", ReadStorage },
{ "write", WriteStorage },
{ "export", ExportStorage },
{ "import", ImportStorage },
{ "clear", ClearStorage },
{ "pbinfo", PBInfo },
{ "blockinfo", BlockInfo },
};
ShowHelp();
});
}
private void ShowRunInfo(string arg, UpdateType ut)
{
Log.WriteFormat("Received command \"{0}\"", arg);
Log.Write(string.Format("With update types: {0}", ut));
Log.WriteLine();
Log.WriteLine();
}
private void ShowInfo()
{
Log.Write("Block:");
Log.Write(string.Format("Name: {0}", Me.CustomName));
Log.Write(string.Format("Entity ID: {0}", Me.EntityId));
Log.Write("Grid:");
Log.Write(string.Format("Name: {0}", Me.CubeGrid.CustomName));
Log.Write(string.Format("Entity ID: {0}", Me.CubeGrid.EntityId));
Log.Write(string.Format("Static: {0}", Me.CubeGrid.IsStatic ? "Yes" : " No"));
Log.Write(string.Format("Type: {0}", Me.CubeGrid.GridSizeEnum));
}
private void ShowHelp()
{
Log.Write("Available commands:");
Log.WriteLine();
Log.Write("read - prints Storage variable");
Log.Write("write <value> - writes passed value to Storage");
Log.Write("export - exports Storage variable to Custom Data");
Log.Write("import - imports new Storage value from Custom Data");
Log.Write("clear - clears Storage variable");
Log.Write("pbinfo - show pb block information");
Log.Write("blockinfo <name> - show block information");
}
private void PBInfo(string arg)
{
ShowInfo();
}
private void ImportStorage(string arg)
{
Storage = Me.CustomData;
Log.Write("import done!");
}
private void ExportStorage(string arg)
{
if (arg.Equals("--force", StringComparison.InvariantCultureIgnoreCase) || string.IsNullOrWhiteSpace(Me.CustomData))
{
Me.CustomData = Storage;
Log.Write("export done!");
}
else
{
Log.Write("Export failed: CustomData is not empty.\n" +
"If you want to export stored data anyway use \"export --force\"\n" +
"Current CustomData value will lost");
}
}
private void WriteStorage(string arg)
{
Storage = arg;
Log.Write("write done!");
}
private void ReadStorage(string arg)
{
Log.Write("Storage is:");
Log.Write(Storage);
}
private void ClearStorage(string arg)
{
Storage = "";
Log.Write("clear done!");
}
private void BlockInfo(string arg)
{
var block = GridTerminalSystem.GetBlockWithName(arg);
Log.Write("== Block ==");
Log.Write($"DefName:\n \"{block.DefinitionDisplayNameText}\"");
Log.Write($"Type Id/Subtype Id:\n \"{block.BlockDefinition.TypeId}\"\n \"{block.BlockDefinition.SubtypeId}\"");
Log.Write($"Type: {block.GetType().Name}");
Log.Write($"CustomName:\n \"{block.CustomName}\"");
Log.Write($"Entity ID: {block.EntityId}");
Log.Write("");
Log.Write("== Status ==");
Log.Write($"OwnerId: {block.OwnerId}");
Log.Write($"IsWorking: {block.IsWorking}");
Log.Write($"IsFunctional: {block.IsFunctional}");
Log.Write($"");
Log.Write("== Inventories ==");
InvList(block);
Log.Write($"");
Log.Write("== Text Surfaces ==");
SurfaceList(block);
Log.Write($"");
Log.Write("== Actions ==");
Actionlist(block);
Log.Write("");
Log.Write("== Properties ==");
Proplist(block);
}
enum PropType
{
Boolean,
StringBuilder,
Single,
Int64,
Color,
String,
}
private void SurfaceList(IMyTerminalBlock block)
{
var provider = block as IMyTextSurfaceProvider;
if (provider == null)
{
Log.Write($"block is not a surface provider");
return;
}
Log.Write($"Surfaces count: {provider.SurfaceCount}");
for (int i = 0, imax = provider.SurfaceCount; i < imax; i++)
{
var surface = provider.GetSurface(i);
Log.Write($"surface {i}");
Log.Write($" texture size: {surface.TextureSize}");
Log.Write($" surface size: {surface.SurfaceSize}");
var texModifier = (float)Math.Min(surface.TextureSize.X, surface.TextureSize.Y) / 512f;
Log.Write($" font size magic scale: {texModifier}");
}
}
private void InvList(IMyTerminalBlock block)
{
Log.Write($"Inventories count: {block.InventoryCount}");
for (int i = 0, imax = block.InventoryCount; i < imax; i++)
{
var inventory = block.GetInventory(i);
Log.Write($" inventory {i}: {inventory.CurrentMass}kg {inventory.CurrentVolume * 1000}/{inventory.MaxVolume * 1000}L");
}
}
private void Proplist(IMyTerminalBlock block)
{
List<ITerminalProperty> props = new List<ITerminalProperty>();
block.GetProperties(props);
var allProps = new HashSet<string>();
//var badProps = new HashSet<string>(); // Termimal Properties can have same ids, which makes them unaccessible
foreach (var prop in props)
{
/*if (allProps.Contains(prop.Id))
{
badProps.Add(prop.Id);
}
else*/
//{
allProps.Add(prop.Id);
//}
}
foreach (var prop in props)
{
// block.GetValue<object>(prop.Id) - Property is not of Type object <...>
object value = null;
try
{
PropType propType;
//if (!badProps.Contains(prop.Id) && Enum.TryParse(prop.TypeName, out propType))
if (Enum.TryParse(prop.TypeName, out propType))
{
switch (propType)
{
case PropType.Boolean:
value = block.GetValueBool(prop.Id);
break;
case PropType.Single:
value = block.GetValueFloat(prop.Id);
break;
case PropType.Color:
value = block.GetValueColor(prop.Id);
break;
case PropType.StringBuilder:
value = block.GetValue<StringBuilder>(prop.Id);
break;
case PropType.String:
value = block.GetValue<string>(prop.Id);
break;
case PropType.Int64:
value = block.GetValue<long>(prop.Id);
break;
}
}
}
catch { }
Log.Write($"\"{prop.Id}\" ({prop.TypeName})\n \"{value}\"");
}
}
private void Actionlist(IMyTerminalBlock block)
{
List<ITerminalAction> actions = new List<ITerminalAction>();
block.GetActions(actions);
foreach (var action in actions)
{
Log.Write($"{action.Id}\n {action.Name}");
}
}
public void Main(string argument, UpdateType updateSource)
{
IsolatedRun(() =>
{
Log.NewFrame();
Action command = null;
if ((updateSource & UpdateType.Terminal) != 0)
{
command = paramsRouter.GetCase(argument);
}
if (updateSource != UpdateType.Terminal) // not contains
{
ShowRunInfo(argument, updateSource);
}
else
{
if (command != null)
{
command();
}
else
{
ShowRunInfo(argument, updateSource);
ShowHelp();
}
}
});
}
// ----------
// Environment.cs
Exception lastException = null;
public static MyGridProgram Current;
public void IsolatedRun(Action work)
{
if (lastException == null)
{
try
{
Current = this;
Log.NewFrame();
work();
Current = null;
}
catch (Exception e)
{
lastException = e;
}
}
if (lastException != null)
{
EchoException();
return;
}
}
private void EchoException()
{
Echo("Exception handled!");
Echo("Please, make screenshot of this message and report the issue to developer");
Echo("To recover recompile the script");
Echo(lastException.Message);
Echo(lastException.StackTrace);
Echo(lastException.Message);
}
}
// ----------
// ParamsRouter.cs
class ParamsRouter
{
public Dictionary<string, Action<string>> Cases = null;
public Action NoArgsCase = null;
public Action<string, UpdateType> UnknownCase = null;
void GetCommand(string arg, out string cmd, out string tail)
{
arg = arg.TrimStart();
int firstSpace = arg.IndexOf(' ');
cmd = null;
tail = null;
if (firstSpace != -1)
{
cmd = arg.Substring(0, firstSpace);
tail = arg.Substring(firstSpace + 1);
}
else
{
cmd = arg;
tail = "";
}
}
public Action GetCase(string arg)
{
string command, tail;
GetCommand(arg, out command, out tail);
if (!string.IsNullOrEmpty(command))
{
if (Cases?.ContainsKey(command) ?? false)
{
return () => { Cases[command](tail); };
}
}
return null;
}
}
// ----------
// Log.cs
enum LogLevel
{
None,
Error,
Warning,
Verbose,
SpamMeToDeath
}
class Log
{
public static Dictionary<string, LogLevel> LogLevels = null;
const string NewFrameSeparator = "--------------------------------------";
static bool insertNewFrameSeparator = false;
static StringUtils strUtils = new StringUtils(new DebugFontDataProvider());
public static void Write(string logcat, LogLevel level, object anObject)
{
if (IsAllowed(logcat, level))
{
string str = string.Format("[{0}] {1}", logcat, anObject);
WriteInternal(str);
}
}
private static bool IsAllowed(string logcat, LogLevel level)
{
LogLevel currentLevel = LogLevel.None;
bool isAllowed = LogLevels == null || ((LogLevels?.TryGetValue(logcat, out currentLevel) ?? false) && level <= currentLevel);
return isAllowed;
}
public static void WriteFormat(string logcat, LogLevel level, string format, params object[] args)
{
if (IsAllowed(logcat, level))
{
string str = string.Format("[{0}] {1}", logcat, string.Format(format, args));
WriteInternal(str);
}
}
public static void WriteLine(string logcat, LogLevel level)
{
if (IsAllowed(logcat, level))
{
WriteInternal("");
}
}
public static void Write(object anObject)
{
if (anObject == null)
{
anObject = "<null>";
}
string str = anObject.ToString();
WriteInternal(str);
}
public static void WriteFormat(string format, params object[] args)
{
WriteInternal(string.Format(format, args));
}
public static void WriteLine()
{
WriteInternal("");
}
private static void WriteInternal(string str)
{
if (Program.Current != null)
{
WrappedEcho(str);
var dServer = Program.Current.GridTerminalSystem.GetBlockWithName("DebugSrv") as IMyProgrammableBlock;
if (dServer != null)
{
if (insertNewFrameSeparator)
{
insertNewFrameSeparator = false;
dServer.TryRun("L" + NewFrameSeparator);
}
dServer.TryRun("L" + str);
}
}
else
{
throw new InvalidOperationException("Program.Current is not assigned");
}
}
private static void WrappedEcho(string str)
{
foreach (var line in strUtils.Wrap(str, 615))
{
Program.Current.Echo(line);
}
}
internal static void NewFrame()
{
insertNewFrameSeparator = true;
}
}
// ----------
// SEStrUtils.cs
public enum WordWrapOptions
{
None = 0,
Overflow = 1,
SplitWords = 2,
NewLineExtra = 4,
Default = None
}
public interface IFontDataProvider
{
int Width(char ch);
int Width(string str, char lead = '\0');
int Height();
int LetterSpacing(char left, char right);
}
public class StringUtils
{
public StringUtils(IFontDataProvider dataProvider)
{
this.dataProvider = dataProvider;
}
IFontDataProvider dataProvider = null;
public List<string> Wrap(string str, int width, WordWrapOptions options = WordWrapOptions.Default)
{
int end;
return Wrap(str, x => width, 0, out end, options);
}
public List<string> Wrap(string str, Func<int, int> width, WordWrapOptions options = WordWrapOptions.Default)
{
int end;
return Wrap(str, width, 0, out end, options);
}
public List<string> Wrap(string str, Func<int, int> width, int start, out int end, WordWrapOptions options = WordWrapOptions.Default)
{
List<string> lines = new List<string>();
bool allowClipping = (options | WordWrapOptions.Overflow) == options;
bool splitWords = (options | WordWrapOptions.SplitWords) == options;
bool newLineExtra = (options | WordWrapOptions.NewLineExtra) == options;
StringBuilder line = new StringBuilder();
StringBuilder candidate = new StringBuilder();
int lWidth = width(lines.Count);
if (lWidth == -1)
{
end = start;
return lines;
}
int cWidth = 0;
char left = '\0';
char[] chars = str.ToCharArray();
int i = start;
int imax = chars.Length;
for (; i < imax; i++)
{
char ch = chars[i];
int delta = dataProvider.LetterSpacing(left, ch) + dataProvider.Width(ch);
if (ch == '\n')
{
line.Append(candidate);
lines.Add(line.ToString());
if (newLineExtra && line.Length > 0)
{
lines.Add("");
}
candidate.Clear();
cWidth = 0;
line.Clear();
lWidth = width(lines.Count);
if (lWidth == -1)
{
goto loop_break;
}
}
else if (ch == '\r')
{
}
else if (splitWords || !char.IsWhiteSpace(ch))
{
if (cWidth + delta > lWidth)
{
if (lWidth < width(lines.Count))
{
lines.Add(line.ToString());
line.Clear();
lWidth = width(lines.Count);
if (lWidth == -1)
{
goto loop_break;
}
}
if (
(cWidth + delta > width(lines.Count)) &&
!(allowClipping && candidate.Length == 0)
)
{
if (candidate.Length == 0)
{
goto loop_break;
}
line.Append(candidate);
lines.Add(line.ToString());
line.Clear();
lWidth = width(lines.Count);
if (lWidth == -1)
{
goto loop_break;
}
candidate.Clear();
candidate.Append(ch);
cWidth = delta;
}
else
{
cWidth += delta;
candidate.Append(ch);
}
}
else
{
cWidth += delta;
candidate.Append(ch);
}
}
else
{
line.Append(candidate);
lWidth -= cWidth;
candidate.Clear();
cWidth = 0;
if (lWidth <= delta)
{
lines.Add(line.ToString());
line.Clear();
lWidth = width(lines.Count);
if (lWidth == -1)
{
goto loop_break;
}
}
else
{
lWidth -= delta;
line.Append(ch);
}
}
}
if (candidate.Length > 0)
{
line.Append(candidate);
}
if (line.Length > 0 || lines.Count == 0)
{
lines.Add(line.ToString());
}
loop_break:
end = i;
return lines;
}
}
class DebugFontDataProvider : IFontDataProvider
{
static string cRef =
"'|¦ˉ‘’‚\nј\n !I`ijl ¡¨¯´¸ÌÍÎÏìíîïĨĩĪīĮįİıĵĺļľłˆˇ˘˙˚˛˜˝ІЇії‹›∙\n(),.1:;[]ft{" +
"}·ţťŧț\n\"-rª­ºŀŕŗř\n*²³¹\n\\°“”„\nґ\n/ijтэє\nL_vx«»ĹĻĽĿŁГгзлхчҐ–•\n7?Jcz¢¿çć" +
"ĉċčĴźżžЃЈЧавийнопсъьѓѕќ\n3FKTabdeghknopqsuy£µÝàáâãäåèéêëðñòóôõöøùúûüýþÿāăąď" +
"đēĕėęěĝğġģĥħĶķńņňʼnōŏőśŝşšŢŤŦũūŭůűųŶŷŸșȚЎЗКЛбдекруцяёђћўџ\n+<=>E^~¬±¶ÈÉÊË×÷Ē" +
"ĔĖĘĚЄЏЕНЭ−\n#0245689CXZ¤¥ÇßĆĈĊČŹŻŽƒЁЌАБВДИЙПРСТУХЬ€\n$&GHPUVY§ÙÚÛÜÞĀĜĞĠĢĤĦŨ" +
"ŪŬŮŰŲОФЦЪЯжы†‡\nABDNOQRSÀÁÂÃÄÅÐÑÒÓÔÕÖØĂĄĎĐŃŅŇŌŎŐŔŖŘŚŜŞŠȘЅЊЖф□\nљ\nю\n%IJЫ\n@" +
"©®мшњ\nMМШ\nmw¼ŵЮщ\n¾æœЉ\n½Щ\n™\nWÆŒŴ—…‰\n\n\n\n\n\n\n\n\n" +
"\n";
static string kRef =
"\nЖв?ж?\nъв>\nьв>\nҐ,?-?.?‚?„?…?–?—?š>œ>à>á>â>ã>ä>å>æ>ç>è>é>ê>ë>ð>ñ?ò>ó>ô>õ" +
">ö>ø>ù?ú?û?ü?ž?ā>ă>ą>ć>ĉ>č>ď>đ>ě>ē>ĕ>ę>ĝ>ğ>ń?ň?ō>ŏ>ŕ?ř?ś>ŝ>ş>ș>ũ?ū?ŭ?ů?ų?ŵ?" +
"ź?Ц>Ш>Ы?Ю?б>в?ж?н>";
static Dictionary<char, int> cW;
static Dictionary<char, Dictionary<char, int>> kP;
public DebugFontDataProvider()
{
cW = new Dictionary<char, int>();
int w = 6;
foreach (char c in cRef)
{
if (c == '\n')
w++;
else
cW[c] = w;
}
kP = new Dictionary<char, Dictionary<char, int>>();
Dictionary<char, int> cur = null;
for (var e = ((IList<char>)kRef.ToCharArray()).GetEnumerator(); e.MoveNext();)
{
char c1 = e.Current;
e.MoveNext();
char c2 = e.Current;
if (c1 == '\n')
kP[c2] = cur = new Dictionary<char, int>();
else
cur[c1] = c2 - 64;
}
}
public int Width(char ch)
{
int w;
cW.TryGetValue(ch, out w);
return w;
}
public int Width(string str, char lead = '\0')
{
char lc = lead;
int wd = 0, w;
foreach (var c in str)
{
cW.TryGetValue(c, out w);
wd += w + 1;
if (kP.ContainsKey(lc))
{
kP[lc].TryGetValue(c, out w);
wd += w;
}
lc = c;
}
return wd;
}
public int LetterSpacing(char left, char right)
{
if (kP.ContainsKey(left))
{
int w;
kP[left].TryGetValue(right, out w);
return w + 1;
}
return 1;
}
int SpacingOrZero(char left, char right)
{
if (left == '\0' || right == '\0')
{
return 0;
}
else
{
return LetterSpacing(left, right);
}
}
public int Height()
{
return 37;
}
}
class MonospacedFontDataProvider : IFontDataProvider
{
const int charWidth = 24;
const int lineHeight = 37;
const int letterSpacing = 1;
public int LetterSpacing(char left, char right)
{
return letterSpacing;
}
public int Width(char ch)
{
return charWidth;
}
public int Width(string str, char lead = '\0')
{
return (charWidth + letterSpacing) * str.Length;
}
public int Height()
{
return lineHeight;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment