using Newtonsoft.Json; using System; using System.Collections.Generic; using System.Linq; using System.Runtime.InteropServices; using System.Text; using System.Threading; using System.Threading.Tasks; namespace MainForm.ClassFile.XiaomiAPI { /// /// 委托-回调方法类型 /// /// /// /// [UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate void CallbackWithDataIdDelegate(string id, string value, string dataId); /// /// 小米 MqttClient类(Request&Response) /// 数据传输采⽤JSON报⽂,编码格式为 UTF-8,严格区分⼤⼩写 /// 日期格式:2022-06-01 14:27:57.283 /// 数据不能包含⾮法字符,如遇换⾏符、回⻋、单双引号、斜杠等需要进⾏转义或特殊处理 /// 设计断⽹重连、数据重传机制 /// public class XiaomiMqttClient { /// /// 设置日志文件保存路径 /// /// 日志文件保存路径 /// [DllImport("DataTransferDll.dll", EntryPoint = "SetLogFileDir", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] public static extern int SetLogFileDir(string logFileDir); #region 打开与关闭 /// /// 1、打开(建立与MqttServer的连接) /// 注:在软件启动时,将MqttServer也随之启动 /// /// IP地址 /// 端口 /// [DllImport("DataTransferDll.dll", EntryPoint = "Open", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] private static extern int Open(string addr, int port); /// /// 1、打开(建立与MqttServer的连接) /// 注:在软件启动时,将MqttServer也随之启动 /// /// IP地址 /// 端口 /// public static int OpenMqttClient(string addr, int port) { int result = Open(addr, port); IsOpen = result == 0; return result; } /// /// 1、打开(建立与MqttServer的连接) /// With 调起 MqttServer /// 注:在软件启动时,将MqttServer也随之启动 /// /// IP地址 /// 端口 /// public static (int, string) OpenWithMqttServer(string addr, int port, string mqttServerPath, string mqttName) { // 检测MqttServer有没有开启,未开启时启动MqttServer if (!ProcessHelper.CheckProcessActivityByProcessName(mqttName)) // 进程名QIMITest.exe { string pathStr1 = mqttServerPath; if (FileIOHelper.ISExists_File(pathStr1)) ProcessHelper.StartProcess(pathStr1); else { throw new Exception($"未找到{mqttServerPath}文件,请文件检查路径!"); } } int result = -999; bool isOk = Task.Run(() => { result = Open(addr, port); }).Wait(10000); if (!isOk) // 无响应 return (-999, "上位机调用Iot的dll无响应[方法名Open]!"); IsOpen = result == 0; return (result, result.ToString()); } /// /// 2、设置回调方法- With DataId(可选,用于记录MqttServer的处理日志) /// /// [DllImport("DataTransferDll.dll", EntryPoint = "SetCallbackWithDataId", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] public static extern void SetCallbackWithDataId(CallbackWithDataIdDelegate callback); /// /// 3、设置参数 -String /// 每次配置参数变更时(修改后点击保存),都需要再次调⽤parameterConfig函数,将新的参数发送给MqttServer /// /// 参数 /// [DllImport("DataTransferDll.dll", EntryPoint = "ParameterConfig", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] public static extern int ParameterConfig(string parameterJson); /// /// 3、设置参数 -结构 /// 每次配置参数变更时(修改后点击保存),都需要再次调⽤parameterConfig函数,将新的参数发送给MqttServer /// /// 参数 /// public static (int, string) ParameterConfig(XiaomiMqttLoginFunAndParam parameter) { string funAndParamJson = JsonConvert.SerializeObject(parameter); int result = -999; bool isOk = Task.Run(() => { result = ParameterConfig(funAndParamJson); }).Wait(10000); if (!isOk) // 无响应 return (-999, "上位机调用Iot的dll无响应[方法名ParameterConfig]!"); return (result, result.ToString()); } /// /// 5、关闭(关闭与MqttServer的连接) /// /// [DllImport("DataTransferDll.dll", EntryPoint = "Close", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] private static extern int Close(); /// /// 5、关闭(关闭与MqttServer的连接) /// /// public static int CloseMqttClient() { int result = Close(); IsOpen = false; return result; } /// /// 5、关闭(关闭与MqttServer的连接) /// With 关闭 MqttServer /// /// IP地址 /// 端口 /// public static int CloseWithMqttServer(string mqttServerPath, string mqttName) { int result = Close(); // 检测MqttServer有没有关闭,未开启时关闭MqttServer if (!ProcessHelper.CheckProcessActivityByProcessName(mqttName)) // 进程名QIMITest.exe { string pathStr1 = mqttServerPath; if (FileIOHelper.ISExists_File(pathStr1)) ProcessHelper.StartProcess(pathStr1); else { throw new Exception($"未找到{mqttServerPath}文件,请文件检查路径!"); } } IsOpen = false; return result; } #endregion 打开与关闭 #region 属性 private static bool _isOpen = false; /// /// 是否已打开 /// public static bool IsOpen { get { return _isOpen; } set { _isOpen = value; } } #endregion 属性 #region 4、发送数据 /// /// 发送数据-英文 /// /// 事件标识;由固定前缀、分类层级(对应层级的英⽂名称)、事件ID/属性ID拼接⽽成 /// 事件数据 /// [DllImport("DataTransferDll.dll", EntryPoint = "Write", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] public static extern int Write(string id, string msg); /// /// 发送数据-中文 /// /// 事件标识;由固定前缀、分类层级(对应层级的英⽂名称)、事件ID/属性ID拼接⽽成 /// 事件数据 /// // 配合使用ToUTF8,发送带中文的value [DllImport("DataTransferDll.dll", EntryPoint = "Write", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] public static extern int Write(string id, byte[] msg); /// /// 发送数据-With DataId /// 英文 /// /// 事件标识;由固定前缀、分类层级(对应层级的英⽂名称)、事件ID/属性ID拼接⽽成 /// 事件数据 /// 自定义事件Id;如:guid /// [DllImport("DataTransferDll.dll", EntryPoint = "WriteWithDataId", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] public static extern int WriteWithDataId(string id, string msg, string dataId); /// /// 发送数据-With DataId /// 中文 /// /// 事件标识;由固定前缀、分类层级(对应层级的英⽂名称)、事件ID/属性ID拼接⽽成 /// 事件数据 /// 自定义事件Id;如:guid /// // 配合使用ToUTF8,发送带中文的value [DllImport("DataTransferDll.dll", EntryPoint = "WriteWithDataId", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] public static extern int WriteWithDataId(string id, byte[] msg, string dataId); /// /// 发送数据-With DeviceCode /// 英文 /// /// 事件标识;由固定前缀、分类层级(对应层级的英⽂名称)、事件ID/属性ID拼接⽽成 /// 事件数据 /// 设备编号;如: /// [DllImport("DataTransferDll.dll", EntryPoint = "WriteWithDeviceCode", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] public static extern int WriteWithDeviceCode(string id, string msg, string deviceCode); /// /// 发送数据-With DeviceCode /// 中文 /// /// 事件标识;由固定前缀、分类层级(对应层级的英⽂名称)、事件ID/属性ID拼接⽽成 /// 事件数据 /// 设备编号;如: /// // 配合使用ToUTF8,发送带中文的value [DllImport("DataTransferDll.dll", EntryPoint = "WriteWithDeviceCode", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] public static extern int WriteWithDeviceCode(string id, byte[] msg, string deviceCode); #endregion 4、发送数据 #region 其他方法 /// /// 转UTF-8 /// /// /// public static byte[] ToUTF8(string text) { byte[] buffer1 = Encoding.Unicode.GetBytes(text); byte[] buffer2 = Encoding.Convert(Encoding.Unicode, Encoding.UTF8, buffer1, 0, buffer1.Length); return buffer2; } #endregion 其他方法 #region 示例 public void Demo() { string ip_addr = "127.0.0.1"; int ip_port = 6666; // 1、连接MQTT工具 Open(ip_addr, ip_port); // 2、设置回调函数 SetCallbackWithDataId(CallbackWithDataId); // 3、参数配置 string parameterJson = "{\r\n \"function\":\"parameterConfig\",\r\n \"redId\":\"123456\",\r\n \"parameter\":{\r\n \"mqtt\":{\r\n \"address\":\"staging-cnbj2-rmq-mqtt.api.xiaomi.com\",\r\n \"port\":\"80\",\r\n \"username\":\"AKMO5BGFQUZL2SBW4X\",\r\n \"password\":\"5AChJjCOYB+No68BpyDocd7uR7cv/foE20RKIpOE\"\r\n },\r\n \"equiment\":{\r\n \"factoryCode\":\"\",\r\n \"deviceCode\":\"DIGITION-TEST\",\r\n \"stationCode\":\"\",\r\n \"project\":\"\",\r\n \"productMode\":\"debug\"\r\n },\r\n \"other\":{\r\n \"logLevel\":\"0\",\r\n \"runMode\":\"online\",\r\n \"uploadDigitalTwinData\":\"true\"\r\n }}}"; ParameterConfig(parameterJson); Thread.Sleep(3000); for (int i = 0; i < 10; i++) { string msg = "{\"beat_tm\":\"2023-03-31 13:54:27.937\",\"signal_name\":\"中文\",\"signal_type\":\"DI\",\"target_status\":true}"; // 4、写入ID、值 int ret = Write("beat_log/control/device_control_signal/DT_motionbeat", ToUTF8(msg)); Console.WriteLine("第{0}次,ret = {1}", i, ret); Thread.Sleep(50); } Console.WriteLine("请按任意键继续..."); Console.ReadKey(); // 5、关闭连接 Close(); } /// /// 回调方法示例- With DataId /// /// /// /// public void CallbackWithDataId(string id, string v, string dataId) { Console.WriteLine("-------CallbackWithDataId-------"); byte[] buffer1 = Encoding.Default.GetBytes(v); byte[] buffer2 = Encoding.Convert(Encoding.UTF8, Encoding.Default, buffer1, 0, buffer1.Length); string strBuffer = Encoding.Default.GetString(buffer2, 0, buffer2.Length); Console.WriteLine("{0} -> {1} {2}", id, strBuffer, dataId); } #endregion 示例 } }