You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

83 lines
6.6 KiB

// Read-only reference harness for the PC's bundled Windows x86 MDF4Writer.dll.
// Mirrors WriteA2LMDF4.cpp's DLL branch and accepts common raw input JSON.
using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Runtime.InteropServices;
using System.Web.Script.Serialization;
class PcMdf4Reference {
const string Dll = "MDF4Writer.dll";
[DllImport("kernel32.dll", CharSet=CharSet.Unicode)] static extern bool SetDllDirectory(string path);
[DllImport(Dll, CallingConvention=CallingConvention.Cdecl)] static extern IntPtr M4WRInitDll();
[DllImport(Dll, CallingConvention=CallingConvention.Cdecl)] static extern void M4WRExitDll(IntPtr h);
[DllImport(Dll, CallingConvention=CallingConvention.Cdecl, CharSet=CharSet.Unicode)] static extern void M4WRSetPathName(IntPtr h, string path);
[DllImport(Dll, CallingConvention=CallingConvention.Cdecl, CharSet=CharSet.Unicode)] static extern bool M4WRCreateMDF4(IntPtr h, string tool, string vendor, string version, string comment, IntPtr mdfVersion);
[DllImport(Dll, CallingConvention=CallingConvention.Cdecl, CharSet=CharSet.Unicode)] static extern bool M4WRFileDescriptionEx(IntPtr h, string comment, int quality, double start, ushort tz, ushort dst, byte flags);
[DllImport(Dll, CallingConvention=CallingConvention.Cdecl, CharSet=CharSet.Unicode)] static extern void M4WRAddGroup64(IntPtr h, string name, long count, out IntPtr group);
[DllImport(Dll, CallingConvention=CallingConvention.Cdecl, CharSet=CharSet.Unicode)] static extern bool M4WRAddTimeSignal(IntPtr h, int group, string name, int type, int first, int bits, string unit, double factor, double offset, out int signal);
[DllImport(Dll, CallingConvention=CallingConvention.Cdecl, CharSet=CharSet.Unicode)] static extern bool M4WRAddSignal(IntPtr h, int group, string name, string label, int type, int first, int bits, string unit, double factor, double offset, int hasNoValue, double noValue, int invalidBit, out int signal);
[DllImport(Dll, CallingConvention=CallingConvention.Cdecl, CharSet=CharSet.Unicode)] static extern bool M4WRSignalDescription(IntPtr h, int group, int signal, string name, string path, string comment);
[DllImport(Dll, CallingConvention=CallingConvention.Cdecl)] static extern bool M4WRMakeGroups(IntPtr h);
[DllImport(Dll, CallingConvention=CallingConvention.Cdecl)] static extern bool M4WRSetSignalValue(IntPtr h, int group, int signal, double value, int noValue);
[DllImport(Dll, CallingConvention=CallingConvention.Cdecl)] static extern bool M4WRWriteRecord(IntPtr h, int group);
[DllImport(Dll, CallingConvention=CallingConvention.Cdecl)] static extern bool M4WRSetMinMax(IntPtr h, int group, int signal, double min, double max);
[DllImport(Dll, CallingConvention=CallingConvention.Cdecl)] static extern bool M4WRFlushGroup(IntPtr h, int group);
[DllImport(Dll, CallingConvention=CallingConvention.Cdecl)] static extern void M4WRClose(IntPtr h);
[DllImport(Dll, CallingConvention=CallingConvention.Cdecl)] static extern void M4WRSetNanoTimeUTC(IntPtr h, long time);
static double Number(object o) { return Convert.ToDouble(o, CultureInfo.InvariantCulture); }
static int Main(string[] args) {
if (args.Length != 3) throw new ArgumentException("dll-directory input-json new-output-mf4");
if (File.Exists(args[2])) throw new IOException("Refusing to overwrite existing reference output");
SetDllDirectory(Path.GetFullPath(args[0]));
var json = new JavaScriptSerializer { MaxJsonLength = Int32.MaxValue };
var data = json.Deserialize<Dictionary<string, object>>(File.ReadAllText(args[1]));
var channels = (ArrayList)data["channels"];
var ids = new int[channels.Count];
IntPtr h = M4WRInitDll();
try {
M4WRSetPathName(h, Path.GetFullPath(args[2]));
if (!M4WRCreateMDF4(h, "DCT", "Doowon Climate Control", "1.0", "", new IntPtr(410))) throw new Exception("CreateMDF4 failed");
M4WRSetNanoTimeUTC(h, Convert.ToInt64(data["start_ns"]));
if (!M4WRFileDescriptionEx(h, "<HDcomment></HDcomment>", -1, Number(data["start_ns"])/1000000000.0,
Convert.ToUInt16(data["timezone_minutes"]), Convert.ToUInt16(data["dst_minutes"]), Convert.ToByte(data["time_flags"]))) throw new Exception("FileDescription failed");
IntPtr group;
M4WRAddGroup64(h, "Measurements", 10000, out group);
int g = group.ToInt32();
if (!M4WRAddTimeSignal(h, g, "Time", 4, 0, 64, "s", 1, 0, out ids[0])) throw new Exception("Time failed");
for (int i=1; i<channels.Count; i++) {
var c = (Dictionary<string,object>)channels[i];
double factor=1, offset=0;
if (c["conversion"] != null) {
var cc=(Dictionary<string,object>)c["conversion"];
if (Convert.ToInt32(cc["type"])!=1) throw new Exception("DLL AddSignal reference only supports linear conversion");
var p=(ArrayList)cc["parameters"]; offset=Number(p[0]); factor=Number(p[1]);
}
if (!M4WRAddSignal(h,g,(string)c["name"],(string)c["description"],Convert.ToInt32(c["data_type"]),-1,Convert.ToInt32(c["bit_count"]),(string)c["unit"],factor,offset,0,0,-1,out ids[i])) throw new Exception("Signal failed: "+c["name"]);
if ((Convert.ToInt32(c["flags"]) & 8) != 0)
M4WRSetMinMax(h,g,ids[i],Number(c["min_raw"]),Number(c["max_raw"]));
if (c["source"]!=null) {
var s=(Dictionary<string,object>)c["source"];
M4WRSignalDescription(h,g,ids[i],(string)s["name"],(string)s["path"],(string)s["comment"]);
}
}
if (!M4WRMakeGroups(h)) throw new Exception("MakeGroups failed");
// PC WriteData reads every EnvSetting item's current value on every record.
// That state survives unreceived messages; the DLL's record buffer itself resets.
var currentValues = new double[ids.Length];
foreach(ArrayList record in (ArrayList)data["records"]) {
for(int i=0; i<ids.Length; i++) {
if (record[i] != null) currentValues[i]=Number(record[i]);
if (!M4WRSetSignalValue(h,g,ids[i],currentValues[i],0)) throw new Exception("SetSignalValue failed");
}
if (!M4WRWriteRecord(h,g)) throw new Exception("WriteRecord failed");
}
M4WRFlushGroup(h,g); M4WRClose(h);
Console.WriteLine("PC DLL reference generated: "+args[2]);
} finally { M4WRExitDll(h); }
return 0;
}
}