using HNWD.LatticeScreen; using System; using System.Collections; using System.Collections.Concurrent; using System.Collections.Generic; using System.Drawing; using System.Globalization; using System.IO; using System.Linq; using System.Runtime.InteropServices; using System.Text; using System.Threading; using System.Windows.Forms; using LatticeScreenServer; namespace HNWD.LatticeScreen { public class LedControl { public const int JR_OK = 0; private static readonly ConcurrentDictionary handle = new ConcurrentDictionary(); private static readonly ConcurrentDictionary domainInfo = new ConcurrentDictionary(); private static string configFile = "Config.ini"; private static FileSystemWatcher fileWatcher = null; private static int randomPtr = 1; public static string ConfigFile { get => configFile; set { configFile = Path.GetFullPath(value); fileWatcher?.Dispose(); fileWatcher = new FileSystemWatcher(Path.GetDirectoryName(configFile) ?? string.Empty, $"*{Path.GetExtension(configFile)}") { EnableRaisingEvents = true }; fileWatcher.Changed += (sender, e) => { if (e.FullPath == configFile && WatcherChangeTypes.All.HasFlag(e.ChangeType)) { isPlaySound = null; soundPerson = null; soundVolume = null; soundSpeed = null; font = null; fontSize = null; } }; } } static LedControl() { ConfigFile = "Config.ini"; // 将 dll 文件保存到运行根目录 SaveNonFile("bx_sdk_dual.dll", OnbonResources.bx_sdk_dual); SaveNonFile("cairo-1.14.6.dll", OnbonResources.cairo_1_14_6); SaveNonFile("freetype.dll", OnbonResources.freetype); SaveNonFile("freetype265.dll", OnbonResources.freetype265); SaveNonFile("libpng16.dll", OnbonResources.libpng16); SaveNonFile("msvcr110.dll", OnbonResources.msvcr110); SaveNonFile("zlibwapi.dll", OnbonResources.zlibwapi); } private static void SaveNonFile(string filePath, byte[] fileData) { if (!Path.IsPathRooted(filePath)) { filePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, filePath); } if (!File.Exists(filePath)) { using (var fileStream = new FileStream(filePath, FileMode.Create, FileAccess.ReadWrite, FileShare.Read, fileData.Length)) { fileStream.Write(fileData, 0, fileData.Length); } } } private static bool? isPlaySound = null; public static bool IsPlaySound { get { if (isPlaySound.HasValue) { return isPlaySound.Value; } var result = ReadValue(nameof(IsPlaySound)); if (bool.TryParse(result, out var value)) { isPlaySound = value; return value; } isPlaySound = false; return false; } } private static byte? soundPerson = null; public static byte SoundPerson { get { if (soundPerson.HasValue) { return soundPerson.Value; } var result = ReadValue(nameof(SoundPerson)); if (byte.TryParse(result, NumberStyles.Any, CultureInfo.InvariantCulture, out var value)) { soundPerson = value; return value; } soundPerson = 0; return 0; } } private static byte? soundVolume = null; public static byte SoundVolume { get { if (soundVolume.HasValue) { return soundVolume.Value; } var result = ReadValue(nameof(SoundVolume)); if (byte.TryParse(result, NumberStyles.Any, CultureInfo.InvariantCulture, out var value)) { soundVolume = value; return value; } soundVolume = 0; return 0; } } private static byte? soundSpeed = null; public static byte SoundSpeed { get { if (soundSpeed.HasValue) { return soundSpeed.Value; } var result = ReadValue(nameof(SoundSpeed)); if (byte.TryParse(result, NumberStyles.Any, CultureInfo.InvariantCulture, out var value)) { soundSpeed = value; return value; } soundSpeed = 0; return 0; } } private static string font = null; public static string Font { get { if (font != null) { return font; } var result = ReadValue(nameof(Font)); if (string.IsNullOrEmpty(result)) { font = "宋体"; } else { font = result; } return font; } } private static int? fontSize = null; public static int FontSize { get { if (fontSize.HasValue) { return fontSize.Value; } var result = ReadValue(nameof(FontSize)); if (int.TryParse(result, out var value)) { fontSize = value; } else { fontSize = 12; } return fontSize.Value; } } public static int JHCreateInst(ref IntPtr hdl, ushort serverid) { //var tmp = new IntPtr(new Random().Next(100, 1000000)); var tmp = new IntPtr(Interlocked.Increment(ref randomPtr)); hdl = tmp; handle.AddOrUpdate(hdl, hdl, (key, value) => tmp); return bx_sdk_dual.InitSdk(); } public static int JHCommInit() { return 0; } public static int JHDeleteInst(IntPtr hdl) { if (handle.TryGetValue(hdl, out var value)) { domainInfo.TryRemove(value, out _); } return 0; } public static int JHCommGetTransportlayer(ref IntPtr ptransportlayer, int dev) { return 0; } public static int JHCommGetNetArgStruct(ref IntPtr pargs, string ipordomain, ushort port, uint timeout) { //var tmp = new IntPtr(new Random().Next(100, 1000000)); var tmp = new IntPtr(Interlocked.Increment(ref randomPtr)); pargs = tmp; ushort controllerType = 0; var data = new bx_sdk_dual.Ping_data(); var err = bx_sdk_dual.cmd_tcpPing(Encoding.Default.GetBytes(ipordomain), port, ref data); if (err == 0) { controllerType = data.ControllerType; } else { var buffer = new byte[Marshal.SizeOf(typeof(bx_sdk_dual.Ping_data))]; err = bx_sdk_dual.cmd_udpPing(buffer); if (err != 0) { return err; } if (ipordomain == Encoding.Default.GetString(data.ipAdder).Replace("\0", "")) { controllerType = data.ControllerType; } else { return -1; } } var result = bx_sdk_dual.cmd_check_time(Encoding.Default.GetBytes(ipordomain), port); var doman = new DomainInfo { Address = ipordomain, Port = port, Timeout = timeout, ControllerType = controllerType, Size = new Size(data.ScreenWidth, data.ScreenHeight), Sync = new object() }; domainInfo.AddOrUpdate(tmp, doman, (key, value) => doman); return 0; } public static int JHMountTransportLayer(IntPtr hdl, IntPtr ptl, IntPtr argstruct) { handle.AddOrUpdate(hdl, argstruct, (key, value) => argstruct); return 0; } //private static string lastText = string.Empty; //private static DrawText? lastDateTime; private static ConcurrentDictionary lastTextDic = new ConcurrentDictionary(); private static ConcurrentDictionary lastDateTimeDic = new ConcurrentDictionary(); public static int JHDrawText(IntPtr hdl, short x, short y, ushort width, ushort height, uint format, string ptext) { if (!handle.TryGetValue(hdl, out var argsPtr)) { return -1; } if (!domainInfo.TryGetValue(argsPtr, out var domain)) { return -1; } lock (domain.Sync) { var lastTextTmp = ""; DrawText? lastDateTimeTmp = null; var hasLastTextTmp = lastTextDic.TryGetValue(domain.Address, out lastTextTmp); lastDateTimeDic.TryGetValue(domain.Address, out lastDateTimeTmp); var isDateTime = IsDateTimeText(ptext, out var dateStyle, out var timeStyle); if (isDateTime) { if (width < domain.Size.Width || height < domain.Size.Height) { if (!lastDateTimeTmp.HasValue) { lastDateTimeTmp = new DrawText() { Format = format, Location = new Point(x, y), Size = new Size(width, height), Text = ptext }; lastDateTimeDic.AddOrUpdate(domain.Address, lastDateTimeTmp, (key, value) => lastDateTimeTmp); return 0; } var tmpText = lastDateTimeTmp.Value.Text; if (IsDateTimeText(tmpText, out var dateStyleTmp, out var timeStyleTmp)) { if (dateStyle == dateStyleTmp && timeStyle == timeStyleTmp) { return 0; } } if (tmpText.EndsWith(" ")) { ptext = tmpText + ptext; } else { ptext = tmpText + " " + ptext; } IsDateTimeText(ptext, out dateStyle, out timeStyle); } else { //lastDateTimeTmp = null; lastDateTimeDic.TryRemove(domain.Address, out _); } if (IsDateTimeText(lastTextTmp, out var tmpDateStyle, out var tmpTimeStyle)) { if (dateStyle == tmpDateStyle && timeStyle == tmpTimeStyle) { return 0; } } } else { if (hasLastTextTmp && lastTextTmp == ptext) { return 0; } } bx_sdk_dual.EQprogramHeader_G6 header; header.FileType = 0x00; header.ProgramID = 0; header.ProgramStyle = 0x00; header.ProgramPriority = 0x00; header.ProgramPlayTimes = 1; header.ProgramTimeSpan = 0; header.SpecialFlag = 0; header.CommExtendParaLen = 0x00; header.ScheduNum = 0; header.LoopValue = 0; header.Intergrate = 0x00; header.TimeAttributeNum = 0x00; header.TimeAttribute0Offset = 0x0000; header.ProgramWeek = 0xff; header.ProgramLifeSpan_sy = 0xffff; header.ProgramLifeSpan_sm = 0x03; header.ProgramLifeSpan_sd = 0x14; header.ProgramLifeSpan_ey = 0xffff; header.ProgramLifeSpan_em = 0x03; header.ProgramLifeSpan_ed = 0x14; header.PlayPeriodGrpNum = 1; IntPtr ptr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(bx_sdk_dual.EQprogramHeader_G6))); Marshal.StructureToPtr(header, ptr, false); int err = bx_sdk_dual.program_addProgram_G6(ptr); if (err != 0) { return err; } ushort areaId = 0; if (isDateTime) { var aheader = new bx_sdk_dual.EQareaHeader_G6(); aheader.AreaX = 0; aheader.AreaY = 0; aheader.AreaWidth = (ushort)domain.Size.Width; aheader.AreaHeight = (ushort)domain.Size.Height; aheader.Transparency = 101; aheader.AreaType = (byte)bx_sdk_dual.AreaType.DateTime; ptr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(bx_sdk_dual.EQareaHeader_G6))); Marshal.StructureToPtr(aheader, ptr, false); err = bx_sdk_dual.program_addArea_G6(areaId, ptr); if (err != 0) { return err; } var timeData = new bx_sdk_dual.EQtimeAreaData_G56(); if (timeData.date_enable == 0) { if (dateStyle.HasValue) { timeData.date_enable = 1; timeData.datestyle = dateStyle.Value; } } if (timeData.time_enable == 0) { if (timeStyle.HasValue) { timeData.time_enable = 1; timeData.timestyle = timeStyle.Value; } } if (lastDateTimeTmp.HasValue) { timeData.linestyle = bx_sdk_dual.E_arrMode.eMULTILINE; //lastDateTime = null; lastDateTimeDic.TryRemove(domain.Address, out _); } else { timeData.linestyle = bx_sdk_dual.E_arrMode.eSINGLELINE; } timeData.color = (uint)bx_sdk_dual.E_Color_G56.eRED; timeData.fontName = Font; timeData.fontSize = (ushort)FontSize; timeData.fontAlign = GetHalign(format); if (timeData.fontAlign > 0) { timeData.fontAlign--; } ptr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(bx_sdk_dual.EQtimeAreaData_G56))); Marshal.StructureToPtr(timeData, ptr, false); err = bx_sdk_dual.program_timeAreaAddContent_G6(areaId, ptr); if (err != 0) { return err; } } else { var aheader = new bx_sdk_dual.EQareaHeader_G6(); aheader.AreaX = (ushort)x; aheader.AreaY = (ushort)y; aheader.AreaWidth = width; aheader.AreaHeight = height; aheader.Transparency = 101; ptr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(bx_sdk_dual.EQareaHeader_G6))); Marshal.StructureToPtr(aheader, ptr, false); err = bx_sdk_dual.program_addArea_G6(areaId, ptr); if (err != 0) { return err; } var str = Encoding.GetEncoding("GBK").GetBytes(ptext); if (ControlType.HasVoiceModule(domain.ControllerType) && IsPlaySound) { var sound = new bx_sdk_dual.EQPicAreaSoundHeader_G6(); sound.SoundPerson = SoundPerson; sound.SoundVolum = SoundVolume; sound.SoundSpeed = SoundSpeed; sound.SoundDataMode = 0; sound.SoundReplayTimes = 0; sound.SoundReplayDelay = 1000; sound.SoundReservedParaLen = 3; sound.Soundnumdeal = 0; sound.Soundlanguages = 0; sound.Soundwordstyle = 0; bx_sdk_dual.program_pictureAreaEnableSound_G6(0, sound, str); } var font = Encoding.Default.GetBytes(Font); bx_sdk_dual.EQpageHeader_G6 pheader = new bx_sdk_dual.EQpageHeader_G6(); pheader.PageStyle = 0x00; pheader.DisplayMode = 0x01; //移动模式 pheader.ClearMode = 0x01; pheader.Speed = 15; //速度 pheader.StayTime = 0; //停留时间 pheader.RepeatTime = 1; pheader.ValidLen = 0; pheader.CartoonFrameRate = 0x00; pheader.BackNotValidFlag = 0x00; pheader.arrMode = bx_sdk_dual.E_arrMode.eSINGLELINE; pheader.fontSize = (ushort)FontSize; pheader.color = (uint)0x01; pheader.fontBold = 0; pheader.fontItalic = 0; pheader.tdirection = bx_sdk_dual.E_txtDirection.pNORMAL; pheader.txtSpace = 0; pheader.Valign = GetValign(format); pheader.Halign = GetHalign(format); ptr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(bx_sdk_dual.EQpageHeader_G6))); Marshal.StructureToPtr(pheader, ptr, false); err = bx_sdk_dual.program_picturesAreaAddTxt_G6(areaId, str, font, ptr); if (err != 0) { return err; } } bx_sdk_dual.EQprogram_G6 program = new bx_sdk_dual.EQprogram_G6(); program.fileName = Encoding.GetEncoding("GBK").GetBytes("P000"); program.fileType = 0; program.fileLen = 0; program.fileAddre = IntPtr.Zero; program.dfileName = Encoding.GetEncoding("GBK").GetBytes("D000"); program.dfileType = 0; program.dfileLen = 0; program.dfileAddre = IntPtr.Zero; err = bx_sdk_dual.program_IntegrateProgramFile_G6(ref program); if (err != 0) { return err; } err = bx_sdk_dual.cmd_ofsStartFileTransf(Encoding.GetEncoding("GBK").GetBytes(domain.Address), (ushort)domain.Port); if (err != 0) { return err; } err = bx_sdk_dual.cmd_ofsWriteFile(Encoding.GetEncoding("GBK").GetBytes(domain.Address), (ushort)domain.Port, program.dfileName, program.dfileType, program.dfileLen, 1, program.dfileAddre); if (err != 0) { return err; } err = bx_sdk_dual.cmd_ofsWriteFile(Encoding.GetEncoding("GBK").GetBytes(domain.Address), (ushort)domain.Port, program.fileName, program.fileType, program.fileLen, 1, program.fileAddre); if (err != 0) { return err; } err = bx_sdk_dual.cmd_ofsEndFileTransf(Encoding.GetEncoding("GBK").GetBytes(domain.Address), (ushort)domain.Port); if (err != 0) { return err; } bx_sdk_dual.program_deleteProgram_G6(); //lastText = ptext; lastTextDic.AddOrUpdate(domain.Address, ptext, (key, value) => ptext); return 0; } } public static int JHDrawMultPixel(IntPtr hdl, ushort[] pdat, ushort poscnt) { return 0; } private static Thread eraseThread; public static int JHErase(IntPtr hdl) { //return JHDrawText(hdl, 0, 0, ushort.MaxValue, ushort.MaxValue, 0, ""); StopEraseThreead(); eraseThread = new Thread(state => { Thread.Sleep(TimeSpan.FromSeconds(10)); JHDrawText(hdl, 0, 0, ushort.MaxValue, ushort.MaxValue, 0, ""); }); return 0; } private static void StopEraseThreead() { try { eraseThread?.Abort(); eraseThread?.DisableComObjectEagerCleanup(); } catch { } } public static int JHDrawBitmap(IntPtr hdl, short x, short y, ushort width, ushort height, uint format, tagBitmapSource pbmpsrc) { return 0; } public static int JHCommDeinit() { return 0; } public static int JHCommReleaseArgStruct(IntPtr pargs) { domainInfo.TryRemove(pargs, out _); return 0; } private static byte GetHalign(uint format) { var value = (int)((format >> 4) & 0x3); switch (value) { case 0b00: return 1; case 0b01: return 2; case 0b10: return 3; default: return 0; } } private static byte GetValign(uint format) { var value = (int)((format >> 6) & 0x3); switch (value) { case 0b00: return 1; case 0b01: return 2; case 0b10: return 3; default: return 0; } } private struct DomainInfo { public string Address { get; set; } public int Port { get; set; } public uint Timeout { get; set; } public ushort ControllerType { get; set; } public Size Size { get; set; } public object Sync { get; set; } } private struct DrawText { public Point Location { get; set; } public Size Size { get; set; } public string Text { get; set; } public uint Format { get; set; } public override bool Equals(object obj) { if (obj is DrawText other) { if (Location.Equals(other.Location) && Size.Equals(other.Size) && Format == other.Format) { if (IsDateTimeText(Text, out var thisDateStyle, out var thisTimeStyle)) { if (IsDateTimeText(other.Text, out var otherDateStyle, out var otherTimeStyle)) { return thisDateStyle == otherDateStyle && thisTimeStyle == otherTimeStyle; } } if (!IsDateTimeText(other.Text, out _, out _)) { return this.Text == other.Text; } } } return false; } public override int GetHashCode() { unchecked { var hashCode = Location.GetHashCode(); hashCode = (hashCode * 397) ^ Size.GetHashCode(); hashCode = (hashCode * 397) ^ Format.GetHashCode(); if (!IsDateTimeText(Text, out _, out _)) { hashCode = (hashCode * 397) ^ (Text != null ? Text.GetHashCode() : 0); } return hashCode; } } } private static bool IsDateTimeText(string text, out bx_sdk_dual.E_DateStyle? dateStyle, out bx_sdk_dual.E_TimeStyle? timeStyle) { dateStyle = null; timeStyle = null; if (DateTime.TryParseExact(text, "HH:mm:ss", CultureInfo.InvariantCulture, DateTimeStyles.None, out _)) { timeStyle = bx_sdk_dual.E_TimeStyle.eHH_MM_SS_COLON; return true; } if (DateTime.TryParseExact(text, "HH时MM分ss秒", CultureInfo.InvariantCulture, DateTimeStyles.None, out _)) { timeStyle = bx_sdk_dual.E_TimeStyle.eHH_MM_SS_CHS; return true; } if (DateTime.TryParseExact(text, "HH:mm", CultureInfo.InvariantCulture, DateTimeStyles.None, out _)) { timeStyle = bx_sdk_dual.E_TimeStyle.eHH_MM_COLON; return true; } if (DateTime.TryParseExact(text, "HH时mm分", CultureInfo.InvariantCulture, DateTimeStyles.None, out _)) { timeStyle = bx_sdk_dual.E_TimeStyle.eHH_MM_CHS; return true; } if (DateTime.TryParseExact(text, "yy-MM-dd", CultureInfo.InvariantCulture, DateTimeStyles.None, out _)) { dateStyle = bx_sdk_dual.E_DateStyle.eYYYY_MM_DD_MINUS; return true; } if (DateTime.TryParseExact(text, "yyyy-MM-dd", CultureInfo.InvariantCulture, DateTimeStyles.None, out _)) { dateStyle = bx_sdk_dual.E_DateStyle.eYYYY_MM_DD_MINUS; return true; } if (DateTime.TryParseExact(text, "yyyy年MM月dd日", CultureInfo.InvariantCulture, DateTimeStyles.None, out _)) { dateStyle = bx_sdk_dual.E_DateStyle.eYYYY_MM_DD_CHS; return true; } if (DateTime.TryParseExact(text, "yyyy年MM月dd日HH:mm:ss", CultureInfo.InvariantCulture, DateTimeStyles.None, out _)) { dateStyle = bx_sdk_dual.E_DateStyle.eYYYY_MM_DD_CHS; timeStyle = bx_sdk_dual.E_TimeStyle.eHH_MM_SS_COLON; return true; } if (DateTime.TryParseExact(text, "yyyy年MM月dd日 HH:mm:ss", CultureInfo.InvariantCulture, DateTimeStyles.None, out _)) { dateStyle = bx_sdk_dual.E_DateStyle.eYYYY_MM_DD_CHS; timeStyle = bx_sdk_dual.E_TimeStyle.eHH_MM_SS_COLON; return true; } return false; } private static string ReadValue(string key) { var builder = new StringBuilder(500); GetPrivateProfileString("DotScreen", key, "", builder, 500, configFile); return builder.ToString(); } [DllImport("kernel32")] private static extern int GetPrivateProfileString(string section, string key, string def, StringBuilder retVal, int size, string filePath); [DllImport("kernel32")] private static extern long WritePrivateProfileString(string section, string key, string val, string filePath); } }