From d21391fb0c4bd102c992aecafcb4d300cffe29f5 Mon Sep 17 00:00:00 2001 From: copper Date: Mon, 23 Jun 2025 16:33:31 +0800 Subject: [PATCH] =?UTF-8?q?=E8=B0=B1=E4=BB=AA=E6=95=B0=E6=8D=AE=E5=A4=84?= =?UTF-8?q?=E7=90=86=E9=80=BB=E8=BE=91=20=E4=B8=8A=E4=BC=A0=E9=80=BB?= =?UTF-8?q?=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Form1.Designer.cs | 486 +++++++++----------- Form1.cs | 1053 ++++++++++++++++++++++++------------------- MqttSign.cs | 56 +++ readme.txt | 94 ++++ sscom-sender.csproj | 4 +- sscom-sender.sln | 24 - 6 files changed, 958 insertions(+), 759 deletions(-) create mode 100644 MqttSign.cs create mode 100644 readme.txt delete mode 100644 sscom-sender.sln diff --git a/Form1.Designer.cs b/Form1.Designer.cs index 1a1e333..ca1d4c3 100644 --- a/Form1.Designer.cs +++ b/Form1.Designer.cs @@ -22,333 +22,273 @@ partial class Form1 #region Windows Form Designer generated code + private System.Windows.Forms.GroupBox groupBoxGPS; + private System.Windows.Forms.ComboBox comboBoxGPSPort; + private System.Windows.Forms.Label labelGPSPort; + private System.Windows.Forms.GroupBox groupBoxSpectrometer; + private System.Windows.Forms.NumericUpDown numericUpDownRefreshInterval; + private System.Windows.Forms.Label labelRefreshInterval; + private System.Windows.Forms.ComboBox comboBoxSpectrometerPort; + private System.Windows.Forms.Label labelSpectrometerPort; + private System.Windows.Forms.GroupBox groupBoxMQTT; + private System.Windows.Forms.TextBox textBoxDeviceSecret; + private System.Windows.Forms.Label labelDeviceSecret; + private System.Windows.Forms.TextBox textBoxDeviceName; + private System.Windows.Forms.Label labelDeviceName; + private System.Windows.Forms.TextBox textBoxTopicRoot; + private System.Windows.Forms.Label labelTopicRoot; + private System.Windows.Forms.TextBox textBoxProductKey; + private System.Windows.Forms.Label labelProductKey; + private System.Windows.Forms.Button buttonStartStop; + private System.Windows.Forms.TextBox textBoxLog; + private System.Windows.Forms.Label labelLog; + /// /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// private void InitializeComponent() { - this.groupBox1 = new System.Windows.Forms.GroupBox(); - this.cmbPort1 = new System.Windows.Forms.ComboBox(); - this.cmbBaud1 = new System.Windows.Forms.ComboBox(); - this.btnConnect1 = new System.Windows.Forms.Button(); - this.lblPort1 = new System.Windows.Forms.Label(); - this.lblBaud1 = new System.Windows.Forms.Label(); - this.groupBox2 = new System.Windows.Forms.GroupBox(); - this.cmbPort2 = new System.Windows.Forms.ComboBox(); - this.cmbBaud2 = new System.Windows.Forms.ComboBox(); - this.btnConnect2 = new System.Windows.Forms.Button(); - this.lblPort2 = new System.Windows.Forms.Label(); - this.lblBaud2 = new System.Windows.Forms.Label(); - this.groupBox3 = new System.Windows.Forms.GroupBox(); - this.txtUrl = new System.Windows.Forms.TextBox(); - this.lblUrl = new System.Windows.Forms.Label(); - this.btnStartUpload = new System.Windows.Forms.Button(); - this.btnStopUpload = new System.Windows.Forms.Button(); - this.groupBox4 = new System.Windows.Forms.GroupBox(); - this.txtLog = new System.Windows.Forms.TextBox(); - this.btnClearLog = new System.Windows.Forms.Button(); - this.btnSaveLog = new System.Windows.Forms.Button(); - this.statusStrip1 = new System.Windows.Forms.StatusStrip(); - this.toolStripStatusLabel1 = new System.Windows.Forms.ToolStripStatusLabel(); - this.groupBox1.SuspendLayout(); - this.groupBox2.SuspendLayout(); - this.groupBox3.SuspendLayout(); - this.groupBox4.SuspendLayout(); - this.statusStrip1.SuspendLayout(); + this.groupBoxGPS = new System.Windows.Forms.GroupBox(); + this.comboBoxGPSPort = new System.Windows.Forms.ComboBox(); + this.labelGPSPort = new System.Windows.Forms.Label(); + this.groupBoxSpectrometer = new System.Windows.Forms.GroupBox(); + this.numericUpDownRefreshInterval = new System.Windows.Forms.NumericUpDown(); + this.labelRefreshInterval = new System.Windows.Forms.Label(); + this.comboBoxSpectrometerPort = new System.Windows.Forms.ComboBox(); + this.labelSpectrometerPort = new System.Windows.Forms.Label(); + this.groupBoxMQTT = new System.Windows.Forms.GroupBox(); + this.textBoxDeviceSecret = new System.Windows.Forms.TextBox(); + this.labelDeviceSecret = new System.Windows.Forms.Label(); + this.textBoxDeviceName = new System.Windows.Forms.TextBox(); + this.labelDeviceName = new System.Windows.Forms.Label(); + this.textBoxTopicRoot = new System.Windows.Forms.TextBox(); + this.labelTopicRoot = new System.Windows.Forms.Label(); + this.textBoxProductKey = new System.Windows.Forms.TextBox(); + this.labelProductKey = new System.Windows.Forms.Label(); + this.buttonStartStop = new System.Windows.Forms.Button(); + this.textBoxLog = new System.Windows.Forms.TextBox(); + this.labelLog = new System.Windows.Forms.Label(); + this.groupBoxGPS.SuspendLayout(); + this.groupBoxSpectrometer.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.numericUpDownRefreshInterval)).BeginInit(); + this.groupBoxMQTT.SuspendLayout(); this.SuspendLayout(); // - // groupBox1 + // groupBoxGPS // - this.groupBox1.Controls.Add(this.lblBaud1); - this.groupBox1.Controls.Add(this.lblPort1); - this.groupBox1.Controls.Add(this.btnConnect1); - this.groupBox1.Controls.Add(this.cmbBaud1); - this.groupBox1.Controls.Add(this.cmbPort1); - this.groupBox1.Location = new System.Drawing.Point(12, 12); - this.groupBox1.Name = "groupBox1"; - this.groupBox1.Size = new System.Drawing.Size(200, 120); - this.groupBox1.TabIndex = 0; - this.groupBox1.TabStop = false; - this.groupBox1.Text = "串口1"; + this.groupBoxGPS.Controls.Add(this.comboBoxGPSPort); + this.groupBoxGPS.Controls.Add(this.labelGPSPort); + this.groupBoxGPS.Location = new System.Drawing.Point(12, 12); + this.groupBoxGPS.Name = "groupBoxGPS"; + this.groupBoxGPS.Size = new System.Drawing.Size(200, 80); + this.groupBoxGPS.TabIndex = 0; + this.groupBoxGPS.TabStop = false; + this.groupBoxGPS.Text = "GPS设置"; // - // cmbPort1 + // comboBoxGPSPort // - this.cmbPort1.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.cmbPort1.FormattingEnabled = true; - this.cmbPort1.Location = new System.Drawing.Point(60, 25); - this.cmbPort1.Name = "cmbPort1"; - this.cmbPort1.Size = new System.Drawing.Size(121, 23); - this.cmbPort1.TabIndex = 0; + this.comboBoxGPSPort.FormattingEnabled = true; + this.comboBoxGPSPort.Location = new System.Drawing.Point(70, 30); + this.comboBoxGPSPort.Name = "comboBoxGPSPort"; + this.comboBoxGPSPort.Size = new System.Drawing.Size(121, 28); + this.comboBoxGPSPort.TabIndex = 1; // - // cmbBaud1 + // labelGPSPort // - this.cmbBaud1.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.cmbBaud1.FormattingEnabled = true; - this.cmbBaud1.Items.AddRange(new object[] { - "9600", - "19200", - "38400", - "57600", - "115200"}); - this.cmbBaud1.Location = new System.Drawing.Point(60, 54); - this.cmbBaud1.Name = "cmbBaud1"; - this.cmbBaud1.Size = new System.Drawing.Size(121, 23); - this.cmbBaud1.TabIndex = 1; + this.labelGPSPort.AutoSize = true; + this.labelGPSPort.Location = new System.Drawing.Point(6, 33); + this.labelGPSPort.Name = "labelGPSPort"; + this.labelGPSPort.Size = new System.Drawing.Size(54, 20); + this.labelGPSPort.TabIndex = 0; + this.labelGPSPort.Text = "串口:"; // - // btnConnect1 + // groupBoxSpectrometer // - this.btnConnect1.Location = new System.Drawing.Point(60, 83); - this.btnConnect1.Name = "btnConnect1"; - this.btnConnect1.Size = new System.Drawing.Size(75, 23); - this.btnConnect1.TabIndex = 2; - this.btnConnect1.Text = "连接"; - this.btnConnect1.UseVisualStyleBackColor = true; - this.btnConnect1.Click += new System.EventHandler(this.btnConnect1_Click); + this.groupBoxSpectrometer.Controls.Add(this.numericUpDownRefreshInterval); + this.groupBoxSpectrometer.Controls.Add(this.labelRefreshInterval); + this.groupBoxSpectrometer.Controls.Add(this.comboBoxSpectrometerPort); + this.groupBoxSpectrometer.Controls.Add(this.labelSpectrometerPort); + this.groupBoxSpectrometer.Location = new System.Drawing.Point(230, 12); + this.groupBoxSpectrometer.Name = "groupBoxSpectrometer"; + this.groupBoxSpectrometer.Size = new System.Drawing.Size(250, 80); + this.groupBoxSpectrometer.TabIndex = 1; + this.groupBoxSpectrometer.TabStop = false; + this.groupBoxSpectrometer.Text = "谱仪设置"; // - // lblPort1 + // numericUpDownRefreshInterval // - this.lblPort1.AutoSize = true; - this.lblPort1.Location = new System.Drawing.Point(15, 28); - this.lblPort1.Name = "lblPort1"; - this.lblPort1.Size = new System.Drawing.Size(32, 15); - this.lblPort1.TabIndex = 3; - this.lblPort1.Text = "端口"; + this.numericUpDownRefreshInterval.Location = new System.Drawing.Point(190, 50); + this.numericUpDownRefreshInterval.Maximum = new decimal(new int[] { 60, 0, 0, 0 }); + this.numericUpDownRefreshInterval.Minimum = new decimal(new int[] { 1, 0, 0, 0 }); + this.numericUpDownRefreshInterval.Name = "numericUpDownRefreshInterval"; + this.numericUpDownRefreshInterval.Size = new System.Drawing.Size(50, 27); + this.numericUpDownRefreshInterval.TabIndex = 3; + this.numericUpDownRefreshInterval.Value = new decimal(new int[] { 1, 0, 0, 0 }); // - // lblBaud1 + // labelRefreshInterval // - this.lblBaud1.AutoSize = true; - this.lblBaud1.Location = new System.Drawing.Point(15, 57); - this.lblBaud1.Name = "lblBaud1"; - this.lblBaud1.Size = new System.Drawing.Size(44, 15); - this.lblBaud1.TabIndex = 4; - this.lblBaud1.Text = "波特率"; + this.labelRefreshInterval.AutoSize = true; + this.labelRefreshInterval.Location = new System.Drawing.Point(6, 52); + this.labelRefreshInterval.Name = "labelRefreshInterval"; + this.labelRefreshInterval.Size = new System.Drawing.Size(178, 20); + this.labelRefreshInterval.TabIndex = 2; + this.labelRefreshInterval.Text = "刷新间隔(秒):"; // - // groupBox2 + // comboBoxSpectrometerPort // - this.groupBox2.Controls.Add(this.lblBaud2); - this.groupBox2.Controls.Add(this.lblPort2); - this.groupBox2.Controls.Add(this.btnConnect2); - this.groupBox2.Controls.Add(this.cmbBaud2); - this.groupBox2.Controls.Add(this.cmbPort2); - this.groupBox2.Location = new System.Drawing.Point(230, 12); - this.groupBox2.Name = "groupBox2"; - this.groupBox2.Size = new System.Drawing.Size(200, 120); - this.groupBox2.TabIndex = 1; - this.groupBox2.TabStop = false; - this.groupBox2.Text = "串口2"; + this.comboBoxSpectrometerPort.FormattingEnabled = true; + this.comboBoxSpectrometerPort.Location = new System.Drawing.Point(70, 20); + this.comboBoxSpectrometerPort.Name = "comboBoxSpectrometerPort"; + this.comboBoxSpectrometerPort.Size = new System.Drawing.Size(121, 28); + this.comboBoxSpectrometerPort.TabIndex = 1; // - // cmbPort2 + // labelSpectrometerPort // - this.cmbPort2.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.cmbPort2.FormattingEnabled = true; - this.cmbPort2.Location = new System.Drawing.Point(60, 25); - this.cmbPort2.Name = "cmbPort2"; - this.cmbPort2.Size = new System.Drawing.Size(121, 23); - this.cmbPort2.TabIndex = 0; + this.labelSpectrometerPort.AutoSize = true; + this.labelSpectrometerPort.Location = new System.Drawing.Point(6, 23); + this.labelSpectrometerPort.Name = "labelSpectrometerPort"; + this.labelSpectrometerPort.Size = new System.Drawing.Size(54, 20); + this.labelSpectrometerPort.TabIndex = 0; + this.labelSpectrometerPort.Text = "串口:"; // - // cmbBaud2 + // groupBoxMQTT // - this.cmbBaud2.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.cmbBaud2.FormattingEnabled = true; - this.cmbBaud2.Items.AddRange(new object[] { - "9600", - "19200", - "38400", - "57600", - "115200"}); - this.cmbBaud2.Location = new System.Drawing.Point(60, 54); - this.cmbBaud2.Name = "cmbBaud2"; - this.cmbBaud2.Size = new System.Drawing.Size(121, 23); - this.cmbBaud2.TabIndex = 1; + this.groupBoxMQTT.Controls.Add(this.textBoxDeviceSecret); + this.groupBoxMQTT.Controls.Add(this.labelDeviceSecret); + this.groupBoxMQTT.Controls.Add(this.textBoxDeviceName); + this.groupBoxMQTT.Controls.Add(this.labelDeviceName); + this.groupBoxMQTT.Controls.Add(this.textBoxTopicRoot); + this.groupBoxMQTT.Controls.Add(this.labelTopicRoot); + this.groupBoxMQTT.Controls.Add(this.textBoxProductKey); + this.groupBoxMQTT.Controls.Add(this.labelProductKey); + this.groupBoxMQTT.Location = new System.Drawing.Point(500, 12); + this.groupBoxMQTT.Name = "groupBoxMQTT"; + this.groupBoxMQTT.Size = new System.Drawing.Size(300, 150); + this.groupBoxMQTT.TabIndex = 2; + this.groupBoxMQTT.TabStop = false; + this.groupBoxMQTT.Text = "MQTT设置"; // - // btnConnect2 + // textBoxDeviceSecret // - this.btnConnect2.Location = new System.Drawing.Point(60, 83); - this.btnConnect2.Name = "btnConnect2"; - this.btnConnect2.Size = new System.Drawing.Size(75, 23); - this.btnConnect2.TabIndex = 2; - this.btnConnect2.Text = "连接"; - this.btnConnect2.UseVisualStyleBackColor = true; - this.btnConnect2.Click += new System.EventHandler(this.btnConnect2_Click); + this.textBoxDeviceSecret.Location = new System.Drawing.Point(100, 115); + this.textBoxDeviceSecret.Name = "textBoxDeviceSecret"; + this.textBoxDeviceSecret.Size = new System.Drawing.Size(190, 27); + this.textBoxDeviceSecret.TabIndex = 7; + this.textBoxDeviceSecret.Text = "1031a49a4f61c29a086f79b41ed971c7"; // - // lblPort2 + // labelDeviceSecret // - this.lblPort2.AutoSize = true; - this.lblPort2.Location = new System.Drawing.Point(15, 28); - this.lblPort2.Name = "lblPort2"; - this.lblPort2.Size = new System.Drawing.Size(32, 15); - this.lblPort2.TabIndex = 3; - this.lblPort2.Text = "端口"; + this.labelDeviceSecret.AutoSize = true; + this.labelDeviceSecret.Location = new System.Drawing.Point(6, 118); + this.labelDeviceSecret.Name = "labelDeviceSecret"; + this.labelDeviceSecret.Size = new System.Drawing.Size(88, 20); + this.labelDeviceSecret.TabIndex = 6; + this.labelDeviceSecret.Text = "设备密钥:"; // - // lblBaud2 + // textBoxDeviceName // - this.lblBaud2.AutoSize = true; - this.lblBaud2.Location = new System.Drawing.Point(15, 57); - this.lblBaud2.Name = "lblBaud2"; - this.lblBaud2.Size = new System.Drawing.Size(44, 15); - this.lblBaud2.TabIndex = 4; - this.lblBaud2.Text = "波特率"; + this.textBoxDeviceName.Location = new System.Drawing.Point(100, 85); + this.textBoxDeviceName.Name = "textBoxDeviceName"; + this.textBoxDeviceName.Size = new System.Drawing.Size(190, 27); + this.textBoxDeviceName.TabIndex = 5; + this.textBoxDeviceName.Text = "PubDevice"; // - // groupBox3 + // labelDeviceName // - this.groupBox3.Controls.Add(this.btnStopUpload); - this.groupBox3.Controls.Add(this.btnStartUpload); - this.groupBox3.Controls.Add(this.lblUrl); - this.groupBox3.Controls.Add(this.txtUrl); - this.groupBox3.Location = new System.Drawing.Point(450, 12); - this.groupBox3.Name = "groupBox3"; - this.groupBox3.Size = new System.Drawing.Size(320, 120); - this.groupBox3.TabIndex = 2; - this.groupBox3.TabStop = false; - this.groupBox3.Text = "数据上传"; + this.labelDeviceName.AutoSize = true; + this.labelDeviceName.Location = new System.Drawing.Point(6, 88); + this.labelDeviceName.Name = "labelDeviceName"; + this.labelDeviceName.Size = new System.Drawing.Size(88, 20); + this.labelDeviceName.TabIndex = 4; + this.labelDeviceName.Text = "设备名称:"; // - // txtUrl + // textBoxTopicRoot // - this.txtUrl.Location = new System.Drawing.Point(15, 40); - this.txtUrl.Name = "txtUrl"; - this.txtUrl.Size = new System.Drawing.Size(290, 23); - this.txtUrl.TabIndex = 0; - this.txtUrl.Text = "http://localhost:8080/api/data"; + this.textBoxTopicRoot.Location = new System.Drawing.Point(100, 55); + this.textBoxTopicRoot.Name = "textBoxTopicRoot"; + this.textBoxTopicRoot.Size = new System.Drawing.Size(190, 27); + this.textBoxTopicRoot.TabIndex = 3; + this.textBoxTopicRoot.Text = "a10inDdCRS6"; // - // lblUrl + // labelTopicRoot // - this.lblUrl.AutoSize = true; - this.lblUrl.Location = new System.Drawing.Point(15, 22); - this.lblUrl.Name = "lblUrl"; - this.lblUrl.Size = new System.Drawing.Size(56, 15); - this.lblUrl.TabIndex = 1; - this.lblUrl.Text = "上传地址"; + this.labelTopicRoot.AutoSize = true; + this.labelTopicRoot.Location = new System.Drawing.Point(6, 58); + this.labelTopicRoot.Name = "labelTopicRoot"; + this.labelTopicRoot.Size = new System.Drawing.Size(88, 20); + this.labelTopicRoot.TabIndex = 2; + this.labelTopicRoot.Text = "主题根路径:"; // - // btnStartUpload + // textBoxProductKey // - this.btnStartUpload.Location = new System.Drawing.Point(15, 80); - this.btnStartUpload.Name = "btnStartUpload"; - this.btnStartUpload.Size = new System.Drawing.Size(75, 23); - this.btnStartUpload.TabIndex = 2; - this.btnStartUpload.Text = "开始上传"; - this.btnStartUpload.UseVisualStyleBackColor = true; - this.btnStartUpload.Click += new System.EventHandler(this.btnStartUpload_Click); + this.textBoxProductKey.Location = new System.Drawing.Point(100, 25); + this.textBoxProductKey.Name = "textBoxProductKey"; + this.textBoxProductKey.Size = new System.Drawing.Size(190, 27); + this.textBoxProductKey.TabIndex = 1; + this.textBoxProductKey.Text = "gfcq950RDqt"; // - // btnStopUpload + // labelProductKey // - this.btnStopUpload.Location = new System.Drawing.Point(110, 80); - this.btnStopUpload.Name = "btnStopUpload"; - this.btnStopUpload.Size = new System.Drawing.Size(75, 23); - this.btnStopUpload.TabIndex = 3; - this.btnStopUpload.Text = "停止上传"; - this.btnStopUpload.UseVisualStyleBackColor = true; - this.btnStopUpload.Click += new System.EventHandler(this.btnStopUpload_Click); + this.labelProductKey.AutoSize = true; + this.labelProductKey.Location = new System.Drawing.Point(6, 28); + this.labelProductKey.Name = "labelProductKey"; + this.labelProductKey.Size = new System.Drawing.Size(88, 20); + this.labelProductKey.TabIndex = 0; + this.labelProductKey.Text = "产品密钥:"; // - // groupBox4 + // buttonStartStop // - this.groupBox4.Controls.Add(this.btnSaveLog); - this.groupBox4.Controls.Add(this.btnClearLog); - this.groupBox4.Controls.Add(this.txtLog); - this.groupBox4.Location = new System.Drawing.Point(12, 150); - this.groupBox4.Name = "groupBox4"; - this.groupBox4.Size = new System.Drawing.Size(758, 270); - this.groupBox4.TabIndex = 3; - this.groupBox4.TabStop = false; - this.groupBox4.Text = "数据日志"; + this.buttonStartStop.Font = new System.Drawing.Font("Microsoft YaHei UI", 12F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point); + this.buttonStartStop.Location = new System.Drawing.Point(12, 110); + this.buttonStartStop.Name = "buttonStartStop"; + this.buttonStartStop.Size = new System.Drawing.Size(120, 50); + this.buttonStartStop.TabIndex = 3; + this.buttonStartStop.Text = "开始"; + this.buttonStartStop.UseVisualStyleBackColor = true; + this.buttonStartStop.Click += new System.EventHandler(this.buttonStartStop_Click); // - // txtLog + // textBoxLog // - this.txtLog.Location = new System.Drawing.Point(15, 22); - this.txtLog.Multiline = true; - this.txtLog.Name = "txtLog"; - this.txtLog.ReadOnly = true; - this.txtLog.ScrollBars = System.Windows.Forms.ScrollBars.Vertical; - this.txtLog.Size = new System.Drawing.Size(728, 210); - this.txtLog.TabIndex = 0; + this.textBoxLog.Location = new System.Drawing.Point(12, 200); + this.textBoxLog.Multiline = true; + this.textBoxLog.Name = "textBoxLog"; + this.textBoxLog.ReadOnly = true; + this.textBoxLog.ScrollBars = System.Windows.Forms.ScrollBars.Vertical; + this.textBoxLog.Size = new System.Drawing.Size(788, 250); + this.textBoxLog.TabIndex = 4; // - // btnClearLog + // labelLog // - this.btnClearLog.Location = new System.Drawing.Point(15, 238); - this.btnClearLog.Name = "btnClearLog"; - this.btnClearLog.Size = new System.Drawing.Size(75, 23); - this.btnClearLog.TabIndex = 1; - this.btnClearLog.Text = "清空日志"; - this.btnClearLog.UseVisualStyleBackColor = true; - this.btnClearLog.Click += new System.EventHandler(this.btnClearLog_Click); - // - // btnSaveLog - // - this.btnSaveLog.Location = new System.Drawing.Point(110, 238); - this.btnSaveLog.Name = "btnSaveLog"; - this.btnSaveLog.Size = new System.Drawing.Size(75, 23); - this.btnSaveLog.TabIndex = 2; - this.btnSaveLog.Text = "保存日志"; - this.btnSaveLog.UseVisualStyleBackColor = true; - this.btnSaveLog.Click += new System.EventHandler(this.btnSaveLog_Click); - // - // statusStrip1 - // - this.statusStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { - this.toolStripStatusLabel1}); - this.statusStrip1.Location = new System.Drawing.Point(0, 428); - this.statusStrip1.Name = "statusStrip1"; - this.statusStrip1.Size = new System.Drawing.Size(784, 22); - this.statusStrip1.TabIndex = 4; - this.statusStrip1.Text = "statusStrip1"; - // - // toolStripStatusLabel1 - // - this.toolStripStatusLabel1.Name = "toolStripStatusLabel1"; - this.toolStripStatusLabel1.Size = new System.Drawing.Size(32, 17); - this.toolStripStatusLabel1.Text = "就绪"; + this.labelLog.AutoSize = true; + this.labelLog.Location = new System.Drawing.Point(12, 177); + this.labelLog.Name = "labelLog"; + this.labelLog.Size = new System.Drawing.Size(69, 20); + this.labelLog.TabIndex = 5; + this.labelLog.Text = "数据日志"; // // Form1 // - this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F); + this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 20F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.ClientSize = new System.Drawing.Size(784, 450); - this.Controls.Add(this.statusStrip1); - this.Controls.Add(this.groupBox4); - this.Controls.Add(this.groupBox3); - this.Controls.Add(this.groupBox2); - this.Controls.Add(this.groupBox1); - this.Name = "Form1"; - this.Text = "串口数据采集与上传系统"; - this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.Form1_FormClosing); - this.Load += new System.EventHandler(this.Form1_Load); - this.groupBox1.ResumeLayout(false); - this.groupBox1.PerformLayout(); - this.groupBox2.ResumeLayout(false); - this.groupBox2.PerformLayout(); - this.groupBox3.ResumeLayout(false); - this.groupBox3.PerformLayout(); - this.groupBox4.ResumeLayout(false); - this.groupBox4.PerformLayout(); - this.statusStrip1.ResumeLayout(false); - this.statusStrip1.PerformLayout(); + this.ClientSize = new System.Drawing.Size(820, 470); + this.Controls.Add(this.labelLog); + this.Controls.Add(this.textBoxLog); + this.Controls.Add(this.buttonStartStop); + this.Controls.Add(this.groupBoxMQTT); + this.Controls.Add(this.groupBoxSpectrometer); + this.Controls.Add(this.groupBoxGPS); + this.Text = "串口数据采集与MQTT发送"; + this.groupBoxGPS.ResumeLayout(false); + this.groupBoxGPS.PerformLayout(); + this.groupBoxSpectrometer.ResumeLayout(false); + this.groupBoxSpectrometer.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)(this.numericUpDownRefreshInterval)).EndInit(); + this.groupBoxMQTT.ResumeLayout(false); + this.groupBoxMQTT.PerformLayout(); this.ResumeLayout(false); this.PerformLayout(); } #endregion - - private System.Windows.Forms.GroupBox groupBox1; - private System.Windows.Forms.ComboBox cmbPort1; - private System.Windows.Forms.ComboBox cmbBaud1; - private System.Windows.Forms.Button btnConnect1; - private System.Windows.Forms.Label lblPort1; - private System.Windows.Forms.Label lblBaud1; - private System.Windows.Forms.GroupBox groupBox2; - private System.Windows.Forms.ComboBox cmbPort2; - private System.Windows.Forms.ComboBox cmbBaud2; - private System.Windows.Forms.Button btnConnect2; - private System.Windows.Forms.Label lblPort2; - private System.Windows.Forms.Label lblBaud2; - private System.Windows.Forms.GroupBox groupBox3; - private System.Windows.Forms.TextBox txtUrl; - private System.Windows.Forms.Label lblUrl; - private System.Windows.Forms.Button btnStartUpload; - private System.Windows.Forms.Button btnStopUpload; - private System.Windows.Forms.GroupBox groupBox4; - private System.Windows.Forms.TextBox txtLog; - private System.Windows.Forms.Button btnClearLog; - private System.Windows.Forms.Button btnSaveLog; - private System.Windows.Forms.StatusStrip statusStrip1; - private System.Windows.Forms.ToolStripStatusLabel toolStripStatusLabel1; } diff --git a/Form1.cs b/Form1.cs index b147745..fcf7531 100644 --- a/Form1.cs +++ b/Form1.cs @@ -1,461 +1,592 @@ -using System.Collections.Concurrent; -using System.IO.Ports; -using System.Text; -using System.Text.Json; -using System.Text.RegularExpressions; - -namespace sscom_sender -{ - public partial class Form1 : Form - { - // 串口对象 - private SerialPort? serialPort1; - private SerialPort? serialPort2; - - // 数据缓冲区 - private StringBuilder buffer1 = new StringBuilder(); - private StringBuilder buffer2 = new StringBuilder(); - - // 数据队列 - private ConcurrentQueue dataQueue = new ConcurrentQueue(); - - // 线程控制 - private CancellationTokenSource? uploadCancellationTokenSource; - private CancellationTokenSource? logCancellationTokenSource; - private Task? uploadTask; - private Task? logTask; - - // HTTP客户端 - private readonly HttpClient httpClient = new HttpClient(); - - // 日志文件路径 - private readonly string logFilePath = Path.Combine(Application.StartupPath, "data_log.txt"); - - // 线程安全的日志队列 - private ConcurrentQueue logQueue = new ConcurrentQueue(); - - public Form1() - { - InitializeComponent(); - } - - private void Form1_Load(object sender, EventArgs e) - { - // 初始化串口列表 - RefreshPortList(); - - // 设置默认波特率 - cmbBaud1.SelectedIndex = 4; // 115200 - cmbBaud2.SelectedIndex = 4; // 115200 - - // 启动日志线程 - StartLogThread(); - - UpdateStatus("系统已启动"); - } - - private void RefreshPortList() - { - string[] ports = SerialPort.GetPortNames(); - - cmbPort1.Items.Clear(); - cmbPort2.Items.Clear(); - - foreach (string port in ports) - { - cmbPort1.Items.Add(port); - cmbPort2.Items.Add(port); - } - - if (ports.Length > 0) - { - cmbPort1.SelectedIndex = 0; - if (ports.Length > 1) - cmbPort2.SelectedIndex = 1; - else - cmbPort2.SelectedIndex = 0; - } - } - - private void btnConnect1_Click(object sender, EventArgs e) - { - if (serialPort1?.IsOpen == true) - { - // 断开连接 - serialPort1.Close(); - btnConnect1.Text = "连接"; - UpdateStatus("串口1已断开"); - } - else - { - // 建立连接 - try - { - serialPort1 = new SerialPort( - cmbPort1.Text, - int.Parse(cmbBaud1.Text), - Parity.None, - 8, - StopBits.One); - - serialPort1.DataReceived += SerialPort1_DataReceived; - serialPort1.Open(); - - btnConnect1.Text = "断开"; - UpdateStatus($"串口1已连接: {cmbPort1.Text}"); - } - catch (Exception ex) - { - MessageBox.Show($"串口1连接失败: {ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error); - } - } - } - - private void btnConnect2_Click(object sender, EventArgs e) - { - if (serialPort2?.IsOpen == true) - { - // 断开连接 - serialPort2.Close(); - btnConnect2.Text = "连接"; - UpdateStatus("串口2已断开"); - } - else - { - // 建立连接 - try - { - serialPort2 = new SerialPort( - cmbPort2.Text, - int.Parse(cmbBaud2.Text), - Parity.None, - 8, - StopBits.One); - - serialPort2.DataReceived += SerialPort2_DataReceived; - serialPort2.Open(); - - btnConnect2.Text = "断开"; - UpdateStatus($"串口2已连接: {cmbPort2.Text}"); - } - catch (Exception ex) - { - MessageBox.Show($"串口2连接失败: {ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error); - } - } - } - - private void SerialPort1_DataReceived(object sender, SerialDataReceivedEventArgs e) - { - if (serialPort1?.IsOpen == true) - { - string data = serialPort1.ReadExisting(); - ProcessSerialData(data, 1, buffer1); - } - } - - private void SerialPort2_DataReceived(object sender, SerialDataReceivedEventArgs e) - { - if (serialPort2?.IsOpen == true) - { - string data = serialPort2.ReadExisting(); - ProcessSerialData(data, 2, buffer2); - } - } - - private void ProcessSerialData(string data, int portNumber, StringBuilder buffer) - { - // 将数据添加到缓冲区 - buffer.Append(data); - - // 查找完整的数据包(以换行符结束) - string bufferContent = buffer.ToString(); - string[] lines = bufferContent.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries); - - if (lines.Length > 0) - { - // 处理完整的行 - for (int i = 0; i < lines.Length - 1; i++) - { - ProcessCompleteDataLine(lines[i], portNumber); - } - - // 检查最后一行是否完整 - if (bufferContent.EndsWith("\r") || bufferContent.EndsWith("\n")) - { - ProcessCompleteDataLine(lines[lines.Length - 1], portNumber); - buffer.Clear(); - } - else - { - // 保留不完整的最后一行 - buffer.Clear(); - buffer.Append(lines[lines.Length - 1]); - } - } - } - - private void ProcessCompleteDataLine(string line, int portNumber) - { - if (string.IsNullOrWhiteSpace(line)) return; - - var dataPacket = new DataPacket - { - PortNumber = portNumber, - RawData = line, - Timestamp = DateTime.Now - }; - - // 如果是串口1且数据格式匹配BESTPOS,提取经纬度 - if (portNumber == 1 && line.Contains("BESTPOSA")) - { - var coordinates = ExtractCoordinates(line); - if (coordinates != null) - { - dataPacket.Latitude = coordinates.Value.Latitude; - dataPacket.Longitude = coordinates.Value.Longitude; - } - } - - // 添加到队列 - dataQueue.Enqueue(dataPacket); - - // 添加到日志显示 - string logMessage = $"[{dataPacket.Timestamp:yyyy-MM-dd HH:mm:ss}] 串口{portNumber}: {line}"; - if (dataPacket.Latitude.HasValue && dataPacket.Longitude.HasValue) - { - logMessage += $" [经度: {dataPacket.Longitude:F8}, 纬度: {dataPacket.Latitude:F8}]"; - } - - AddLogMessage(logMessage); - } - - private (double Latitude, double Longitude)? ExtractCoordinates(string data) - { - try - { - // BESTPOS数据格式解析 - // #BESTPOSA,COM1,14394,98.0,UNKNOWN,1,1699.000,1700908,2,18;INSUFFICIENT_OBS,NONE,纬度,经度,高度,undulation,datum,lat_std,lon_std,hgt_std,stn_id,diff_age,sol_age,#obs,#L1,#L2,reserved,ext_sol_stat,galileo_beidou_sig_mask,gps_glonass_sig_mask*checksum - string[] parts = data.Split(','); - if (parts.Length >= 14) - { - if (double.TryParse(parts[11], out double latitude) && - double.TryParse(parts[12], out double longitude)) - { - return (latitude, longitude); - } - } - } - catch (Exception ex) - { - AddLogMessage($"坐标解析错误: {ex.Message}"); - } - return null; - } - - private void btnStartUpload_Click(object sender, EventArgs e) - { - if (string.IsNullOrWhiteSpace(txtUrl.Text)) - { - MessageBox.Show("请输入上传地址", "提示", MessageBoxButtons.OK, MessageBoxIcon.Warning); - return; - } - - uploadCancellationTokenSource = new CancellationTokenSource(); - uploadTask = Task.Run(() => UploadWorker(uploadCancellationTokenSource.Token)); - - btnStartUpload.Enabled = false; - btnStopUpload.Enabled = true; - UpdateStatus("数据上传已启动"); - } - - private void btnStopUpload_Click(object sender, EventArgs e) - { - uploadCancellationTokenSource?.Cancel(); - btnStartUpload.Enabled = true; - btnStopUpload.Enabled = false; - UpdateStatus("数据上传已停止"); - } - - private async Task UploadWorker(CancellationToken cancellationToken) - { - while (!cancellationToken.IsCancellationRequested) - { - try - { - if (dataQueue.TryDequeue(out DataPacket? packet)) - { - await UploadData(packet); - } - else - { - await Task.Delay(100, cancellationToken); // 等待100ms - } - } - catch (OperationCanceledException) - { - break; - } - catch (Exception ex) - { - Invoke(() => AddLogMessage($"上传错误: {ex.Message}")); - await Task.Delay(5000, cancellationToken); // 错误后等待5秒 - } - } - } - - private async Task UploadData(DataPacket packet) - { - try - { - var jsonData = JsonSerializer.Serialize(packet); - var content = new StringContent(jsonData, Encoding.UTF8, "application/json"); - - var response = await httpClient.PostAsync(txtUrl.Text, content); - - if (response.IsSuccessStatusCode) - { - Invoke(() => AddLogMessage($"数据上传成功: 串口{packet.PortNumber}")); - } - else - { - Invoke(() => AddLogMessage($"数据上传失败: {response.StatusCode} - 串口{packet.PortNumber}")); - } - } - catch (Exception ex) - { - Invoke(() => AddLogMessage($"上传异常: {ex.Message}")); - } - } - - private void StartLogThread() - { - logCancellationTokenSource = new CancellationTokenSource(); - logTask = Task.Run(() => LogWorker(logCancellationTokenSource.Token)); - } - - private async Task LogWorker(CancellationToken cancellationToken) - { - while (!cancellationToken.IsCancellationRequested) - { - try - { - var logsToWrite = new List(); - - // 批量获取日志 - while (logQueue.TryDequeue(out string? logMessage)) - { - logsToWrite.Add(logMessage); - if (logsToWrite.Count >= 10) break; // 批量写入,最多10条 - } - - if (logsToWrite.Count > 0) - { - await File.AppendAllLinesAsync(logFilePath, logsToWrite, cancellationToken); - } - else - { - await Task.Delay(1000, cancellationToken); // 等待1秒 - } - } - catch (OperationCanceledException) - { - break; - } - catch (Exception ex) - { - // 日志写入错误,避免无限循环 - Console.WriteLine($"日志写入错误: {ex.Message}"); - await Task.Delay(5000, cancellationToken); - } - } - } - - private void AddLogMessage(string message) - { - // 添加到界面显示 - if (InvokeRequired) - { - Invoke(() => AddLogMessage(message)); - return; - } - - txtLog.AppendText(message + Environment.NewLine); - txtLog.SelectionStart = txtLog.Text.Length; - txtLog.ScrollToCaret(); - - // 添加到日志文件队列 - logQueue.Enqueue(message); - - // 限制界面显示的行数 - if (txtLog.Lines.Length > 1000) - { - var lines = txtLog.Lines.Skip(500).ToArray(); - txtLog.Text = string.Join(Environment.NewLine, lines); - } - } - - private void UpdateStatus(string message) - { - if (InvokeRequired) - { - Invoke(() => UpdateStatus(message)); - return; - } - - toolStripStatusLabel1.Text = message; - AddLogMessage($"[系统] {message}"); - } - - private void btnClearLog_Click(object sender, EventArgs e) - { - txtLog.Clear(); - } - - private void btnSaveLog_Click(object sender, EventArgs e) - { - try - { - using (var saveDialog = new SaveFileDialog()) - { - saveDialog.Filter = "文本文件|*.txt|所有文件|*.*"; - saveDialog.FileName = $"log_{DateTime.Now:yyyyMMdd_HHmmss}.txt"; - - if (saveDialog.ShowDialog() == DialogResult.OK) - { - File.WriteAllText(saveDialog.FileName, txtLog.Text); - MessageBox.Show("日志保存成功", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information); - } - } - } - catch (Exception ex) - { - MessageBox.Show($"保存失败: {ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error); - } - } - - private void Form1_FormClosing(object sender, FormClosingEventArgs e) - { - // 停止所有线程 - uploadCancellationTokenSource?.Cancel(); - logCancellationTokenSource?.Cancel(); - - // 关闭串口 - serialPort1?.Close(); - serialPort2?.Close(); - - // 释放资源 - httpClient.Dispose(); - } - } - - // 数据包类 - public class DataPacket - { - public int PortNumber { get; set; } - public string RawData { get; set; } = string.Empty; - public DateTime Timestamp { get; set; } - public double? Latitude { get; set; } - public double? Longitude { get; set; } - } -} +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Drawing; +using System.IO; +using System.IO.Ports; +using System.Linq; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using System.Windows.Forms; +using uPLibrary.Networking.M2Mqtt; +using uPLibrary.Networking.M2Mqtt.Messages; +using Newtonsoft.Json; +using aiot_paho_csharp; + +namespace sscom_sender +{ + public partial class Form1 : Form + { + private SerialPort gpsPort; + private SerialPort spectrometerPort; + private MqttClient mqttClient; + private bool isRunning = false; + private string logFileName; + + // 数据队列 + private ConcurrentQueue gpsDataQueue = new ConcurrentQueue(); + private ConcurrentQueue spectrometerDataQueue = new ConcurrentQueue(); + private ConcurrentQueue logQueue = new ConcurrentQueue(); + + // 线程控制 + private CancellationTokenSource cancellationTokenSource; + private Task gpsTask; + private Task spectrometerTask; + private Task mqttTask; + private Task logTask; + + // 数据缓冲区 + private StringBuilder gpsBuffer = new StringBuilder(); + private StringBuilder spectrometerBuffer = new StringBuilder(); + + public Form1() + { + InitializeComponent(); + InitializeForm(); + } + + private void InitializeForm() + { + // 初始化串口列表 + RefreshSerialPorts(); + + // 创建日志文件名 + logFileName = Path.Combine(Application.StartupPath, "logs", $"log_{DateTime.Now:yyyyMMdd_HHmmss}.txt"); + Directory.CreateDirectory(Path.GetDirectoryName(logFileName)); + } + + private void RefreshSerialPorts() + { + string[] ports = SerialPort.GetPortNames(); + comboBoxGPSPort.Items.Clear(); + comboBoxSpectrometerPort.Items.Clear(); + + foreach (string port in ports) + { + comboBoxGPSPort.Items.Add(port); + comboBoxSpectrometerPort.Items.Add(port); + } + + if (ports.Length > 0) + { + comboBoxGPSPort.SelectedIndex = 0; + if (ports.Length > 1) + comboBoxSpectrometerPort.SelectedIndex = 1; + else + comboBoxSpectrometerPort.SelectedIndex = 0; + } + } + + private void buttonStartStop_Click(object sender, EventArgs e) + { + if (!isRunning) + { + StartDataCollection(); + } + else + { + StopDataCollection(); + } + } + + private void StartDataCollection() + { + try + { + // 初始化串口 + InitializeSerialPorts(); + + // 启动线程 + cancellationTokenSource = new CancellationTokenSource(); + var token = cancellationTokenSource.Token; + + gpsTask = Task.Run(() => GPSDataReader(token), token); + spectrometerTask = Task.Run(() => SpectrometerDataReader(token), token); + mqttTask = Task.Run(() => MQTTDataSender(token), token); + logTask = Task.Run(() => LogWriter(token), token); + + isRunning = true; + buttonStartStop.Text = "停止"; + buttonStartStop.BackColor = Color.Red; + + LogMessage("数据采集已启动"); + } + catch (Exception ex) + { + MessageBox.Show($"启动失败: {ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error); + } + } + + private void StopDataCollection() + { + try + { + isRunning = false; + cancellationTokenSource?.Cancel(); + + // 立即更新UI状态 + buttonStartStop.Text = "开始"; + buttonStartStop.BackColor = SystemColors.Control; + LogMessage("正在停止数据采集..."); + + // 异步等待任务完成,避免阻塞UI线程 + Task.Run(() => + { + try + { + // 给任务一些时间优雅退出 + var tasks = new[] { gpsTask, spectrometerTask, mqttTask, logTask }.Where(t => t != null).ToArray(); + if (tasks.Length > 0) + { + Task.WaitAll(tasks, 2000); // 减少等待时间到2秒 + } + + // 关闭串口 + try { gpsPort?.Close(); } catch { } + try { spectrometerPort?.Close(); } catch { } + + // 断开MQTT + try { mqttClient?.Disconnect(); } catch { } + + // 在UI线程上更新状态 + this.Invoke(new Action(() => + { + LogMessage("数据采集已停止"); + })); + } + catch (Exception ex) + { + this.Invoke(new Action(() => + { + LogMessage($"停止时发生错误: {ex.Message}"); + })); + } + }); + } + catch (Exception ex) + { + LogMessage($"停止时发生错误: {ex.Message}"); + } + } + + private void InitializeSerialPorts() + { + // GPS串口 + if (comboBoxGPSPort.SelectedItem != null) + { + gpsPort = new SerialPort(comboBoxGPSPort.SelectedItem.ToString(), 115200, Parity.None, 8, StopBits.One); + gpsPort.DataReceived += GPSPort_DataReceived; + gpsPort.Open(); + + // 发送GPS初始化命令 + gpsPort.WriteLine("bestposa com1 1"); + } + + // 谱仪串口 + if (comboBoxSpectrometerPort.SelectedItem != null) + { + + spectrometerPort = new SerialPort(comboBoxSpectrometerPort.SelectedItem.ToString(), 115200, Parity.None, 8, StopBits.One); + spectrometerPort.DataReceived += SpectrometerPort_DataReceived; + spectrometerPort.Open(); + if (!spectrometerPort.IsOpen) { + LogMessage("谱仪端口打开失败"); + return; + } + LogMessage("谱仪端口已打开"); + // 发送谱仪开始命令 + spectrometerPort.DiscardInBuffer(); + spectrometerPort.Write("$start\r\n"); + spectrometerPort.ReadTimeout = 1500; + } + } + + private void InitializeMQTT() + { + try + { + string productKey = textBoxProductKey.Text; + string topicRoot = textBoxTopicRoot.Text; + string deviceName = textBoxDeviceName.Text; + string deviceSecret = textBoxDeviceSecret.Text; + + MqttSign sign = new MqttSign(); + sign.calculate(productKey, deviceName, deviceSecret); + + string broker = productKey + ".iot-as-mqtt.cn-shanghai.aliyuncs.com"; + mqttClient = new MqttClient(broker, 1883, true, null, null, MqttSslProtocols.TLSv1_2); + mqttClient.Connect(sign.getClientid(), sign.getUsername(), sign.getPassword()); + + LogMessage($"MQTT已连接到: {broker}"); + } + catch (Exception ex) + { + LogMessage($"MQTT连接失败: {ex.Message},将继续进行数据采集和日志记录"); + mqttClient = null; // 确保mqttClient为null,避免后续使用 + } + } + + private void GPSPort_DataReceived(object sender, SerialDataReceivedEventArgs e) + { + try + { + string data = gpsPort.ReadExisting(); + gpsBuffer.Append(data); + + // 处理完整的行 + string bufferContent = gpsBuffer.ToString(); + string[] lines = bufferContent.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries); + + if (bufferContent.EndsWith("\r") || bufferContent.EndsWith("\n")) + { + gpsBuffer.Clear(); + foreach (string line in lines) + { + ProcessGPSData(line); + } + } + else + { + gpsBuffer.Clear(); + for (int i = 0; i < lines.Length - 1; i++) + { + ProcessGPSData(lines[i]); + } + gpsBuffer.Append(lines[lines.Length - 1]); + } + } + catch (Exception ex) + { + LogMessage($"GPS数据接收错误: {ex.Message}"); + } + } + + private void SpectrometerPort_DataReceived(object sender, SerialDataReceivedEventArgs e) + { + try + { + string data = spectrometerPort.ReadExisting(); + // LogMessage($"谱仪原始数据: {data}"); + spectrometerBuffer.Append(data); + + // 处理完整的行 + string bufferContent = spectrometerBuffer.ToString(); + string[] lines = bufferContent.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries); + + if (bufferContent.EndsWith("\r") || bufferContent.EndsWith("\n")) + { + spectrometerBuffer.Clear(); + foreach (string line in lines) + { + ProcessSpectrometerData(line); + } + } + else + { + spectrometerBuffer.Clear(); + for (int i = 0; i < lines.Length - 1; i++) + { + ProcessSpectrometerData(lines[i]); + } + spectrometerBuffer.Append(lines[lines.Length - 1]); + } + } + catch (Exception ex) + { + LogMessage($"谱仪数据接收错误: {ex.Message}"); + } + } + + private void ProcessGPSData(string data) + { + LogMessage($"GPS原始数据: {data}"); + if (data.StartsWith("#BESTPOSA")) + { + try + { + string[] parts = data.Split(','); + // 查找WGS84标识的位置 + // LogMessage($"GPS数据部分数量: {parts.Length}"); + int wgs84Index = -1; + for (int i = 0; i < parts.Length; i++) + { + // LogMessage($"GPS数据部分: {parts[i]}"); + if (parts[i].Contains("WGS84")) + { + wgs84Index = i; + break; + } + } + // LogMessage($"WGS84索引: {wgs84Index}"); + + // WGS84后面的两个字段是经纬度 + if (wgs84Index >= 0 && parts.Length > wgs84Index + 2) + { + double lat = double.Parse(parts[wgs84Index + 1]); + double lng = double.Parse(parts[wgs84Index + 2]); + + GPSData gpsData = new GPSData + { + Latitude = lat, + Longitude = lng, + Timestamp = DateTime.Now + }; + + gpsDataQueue.Enqueue(gpsData); + LogMessage($"GPS数据: 纬度={lat:F8}, 经度={lng:F8}"); + + } + } + catch (Exception ex) + { + LogMessage($"GPS数据解析错误: {ex.Message}"); + } + } + } + + private void ProcessSpectrometerData(string data) + { + LogMessage($"谱仪原始数据: {data}"); + if (data.Equals("ok")) { + LogMessage("谱仪已准备好"); + return; + } + try + { + string[] parts = data.Split(' '); + // 检查是否为完整的反馈格式:16个字段(日期 时间 + 14个数值) + if (parts.Length == 16) + { + // 验证日期格式 xxxx.xx.xx + if (!System.Text.RegularExpressions.Regex.IsMatch(parts[0], @"^\d{4}\.\d{2}\.\d{2}$")) + return; + + // 验证时间格式 xx:xx:xx + if (!System.Text.RegularExpressions.Regex.IsMatch(parts[1], @"^\d{2}:\d{2}:\d{2}$")) + return; + + // 解析时间戳 (xxxx.xx.xx xx:xx:xx) + string dateStr = parts[0]; + string timeStr = parts[1]; + + SpectrometerData spectData = new SpectrometerData + { + Timestamp = DateTime.Now, // 使用当前时间,也可以解析返回的时间 + RealTime = double.Parse(parts[2]), // aaaaaaa.aa 实时间 + DeadTime = double.Parse(parts[3]), // bbbbbbb.bb 死时间 + DoseRate = double.Parse(parts[14]), // JJJJJJ.JJ 环境总辐射剂量率值(第15个字段) + TotalCountRate = double.Parse(parts[15]) // cccccccccc.cc γ谱的全谱计数率(第16个字段) + }; + + spectrometerDataQueue.Enqueue(spectData); + LogMessage($"谱仪数据: 时间={dateStr} {timeStr}, 剂量率={spectData.DoseRate:F2} nSv/h, 全谱计数率={spectData.TotalCountRate:F2} cps"); + } + } + catch (Exception ex) + { + LogMessage($"谱仪数据解析错误: {ex.Message}, 数据: {data}"); + } + + } + + private async void GPSDataReader(CancellationToken token) + { + while (!token.IsCancellationRequested && isRunning) + { + await Task.Delay(1000, token); + } + } + + private async void SpectrometerDataReader(CancellationToken token) + { + while (!token.IsCancellationRequested && isRunning) + { + try + { + LogMessage("尝试读取谱仪数据"); + if (spectrometerPort != null && spectrometerPort.IsOpen) + { + // spectrometerPort.DiscardInBuffer(); + // spectrometerPort.Write("$start\r\n"); + // await Task.Delay(50, token); // 等待刷新完成 + // // spectrometerPort.ReadTimeout = 1500; + // // string response = spectrometerPort.ReadLine(); + // // LogMessage($"谱仪原始数据: {response}"); + // spectrometerPort.DiscardInBuffer(); + // spectrometerPort.Write("$start\r"); + // await Task.Delay(50, token); // 等待刷新完成 + // spectrometerPort.ReadTimeout = 1500; + // response = spectrometerPort.ReadLine(); + // LogMessage($"谱仪原始数据: {response}"); + + // response = spectrometerPort.ReadLine(); + // LogMessage($"谱仪原始数据: {response}"); + // spectrometerPort.DiscardInBuffer(); + // spectrometerPort.Write("$start\r\n\r\n"); + // spectrometerPort.ReadTimeout = 1500; + // response = spectrometerPort.ReadLine(); + // LogMessage($"谱仪原始数据: {response}"); + // await Task.Delay(50, token); // 等待刷新完成 + LogMessage("谱仪端口已打开"); + // 先发送刷新指令 + spectrometerPort.Write("$refresh\r\n"); + await Task.Delay(500, token); // 等待刷新完成 + + // 再发送获取结果指令 + spectrometerPort.Write("$getSperesult\r\n"); + } + + int interval = (int)(numericUpDownRefreshInterval.Value * 1000); + await Task.Delay(interval, token); + } + catch (Exception ex) + { + LogMessage($"谱仪数据读取错误: {ex.Message}"); + await Task.Delay(5000, token); + } + } + } + + private async void MQTTDataSender(CancellationToken token) + { + // 在数据发送线程中初始化MQTT连接 + InitializeMQTT(); + + while (!token.IsCancellationRequested && isRunning) + { + try + { + if (spectrometerDataQueue.TryDequeue(out SpectrometerData spectData)) + { + // 查找最近的GPS数据 + GPSData nearestGPS = FindNearestGPSData(spectData.Timestamp); + + if (nearestGPS != null) + { + var mqttData = new + { + OptTime = spectData.Timestamp.ToString("yyyy-MM-dd HH:mm:ss.fff"), + PointLat = nearestGPS.Latitude.ToString("F8"), + PointLng = nearestGPS.Longitude.ToString("F8"), + Value = spectData.DoseRate / 1000000000.0 // 转换为合适的单位 + }; + + string json = JsonConvert.SerializeObject(mqttData); + string topic = $"/{textBoxTopicRoot.Text}/{textBoxDeviceName.Text}/user/update"; + + if (mqttClient != null && mqttClient.IsConnected) + { + mqttClient.Publish(topic, Encoding.UTF8.GetBytes(json)); + LogMessage($"MQTT发送: {json}"); + } + else + { + LogMessage($"MQTT未连接,数据记录: {json}"); + } + } + } + + await Task.Delay(100, token); + } + catch (Exception ex) + { + LogMessage($"MQTT发送错误: {ex.Message}"); + await Task.Delay(5000, token); + } + } + } + + private GPSData FindNearestGPSData(DateTime targetTime) + { + GPSData nearest = null; + TimeSpan minDiff = TimeSpan.MaxValue; + + var gpsDataList = gpsDataQueue.ToArray(); + foreach (var gpsData in gpsDataList) + { + TimeSpan diff = Math.Abs((gpsData.Timestamp - targetTime).Ticks) == (gpsData.Timestamp - targetTime).Ticks ? + gpsData.Timestamp - targetTime : targetTime - gpsData.Timestamp; + + if (diff < minDiff) + { + minDiff = diff; + nearest = gpsData; + } + } + + return nearest; + } + + private async void LogWriter(CancellationToken token) + { + while (!token.IsCancellationRequested) + { + try + { + if (logQueue.TryDequeue(out string logMessage)) + { + await File.AppendAllTextAsync(logFileName, logMessage + Environment.NewLine, token); + } + + await Task.Delay(100, token); + } + catch (Exception ex) + { + // 避免日志写入错误导致的无限循环 + Console.WriteLine($"日志写入错误: {ex.Message}"); + await Task.Delay(1000, token); + } + } + } + + private void LogMessage(string message) + { + string logEntry = $"[{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff}] {message}"; + + // 添加到日志队列 + logQueue.Enqueue(logEntry); + + // 更新UI + if (textBoxLog.InvokeRequired) + { + textBoxLog.Invoke(new Action(() => { + textBoxLog.AppendText(logEntry + Environment.NewLine); + textBoxLog.ScrollToCaret(); + })); + } + else + { + textBoxLog.AppendText(logEntry + Environment.NewLine); + textBoxLog.ScrollToCaret(); + } + } + + protected override void OnFormClosing(FormClosingEventArgs e) + { + if (isRunning) + { + StopDataCollection(); + } + base.OnFormClosing(e); + } + } + + public class GPSData + { + public double Latitude { get; set; } + public double Longitude { get; set; } + public DateTime Timestamp { get; set; } + } + + public class SpectrometerData + { + public DateTime Timestamp { get; set; } + public double RealTime { get; set; } + public double DeadTime { get; set; } + public double DoseRate { get; set; } + public double TotalCountRate { get; set; } + } +} diff --git a/MqttSign.cs b/MqttSign.cs new file mode 100644 index 0000000..e6b812d --- /dev/null +++ b/MqttSign.cs @@ -0,0 +1,56 @@ +using System; +using System.Security.Cryptography; + +namespace aiot_paho_csharp +{ + class CryptoUtil + { + public static String hmacSha256(String plainText, String key) + { + var encoding = new System.Text.UTF8Encoding(); + byte[] plainTextBytes = encoding.GetBytes(plainText); + byte[] keyBytes = encoding.GetBytes(key); + + HMACSHA256 hmac = new HMACSHA256(keyBytes); + byte[] sign = hmac.ComputeHash(plainTextBytes); + return BitConverter.ToString(sign).Replace("-", string.Empty); + } + } + public class MqttSign + { + private String username = ""; + + private String password = ""; + + private String clientid = ""; + + public String getUsername() { return this.username; } + + public String getPassword() { return this.password; } + + public String getClientid() { return this.clientid; } + + public bool calculate(String productKey, String deviceName, String deviceSecret) + { + if (productKey == null || deviceName == null || deviceSecret == null) + { + return false; + } + + //MQTT用户名 + this.username = deviceName + "&" + productKey; + + //MQTT密码 + String timestamp = Convert.ToInt64((DateTime.UtcNow - new DateTime(1970, 1, 1)).TotalMilliseconds).ToString(); + String plainPasswd = "clientId" + productKey + "." + deviceName + "deviceName" + + deviceName + "productKey" + productKey + "timestamp" + timestamp; + this.password = CryptoUtil.hmacSha256(plainPasswd, deviceSecret); + + //MQTT ClientId + this.clientid = productKey + "." + deviceName + "|" + "timestamp=" + timestamp + + ",_v=paho-c#-1.0.0,securemode=2,signmethod=hmacsha256|"; + + return true; + } + } +} \ No newline at end of file diff --git a/readme.txt b/readme.txt new file mode 100644 index 0000000..3b681e2 --- /dev/null +++ b/readme.txt @@ -0,0 +1,94 @@ +我希望用C#写一个GUI应用,能够打开2个串口,然后读取数据,然后能够整理数据,把数据发送到配置好的网址上,并且能够在界面上看到数据记录,然后也能把数据写到一个log文件中。 +一个串口是GPS的,数据格式是这样的。要提取WGS84后面的经纬度信息。这个串口打开,发送bestposa com1 1 \r\n后就有数据传输进来: +#BESTPOSA,COM1,14394,98.0,UNKNOWN,1,1699.000,1700908,2,18;INSUFFICIENT_OBS,NONE,0.00000000000,0.00000000000,0.0000,0.0000,WGS84,0.0000,0.0000,0.0000,"",0.000,0.000,0,0,0,0,0,00,00,00*fcf220b4 +另一个串口是谱仪数据,文档如下: +航空γ谱仪通信协议 +通信方式 +采用RS232串口通信,115200,n,8,1。 +操作指令 +开始测量指令 +指令格式:$start[回车][换行] +反馈格式:ok[回车][换行] +谱数据刷新指令(刷新的回复要忽略) +指令格式:$refresh[回车][换行] +反馈格式:0 0 0 0 0 0 0 0 0 0 0 0 0 (很长的一个序列)[回车][换行] +清空测量指令 +指令格式:$clear[回车][换行] +反馈格式:ok[回车][换行] +谱分析指令 +指令格式:$getSperesult[回车][换行] +反馈格式:xxxx.xx.xx xx:xx:xx aaaaaaa.aa bbbbbbb.bb 1111111.11 2222222.12 3333333.33 4444444.44 5555555.55 6666666.66 7777777.77 8888888.88 9999999.99 0000000.00 JJJJJJ.JJ cccccccccc.cc [回车][换行] +“aaaaaaa.aa bbbbbbb.bb”表示实时间和死时间,单位:s(秒); +“1111111.11 … …0000000.00”表示十种核素计数率,单位:cps; +“JJJJJJ.JJ”表示环境总辐射剂量率值,单位:nSv/h; +“cccccccccc.cc”表示γ谱的全谱计数率cps; +“xxxx.xx.xx xx:xx:xx”表示返回该帧时间; +1111111.11 (核素40K,单位cps) +2222222.22 (核素208Tl,单位cps) +3333333.33 (核素214Bi,单位cps) +4444444.44 (核素214Pb,单位cps) +5555555.55 (核素226Ra,单位cps) +6666666.66 (核素232Th,单位cps) +7777777.77 (核素60Co,单位cps) +8888888.88 (核素131I,单位cps) +9999999.99 (核素137Cs,单位cps) +0000000.00 (核素192Ir,单位cps) + +这个需要打开串口的时候先开始,然后就进入一个循环,刷新一次,解析一次,得到数据,这样反复。我希望能选择刷新的频率。 + +两个串口的数据分别放到两个队列里,每次要从谱仪队列里读取数据,然后从GPS队列里读取时间最近的数据,两个合并起来,组成这样的格式:{"OptTime":"2020-11-30 13:16:24.178","PointLat":"23.10606","PointLng":"113.213425","Value":1.9334545697802241E-07}。 + +然后通过mqtt发送出去。 + +这是大致的发布代码: + String productKey = "gfcq950RDqt"; + String topicRoot = "a10inDdCRS6"; + String deviceName = "PubDevice"; + String deviceSecret = "1031a49a4f61c29a086f79b41ed971c7"; + + //计算Mqtt建联参数 + MqttSign sign = new MqttSign(); + sign.calculate(productKey, deviceName, deviceSecret); + + Console.WriteLine("username: " + sign.getUsername()); + Console.WriteLine("password: " + sign.getPassword()); + Console.WriteLine("clientid: " + sign.getClientid()); + + //使用Paho链接阿里云物联网平台 + int port = 443; + String broker = productKey + ".iot-as-mqtt.cn-shanghai.aliyuncs.com"; + + MqttClient mqttClient = new MqttClient(broker, port, true, MqttSslProtocols.TLSv1_2, null, null); + mqttClient.Connect(sign.getClientid(), sign.getUsername(), sign.getPassword()); + + Console.WriteLine("broker: " + broker + " Connected"); + + // //Paho Mqtt 消息订阅 + // String topicReply = "/" + topicRoot + "/" + deviceName + "/user/get"; + + // mqttClient.MqttMsgPublishReceived += MqttPostProperty_MqttMsgPublishReceived; + // mqttClient.Subscribe(new string[] { topicReply }, new byte[] { MqttMsgBase.QOS_LEVEL_AT_MOST_ONCE }); + // Console.WriteLine("subscribe: " + topicReply); + + //Paho Mqtt 消息发布 + String topic = "/" + topicRoot + "/" + deviceName + "/user/update"; + String message = "{\"id\":\"1\",\"version\":\"1.0\",\"params\":{\"LightSwitch\":0}}"; + mqttClient.Publish(topic, Encoding.UTF8.GetBytes(message)); + Console.WriteLine("publish: " + message); + + while(true) { + Thread.Sleep(2000); + } + + //Paho Mqtt 断开连接 + mqttClient.Disconnect(); + +另外,我希望是2个串口接收数据分别分配一个线程,上传分配一个线程。数据读取后放到一个队列里,上传线程去读取队列数据。 +还要有一个日志线程,把数据写入到日志里。 +软件要有个简单的文本框来展示串口收到的数据 +日志要自动创建文件,每次打开的时候,日志的名字应该是当前的时间。 + +要有个按钮可以一键开始和结束。 + +另外,接收数据的时候很可能每次收到的是不完整的数据。 +我已经创建好了模板程序。 \ No newline at end of file diff --git a/sscom-sender.csproj b/sscom-sender.csproj index 1b91352..a04e4de 100644 --- a/sscom-sender.csproj +++ b/sscom-sender.csproj @@ -1,4 +1,4 @@ - + WinExe @@ -10,7 +10,9 @@ + + \ No newline at end of file diff --git a/sscom-sender.sln b/sscom-sender.sln deleted file mode 100644 index 90e7ab7..0000000 --- a/sscom-sender.sln +++ /dev/null @@ -1,24 +0,0 @@ -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 17 -VisualStudioVersion = 17.5.2.0 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "sscom-sender", "sscom-sender.csproj", "{F03C6C53-8989-99E6-D747-EC03449AD20F}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {F03C6C53-8989-99E6-D747-EC03449AD20F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F03C6C53-8989-99E6-D747-EC03449AD20F}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F03C6C53-8989-99E6-D747-EC03449AD20F}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F03C6C53-8989-99E6-D747-EC03449AD20F}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {707867E8-3EF5-4E83-A851-9053BE677857} - EndGlobalSection -EndGlobal