using CartActivator; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; using System.Text; using System.Threading; using Newtonsoft.Json; using FundamentalLib; namespace MedullaAdapter { // todo: currently use this ladderlogic to act interaction with Medulla. // todo: directly integrate into CartActivator. // MCU->Medulla is intervally exchanged information. public class MCUInterface : LadderLogic where T : DiverCartDefinition { /// ////////////////////////////// IMPLEMENT THESE //////////////////////////////////////////// public static void SetMCUProgram(string mcu_device_url, byte[] program) { List SplitArrayIntoChunks(byte[] array, int chunkSize) { List chunks = new List(); for (int i = 0; i < array.Length; i += chunkSize) { int currentChunkSize = Math.Min(chunkSize, array.Length - i); byte[] chunk = new byte[currentChunkSize]; Array.Copy(array, i, chunk, 0, currentChunkSize); chunks.Add(chunk); } return chunks; } //todo: modify this. var codeList = SplitArrayIntoChunks(program, 1024); int i = 0; foreach (var codePack in codeList) { var downloadCode = new DownloadCodePack(program.Length, 1024 * i, codePack.Length, codePack).GetPack(); Console.WriteLine(string.Join(" ", downloadCode.Select(p => $"{p:X2}"))); cart.Embedded.SendMessage(downloadCode); Console.WriteLine($"send bytes length:{downloadCode.Length}"); i++; Thread.Sleep(50); //while (true) //{ // var receive = cart.Embedded.GetMessage(); // if (receive == null) continue; // if (receive[5] == 0x90 && (receive[6] == 0x05 || receive[6]==0x06)) break; //} } var controlPack2 = new ControlPack(0x01).GetPack(); cart.Embedded.SendMessage(controlPack2); Console.WriteLine("下发代码"); //MCUTestRunner.DebugSetMCUProgram(program, (bs) => NotifyLowerData("default", bs)); } public static void SendUpperData(string mcu_device_url, byte[] data) { //todo: this is for VM data exchange, contains upperIO/lowerIO modifications. var upperPack = new UpperIOPack(data, data.Length).GetPack(); cart.Embedded.SendMessage(upperPack); //MCUTestRunner.DebugSendUpper(data); } /// ////////////////////////////// INTERFACES //////////////////////////////////////////// public static void NotifyPrint(string mcu_device_url, string message) { Console.WriteLine($"{mcu_device_url}:{message}"); } // whenever a lower io data is uploaded, call this. public static void NotifyLowerData(string mcu_device_url, byte[] lowerIOData) { using var ms = new MemoryStream(lowerIOData); using var br = new BinaryReader(ms); if (mcu_logics.TryGetValue(mcu_device_url, out var tup)) { Hedingben.ToastText($"recv iter {br.ReadInt32()} lowerIO data from {mcu_device_url}, operation {tup.name}", $"DIVER-{tup.name}"); while (ms.Position < lowerIOData.Length) { var cid = br.ReadInt16(); if (cid < 0 || cid > tup.fields.Length) throw new Exception("invalid Cartfield id!"); // if it's upperio skip, otherwise write data. var typeid = br.ReadByte(); if (tup.fields[cid].typeid != typeid) throw new Exception($"??? typeid not match for {tup.fields[cid].field}({cid}), expected {tup.fields[cid].typeid} got {typeid}"); object value; switch (tup.fields[cid].typeid) { case 0: value = br.ReadBoolean(); break; case 1: value = br.ReadByte(); break; case 2: value = br.ReadSByte(); break; case 3: value = br.ReadChar(); break; case 4: value = br.ReadInt16(); break; case 5: value = br.ReadUInt16(); break; case 6: value = br.ReadInt32(); break; case 7: value = br.ReadUInt32(); break; case 8: value = br.ReadSingle(); break; default: throw new Exception($"Unsupported type ID: {tup.fields[cid].typeid}"); } if (tup.fields[cid].isUpper) continue; tup.fields[cid].fi.SetValue(cart, value); } // ok to send current data. using var sends = new MemoryStream(); using var bw = new BinaryWriter(sends); bw.Write(tup.iterations++); for (var cid = 0; cid < tup.fields.Length; cid++) { bw.Write((short)cid); bw.Write((byte)tup.fields[cid].typeid); var val = tup.fields[cid].fi.GetValue(cart); switch (tup.fields[cid].typeid) { case 0: bw.Write((bool)val); break; case 1: bw.Write((byte)val); break; case 2: bw.Write((sbyte)val); break; case 3: bw.Write((char)val); break; case 4: bw.Write((short)val); break; case 5: bw.Write((ushort)val); break; case 6: bw.Write((int)val); break; case 7: bw.Write((uint)val); break; case 8: bw.Write((float)val); break; } } SendUpperData(mcu_device_url, sends.ToArray()); } else { Hedingben.ToastText($"warning: {mcu_device_url} received lowerIOData but not registered","DIVERWarning"); } } public static void Start(T c) { cart = c; var logics = typeof(T).Assembly.GetTypes() .Where(p => p.GetCustomAttribute() != null).ToArray(); foreach (var logic in logics) { var attr = logic.GetCustomAttribute(); Console.WriteLine($"Set logic {logic.Name} to run on MCU-VM @ device {attr.mcu_url}"); byte[] ReadAllBytes(Stream stream) { using (var ms = new MemoryStream()) { stream.CopyTo(ms); return ms.ToArray(); } } var bytes = ReadAllBytes(Assembly.GetExecutingAssembly().GetManifestResourceStream($"{logic.Name}.bin")); SetMCUProgram(attr.mcu_url, bytes); var json = UTF8Encoding.UTF8.GetString(ReadAllBytes(Assembly.GetExecutingAssembly() .GetManifestResourceStream($"{logic.Name}.bin.json"))); Console.WriteLine(json); var fields = JsonConvert.DeserializeObject(json); if (mcu_logics.ContainsKey(attr.mcu_url)) throw new Exception($"Already have logic for {attr.mcu_url}: LadderLogic {logic.Name}"); mcu_logics[attr.mcu_url] = new LogicInfo() { fields = fields, name = logic.Name }; foreach (var pField in fields) { pField.fi = typeof(T).GetField(pField.field); if (pField.fi == null) throw new Exception($"field {pField.field} doesn't exist in cart object?"); pField.isUpper = pField.fi.IsDefined(typeof(AsUpperIO)); // todo: check type. } } } /// ///////////////////////////////// DONT CARE ///////////////////////////////////// private static T cart; class PField { public string field; public FieldInfo fi; public bool isUpper; public int typeid, offset; } class LogicInfo { public string name; public PField[] fields; public int iterations; } private static Dictionary mcu_logics = new(); public override void Operation(int iteration) { // just send LIO. } } //unsafe class MCUTestRunner //{ // public delegate void NotifyLowerDelegate(byte* changedStates, int length); // [DllImport("MCURuntime.dll", CallingConvention = CallingConvention.Cdecl)] // public static extern void set_lowerio_cb(NotifyLowerDelegate callback); // private static NotifyLowerDelegate DNotifyStateChanged = StateChanged; // [DllImport("MCURuntime.dll")] // static extern void test(byte* bin, int len); // [DllImport("MCURuntime.dll")] // static extern void put_upper(byte* bin, int len); // private static Action lo_notifier; // private static void StateChanged(byte* changedstates, int length) // { // byte[] byteArray = new byte[length]; // Marshal.Copy((IntPtr)changedstates, byteArray, 0, length); // lo_notifier(byteArray); // } // public static void DebugSendUpper(byte[] data) // { // fixed (byte* ptr = data) // { // put_upper(ptr, data.Length); // } // } // public static void DebugSetMCUProgram(byte[] program, Action notifier) // { // lo_notifier = notifier; // set_lowerio_cb(DNotifyStateChanged); // new Thread(() => // { // var allb = new byte[10240]; //10K runtime // Array.Copy(program, allb, program.Length); // fixed (byte* ptr = allb) // { // test(ptr, 10240); // } // }).Start(); // } //} }