//含有浓度的设置 public static void setValueToUI(DataGridView dgv, DataGridView dgv1, int well_class_index, string well_num) { //MessageBox.Show("调用了赋值函数:" + well_class_index.ToString() + " - " + well_num + " - " + well_conc); for (int i = 0; i < 8; i++) { for (int j = 0; j < 12; j++) { if (dgv.Rows[i].Cells[j].Selected == true) { //Info info = tpl[i, j]; Info info; if (well_class_index == 2) { info = new Info(i, j, "blank", well_num); } else { info = new Info(i, j, "smp", well_num); } dgv.Rows[i].Cells[j].Value = info.well_class + " " + info.well_num; //调整板子的颜色变化 DataReadWrite.changeODBackColor(info.well_class, dgv, info.i, info.j);//set板子 DataReadWrite.changeODBackColor(info.well_class, dgv1, info.i, info.j);//od板子 } } } }
//计算线性拟合的标准曲线 private void calclateStd(Graphics g) { //计算标准曲线 List <PointF> stdPoints = new List <PointF>();//保存标准曲线上的点 //lambda表达式 : double变为float格式 Func <double, float> double2Float = x => float.Parse(x.ToString()); //整理出标准品数据,用字典实现conc唯一性 int std_count = 0; Dictionary <double, List <Info> > std = new Dictionary <double, List <Info> >(); for (int i = 0; i < 8; i++) { for (int j = 0; j < 12; j++) { Info info = tpl[i, j]; List <Info> infoPlus = new List <Info>(); if (info != null && info.well_class == "std") { //增加od属性 info.well_od = od[i, j]; //整合到数据中 if (std.ContainsKey(info.well_conc)) { //如果存在,则先获得od值,然后增加 infoPlus = std[info.well_conc]; infoPlus.Add(info); std.Add(info.well_conc, infoPlus); } else { //首次增加 std_count++; infoPlus.Add(info); std.Add(info.well_conc, infoPlus); } } } } //整理x和y数组 double[] arr_x = new double[std_count]; //conc double[] arr_y = new double[std_count]; //od int std_i = 0; //最大值为 std_count foreach (double conc in std.Keys) { //MessageBox.Show(conc.ToString()); arr_x[std_i] = conc; List <Info> list = std[conc]; int od_counter = 0; double od_sum = 0; foreach (Info info in list) { double temp = double2Float(info.well_od); od_sum += temp; od_counter++; //保存标准曲线上的点 stdPoints.Add(new PointF(double2Float(conc), double2Float(temp))); } arr_y[std_i] = od_sum / od_counter;//计算od的平均值 std_i++; } //输出数组 //showArray(arr_x); this.richTextBox1.Text += "\r\r"; //showArray(arr_y); //拟合计算============================================================================ int Pointlen = arr_x.Length; //拟合,返回参数列表 Double[] paras = CurveFit.MultiLine(arr_x, arr_y, Pointlen, 1); //输出结果 //y=a0+a1*x 返回值则为a0 a1 int len = paras.Length; string str = ""; for (int i = 0; i < len; i++) { str += paras[i].ToString() + "\r\n"; } //显示结果 this.richTextBox1.Text += str; //由参数计算回算测量值============================================= double a0 = paras[0], a1 = paras[1]; double rss = 0, tss = 0; //残差平方和,总平方和 double[] arrX2 = new double[Pointlen]; //用于浓度回算 double[] arrY2 = new double[Pointlen]; //用于计算RSqure for (int i = 0; i < Pointlen; i++) { arrX2[i] = (arr_y[i] - a0) / a1; arrY2[i] = a0 + a1 * arr_x[i]; rss += Math.Pow(arr_y[i] - arrY2[i], 2); tss += Math.Pow(arr_y[i], 2); } //计算R^2 double RSqure = 1 - rss / tss; //this.richTextBox1.Text += "\r\nRSqure=" + RSqure; this.label2.Text = RSqure.ToString(); //==========================================================画图 basic //计算最值 double[] xM = getMinMax(arr_x); double[] yM = getMinMax(arr_y); //当前画布最值 double pWidth = this.pictureBox1.Width; double pHeight = this.pictureBox1.Height; //this.richTextBox1.Text += "\r\r画布尺寸(" + pWidth + "," + pHeight + "); \r"; //坐标轴范围 double x_span = xM[1] - xM[0]; double y_span = yM[1] - yM[0]; //lambda表达式 : 做坐标变换, Func <double, double> getAjustX = x => 20 + 0.9 * (x - xM[0]) * pWidth / x_span; Func <double, double> getAjustY = y => - 20 + pHeight - 0.9 * (y - yM[0]) * pHeight / y_span; //纵轴倒置 //==========================================================画图 坐标轴 //定义铅笔 Pen pen1 = new Pen(Color.Black, 1); //画坐标轴,带箭头 Pen pen2 = new Pen(Color.Black, 1); //画刻度 Pen pen3 = new Pen(Color.LightGray, 1); //画背景虚线 pen3.DashStyle = System.Drawing.Drawing2D.DashStyle.DashDot; //定义铅笔的头部箭头 System.Drawing.Drawing2D.AdjustableArrowCap lineArrow = new System.Drawing.Drawing2D.AdjustableArrowCap(4, 4, true); pen1.CustomEndCap = lineArrow; //--------------------定义坐标轴刻度 //坐标轴刻度 - 先按照 10格 double x_kedu = Math.Ceiling(x_span / 10);//刻度 double y_kedu = Math.Ceiling(y_span / 10); double x_o = xM[0] + x_kedu * 0.5;//坐标轴所在位置 double y_o = yM[0] + y_kedu * 0.6; //--------------------画坐标轴 //定义坐标点-x轴 PointF px1 = new PointF(0, double2Float(getAjustY(y_o))); PointF px2 = new PointF(double2Float(getAjustX(xM[1])), double2Float(getAjustY(y_o))); //定义坐标点-y轴 PointF py1 = new PointF(double2Float(getAjustX(x_o)), double2Float(pHeight)); PointF py2 = new PointF(double2Float(getAjustX(x_o)), double2Float(getAjustY(yM[1]))); //画坐标轴 g.DrawLine(pen1, px1, px2); //x g.DrawLine(pen1, py1, py2); //y //--------------------画坐标轴刻度 double x_axis = xM[0]; double y_axis = yM[0]; int ke_height = 6; //刻度尺寸 int font_size = 8; Font font = new Font("微软雅黑", font_size); //刻度字体 Brush brush = new SolidBrush(Color.Black); //用笔刷定义刻度字体颜色 PointF kedu_point; // = new PointF();//刻度的坐标 //坐标标度如果太大,则用科学技术法表示 Func <double, string> num2String = d => Int64.Parse(d.ToString()).ToString("E2"); //保留4位有效数字 //x轴刻度 while (x_axis < xM[1]) { //刻度向右递增 x_axis += x_kedu; //定义刻度线的首尾点 PointF px_k1 = new PointF(double2Float(getAjustX(x_axis)), double2Float(getAjustY(y_o)) - ke_height); PointF px_k2 = new PointF(double2Float(getAjustX(x_axis)), double2Float(getAjustY(y_o))); //画刻度 g.DrawLine(pen2, px_k1, px_k2); //画背景虚线 PointF px_k3 = new PointF(double2Float(getAjustX(x_axis)), double2Float(getAjustY(yM[0]))); PointF px_k4 = new PointF(double2Float(getAjustX(x_axis)), double2Float(getAjustY(yM[1]))); g.DrawLine(pen3, px_k3, px_k4); //标上刻度 kedu_point = new PointF(double2Float(getAjustX(x_axis)) - font_size, double2Float(getAjustY(y_o))); g.DrawString(x_axis.ToString(), font, brush, kedu_point); if (x_axis > 1000) { g.DrawString(num2String(x_axis), font, brush, kedu_point); } else { g.DrawString(x_axis.ToString(), font, brush, kedu_point); } } //y轴刻度 while (y_axis < yM[1]) { //刻度向右递增 y_axis += y_kedu; //定义刻度线的首尾点 PointF py_k1 = new PointF(double2Float(getAjustX(x_o)) + ke_height, double2Float(getAjustY(y_axis))); PointF py_k2 = new PointF(double2Float(getAjustX(x_o)), double2Float(getAjustY(y_axis))); //画刻度 g.DrawLine(pen2, py_k1, py_k2); //画背景虚线 PointF py_k3 = new PointF(double2Float(getAjustX(xM[0])), double2Float(getAjustY(y_axis))); PointF py_k4 = new PointF(double2Float(getAjustX(xM[1])), double2Float(getAjustY(y_axis))); g.DrawLine(pen3, py_k3, py_k4); //标上刻度 int y_axis_len = y_axis.ToString().Length; //kedu_point = new PointF(double2Float(getAjustX(x_o)) - y_axis_len * font_size, double2Float(getAjustY(y_axis))); kedu_point = new PointF(double2Float(getAjustX(x_o)), double2Float(getAjustY(y_axis))); if (y_axis > 1000) { g.DrawString(num2String(y_axis), font, brush, kedu_point); } else { g.DrawString(y_axis.ToString(), font, brush, kedu_point);//num2String } } //==========================================================画标注 主标题和坐标注释 g.DrawString("线性拟合标准曲线", new Font("宋体", 10), new SolidBrush(Color.Black), new Point(Width / 4, 10)); //==========================================================画图 拟合曲线 //细分点数 int dot_num = 10; List <PointF> pointFList = new List <PointF>(); //计算拟合出来的点 double[] xp = new double[dot_num]; double[] yp = new double[dot_num]; for (int i = 0; i < dot_num; i++) { //生成点 xp[i] = xM[0] + i * x_span / dot_num; yp[i] = a0 + a1 * xp[i]; //针对当前画布调整 xp[i] = double2Float(getAjustX(xp[i])); yp[i] = double2Float(getAjustY(yp[i])); //记录曲线 pointFList.Add(new PointF(double2Float(xp[i]), double2Float(yp[i]))); } //画很多线 - 很多点连起来就是一条直线 myDraw.DrawLine(g, pointFList); //==========================================================画图 std原始点 //画std原始点 //myDraw.DrawPoints(g, xp, yp,1,true); PointF p = new PointF(); int dot_radius = 6;//空心点的大小 for (int i = 0; i < stdPoints.Count; i++) { p = stdPoints[i]; p.X = double2Float(getAjustX(p.X)); p.Y = double2Float(getAjustY(p.Y)); g.DrawEllipse(new Pen(Color.Green), p.X, p.Y, dot_radius, dot_radius); } }
//保存文件 private void btnSave_Click(object sender, EventArgs e) { //UI->中间数据 readUItoArray(); //中间数据-》文件 //保存文件 Stream myStream; SaveFileDialog saveFileDialog1 = new SaveFileDialog(); saveFileDialog1.Filter = "*.mub(模板文件)|*.mub|*.dat(数据文件)|*.dat|txt files (*.txt)|*.txt|All files (*.*)|*.*"; saveFileDialog1.FilterIndex = 2; saveFileDialog1.RestoreDirectory = true; if (saveFileDialog1.ShowDialog() == DialogResult.OK) { if ((myStream = saveFileDialog1.OpenFile()) != null) { // Code to write the stream goes here. myStream.Close(); using (StreamWriter writer = new StreamWriter(saveFileDialog1.FileName, true)) { //中间数据写入文件-基本信息 foreach (string k in plate_info.Keys) { writer.WriteLine(k + ":" + plate_info[k]); } //中间数据写入文件-od writer.WriteLine(); writer.WriteLine("[OD Value]"); for (int i = 0; i < 8; i++) { for (int j = 0; j < 12; j++) { //writer.Write(i + "\t"); //if (od[i, j] != null && od[i, j].ToString() != "") if (od[i, j] >= 0) { writer.Write(od[i, j] + "\t"); } else { writer.Write("\t"); } } writer.WriteLine(); } //中间数据写入文件-tpl writer.WriteLine(); writer.WriteLine("[Layout]"); for (int i = 0; i < 8; i++) { for (int j = 0; j < 12; j++) { Info info = tpl[i, j]; if (info != null) { writer.Write(info.well_class + " " + info.well_num + "#" + info.well_conc + "\t"); } else { writer.Write("\t"); } } writer.WriteLine(); } //写曲线类型 writer.WriteLine("Curve:" + getRadioIndex().ToString()); //写备注 writer.WriteLine("Note:" + this.richTextBox1.Text); //plate_info["Note"] = this.richTextBox1.Text; writer.WriteLine("======end======"); //写md5验证码:考虑使用tpl和od联合生成 } } myStream.Close(); } }
//中间文件到界面-设置 public static void readIntoUI(Info[,] tpl, DataGridView dgv, DataGridView dgv1) { //输出模板信息 for (int i = 0; i < 8; i++) { for (int j = 0; j < 12; j++) { if (tpl[i, j] != null) { Info info = tpl[i, j]; //textDebug += "(" + info.i + "," + info.j + ")," + info.well_class + "(" + info.well_num + "): " + " conc=" + info.well_conc + "\n"; //dgv.Rows[i].Cells[j].Value = info.well_class + " " + info.well_num + System.Environment.NewLine + info.well_conc; dgv.Rows[i].Cells[j].Value = info.well_class + " " + info.well_num + '\r' + info.well_conc; //调整板子的颜色变化 changeODBackColor(info.well_class, dgv, info.i, info.j);//set板子 changeODBackColor(info.well_class, dgv1, info.i,info.j);//od板子 } } } }
//=================================从界面到文件 //返回模板设置信息 public Info[,] readFromUI(DataGridView dgv, bool isContrl) { for (int i = 0; i < 8; i++) { for (int j = 0; j < 12; j++) { //if (dgv.Rows[i].Cells[j].Value != null) if ((dgv.Rows[i].Cells[j].Value != null) && (dgv.Rows[i].Cells[j].Value.ToString() != "")) { //保存拆分后的字符串 string[] info = new string[2]; string[] info2 = new string[2]; //定义cell信息类 Info wi; //获取单元格内容字符串 string txt = dgv.Rows[i].Cells[j].Value.ToString(); //拆分字符串 info = txt.Split(' '); if (info[1].IndexOf('\r') != -1) { info2 = info[1].Split('\r'); //if (i < 2 && j < 2) MessageBox.Show(info[0] + " " + info2[0] + "#" + info2[1]);//todo wi = new Info(i, j, info[0], info2[0], double.Parse(info2[1])); } else { //if (i < 2 && j < 2) MessageBox.Show(info[0] + " " + info[1]);//todo wi = new Info(i, j, info[0], info[1]); } tpl[i, j] = wi; } else { tpl[i, j] = null; } } } return tpl; }
//从文件读取到中间格式 public Dictionary<string, string> readFromFile(string fileName) { //流读取 string txt = ""; //使用之后会自动销毁流对象 using (StreamReader sr = new StreamReader(fileName, true)) { //定义文件分隔符 bool flag_head = true;//文件头部信息是否完整 bool flag_Value = false; //值开始 bool flag_Layout = false;//模板开始 bool flag_Curve = false;//拟合曲线类型开始 bool flag_MD5 = false;//文件sha1编码 //设置od和模板数据的循环初始值 int i = 0; int iM = 0; //进行文件循环 while (sr.Peek() >= 0) { //Console.WriteLine(sr.ReadLine()); txt = sr.ReadLine().ToString(); //如果是空行,则过滤掉。 if (txt == "") continue; //根据文本标记设置开关-OD数据 if (txt == "[OD Value]") { flag_head = false; flag_Value = true; flag_Layout = false; flag_Curve = false; flag_MD5 = false; continue; } //根据文本标记设置开关-布局数据 if (txt == "[Layout]") { flag_head = false; flag_Value = false; flag_Layout = true; flag_Curve = false; flag_MD5 = false; continue; } //根据文本标记设置开关-尾部数据 if (txt.Contains("Curve")) { flag_head = false; flag_Value = false; flag_Layout = false; flag_Curve = true; flag_MD5 = false; //continue; } //根据文本标记设置开关-尾部数据 if (txt.Contains("-END-")) { flag_head = false; flag_Value = false; flag_Layout = false; flag_Curve = false; flag_MD5 = true; continue; } //======================================== //---------------- //根据开关进行处理-头部文件 if (flag_head) { //MessageBox.Show(txt, "Head"); if (txt.Contains("SaveTime")) { string[] info = txt.Split(':'); plate_Info.Add(info[0], info[1] + ':' + info[2] + ':' + info[3]); } if (txt.Contains("Name")) { string[] info = txt.Split(':'); plate_Info.Add(info[0], info[1]); } if (txt.Contains("Lot")) { string[] info = txt.Split(':'); plate_Info.Add(info[0], info[1]); } if (txt.Contains("LabDate")) { string[] info = txt.Split(':'); plate_Info.Add(info[0], info[1]); } if (txt.Contains("Unit")) { string[] info = txt.Split(':'); plate_Info.Add(info[0], info[1]); } if (txt.Contains("Notice")) { string[] info = txt.Split(':'); plate_Info.Add(info[0], info[1]); } } //---------------- //根据开关进行处理-OD数据 //if (flag_Value && false)//先跳过OD data if (flag_Value) { //MessageBox.Show(i + "[]" + txt, "OD data"); string[] d_value = txt.Split('\t'); //j相当于列 //string Location = ""; for (int j = 0; j < 12; j++) { if (d_value[j]!=null) { if (d_value[j].Trim() != "") { od[i, j] = Double.Parse(d_value[j]); } else { od[i, j] = -10000; } } } i++;//i相当于行 if (i > 7) { flag_Value = false; } } //---------------- //根据开关进行处理-布局数据 //if (flag_Layout && false)//先跳过模板data if (flag_Layout) { //MessageBox.Show(iM+"[]"+txt, "Layout"); string[] d_value = txt.Split('\t'); //j相当于列 //string Location = ""; //string MuBan_info = ""; string[] info = new string[2]; string[] info2 = new string[2]; for (int j = 0; j < 12; j++) { //MuBan_info = d_value[j] + " \n"; if ( d_value[j]!=null && d_value[j].Trim() != "") { if (d_value[j].Contains("std") || d_value[j].Contains("ctr") ) { info = d_value[j].Split('#'); info2 = info[0].Split(' '); //tpl.std.Add(info2[1], Convert.ToDouble(info[1]) ); //MuBan_info += "标准品" + info2[0] + ", 编号" + info2[1] + ", 浓度" + info[1]; //Info wi = new Info(Convert.ToDouble(info[1]), iM, j); Info wi = new Info(iM, j, info2[0], info2[1], Convert.ToDouble(info[1]) ); tpl[iM,j]=wi; } else if (d_value[j].Contains("smp")) { info = d_value[j].Split(' '); //tpl.ctr.Add(info[1],-1); //MuBan_info += "待测样品" + info[0] + ", 编号" + info[1]; Info wi = new Info(iM, j, info[0], info[1]); tpl[iM, j] = wi; } } } iM++;//i相当于行 if (iM >= 7) { flag_Layout = false; flag_Curve = true; } } //---------------- //根据开关进行处理-尾部文件 if (flag_Curve) { //MessageBox.Show(txt, "Curve"); //if (txt.Contains("Curve")) //{ // string[] info = txt.Split(':'); // plate_Info.Add(info[0], info[1]); //} if (txt.Contains("Note")) { string[] info = txt.Split(':'); plate_Info.Add(info[0], info[1]); } } //---------------- //根据开关进行处理-验证码 if (flag_MD5) { plate_Info.Add("MD5", txt.ToString().Trim()); } } } return plate_Info; }
//设置内置模板文件 private void setTpls() { //设置瘦肉精的模板文件 Info[,] tpl=new Info[8,12]; //==============================默认是空模板:也就是自定义模板。 tpls.Add("自定义模板", tpl); //==========================第1个内置模板 //初始化数组 tpl=new Info[8,12]; //设置标准品 tpl[0, 0] = new Info(0, 0, "std", "0", 0); tpl[1, 0] = new Info(1, 0, "std", "1", 10); tpl[2, 0] = new Info(2, 0, "std", "2", 20); tpl[3, 0] = new Info(3, 0, "std", "3", 30); tpl[4, 0] = new Info(4, 0, "std", "4", 40); tpl[5, 0] = new Info(5, 0, "std", "5", 50); tpl[6, 0] = new Info(6, 0, "std", "6", 60); tpl[7, 0] = new Info(7, 0, "std", "7", 70); //设置结束,增加到模板中 tpls.Add("瘦肉精", tpl); //==========================第2个内置模板 //初始化数组 tpl = new Info[8, 12]; //设置标准品 tpl[0, 0] = new Info(0, 0, "std", "0", 0); tpl[1, 0] = new Info(1, 0, "std", "1", 20); tpl[2, 0] = new Info(2, 0, "std", "2", 40); tpl[3, 0] = new Info(3, 0, "std", "3", 60); tpl[4, 0] = new Info(4, 0, "std", "4", 80); tpl[5, 0] = new Info(5, 0, "std", "5", 100); tpl[6, 0] = new Info(6, 0, "std", "6", 120); tpl[7, 0] = new Info(7, 0, "std", "7", 140); //设置质控品 tpl[0, 1] = new Info(0,1, "ctr", "1", 100); //设置结束,增加到模板中 tpls.Add("霉菌毒素", tpl); //==========================第3个内置模板 //初始化数组 tpl = new Info[8, 12]; //设置标准品 tpl[0, 0] = new Info(0, 0, "std", "0", 0); tpl[0, 1] = new Info(0, 1, "std", "1", 30); tpl[0, 2] = new Info(0, 2, "std", "2", 60); tpl[0, 3] = new Info(0, 3, "std", "3", 90); tpl[0, 4] = new Info(0, 4, "std", "4", 120); tpl[0, 5] = new Info(0, 5, "std", "5", 150); tpl[0, 6] = new Info(0, 6, "std", "6", 180); tpl[0, 7] = new Info(0, 7, "std", "7", 210); //设置质控品 tpl[1, 0] = new Info(1, 0,"ctr","1",100); tpl[1, 1] = new Info(1, 1, "ctr", "2", 200); //设置结束,增加到模板中 tpls.Add("黄曲霉毒素B1", tpl); //==========================第4个内置模板 //初始化数组 tpl = new Info[8, 12]; //设置标准品 tpl[0, 0] = new Info(0, 0, "std", "0", 0); tpl[0, 1] = new Info(0, 1, "std", "1", 30); tpl[0, 2] = new Info(0, 2, "std", "2", 60); tpl[0, 3] = new Info(0, 3, "std", "3", 90); tpl[0, 4] = new Info(0, 4, "std", "4", 120); tpl[0, 5] = new Info(0, 5, "std", "5", 150); tpl[0, 6] = new Info(0, 6, "std", "6", 180); //设置质控品 tpl[1, 0] = new Info(1, 0, "ctr", "1", 100); tpl[1, 1] = new Info(1, 1, "ctr", "2", 200); tpl[1, 2] = new Info(1, 2, "ctr", "3", 300); //设置结束,增加到模板中 tpls.Add("干豆腐黄色素", tpl); }