| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661 |
- using System;
- using SHJX.Service.Common.ReadXML;
- using SHJX.Service.ServerClient.RS485;
- using SHJX.Service.Common.Logging;
- using Microsoft.Extensions.Logging;
- namespace SHJX.Service.ServerClient.TempController
- {
- /// <summary>
- /// 台达DTC1000V温控器通信控制类
- /// </summary>
- public abstract class SCLReverse485
- {
- private static readonly ILogger logger = LogFactory.BuildLogger(typeof(SCLReverse485));
-
- protected SCLReverse485( ReadConfigUtil config)
- {
- AddrNum = Convert.ToByte(config.ModBusNodeIDs["Heat"]);
- RetrieveDatas = "";
- LastReadPVtime = DateTime.Now;
- LastReadSVtime = DateTime.Now;
- LastReadOutputTime = DateTime.Now;
- CurrTempPv = 0;
- CurrTempSv = 0;
- }
- /// <summary>
- /// 温控器RS485通信号
- /// </summary>
- public byte AddrNum { get; set; }
- #region protected properties
- /// <summary>
- /// 最小查询间隔,毫秒
- /// </summary>
- protected const int IntervalMin = 3000;
- /// <summary>
- /// 发送指令后收到的响应数据
- /// </summary>
- private string RetrieveDatas { get; set; }
- /// <summary>
- /// 记录最后一次查询PV温度的时间,避免频繁查询
- /// </summary>
- private DateTime LastReadPVtime { get; set; }
- /// <summary>
- /// 记录最后一次查询SV温度的时间,避免频繁查询
- /// </summary>
- private DateTime LastReadSVtime { get; set; }
- /// <summary>
- /// 输出量最后查询时间
- /// </summary>
- private DateTime LastReadOutputTime { get; set; }
- /// <summary>
- /// 最近一次读到的实测温度
- /// </summary>
- private double CurrTempPv { get; set; }
- /// <summary>
- /// 最近一次读到的设置温度
- /// </summary>
- private double CurrTempSv { get; set; }
- /// <summary>
- /// 当前输出量
- /// </summary>
- private double CurrTempOutput { get; set; }
- #endregion
- #region protected methods
- /// <summary>
- /// 发送ASCII指令给温控器
- /// </summary>
- /// <param name="cmd">功能码+数据+LRC码</param>
- protected string SendCommand(string cmd)
- {
- if (!Rs485Client.IsValid) return string.Empty;
- RetrieveDatas = "";
- //Modbus ASCII格式:[:地址 功能码 数据地址 数据长度 LRC码 \r\n],如:[:-01-03-1000-0002-EA-CRLF]
- var msg = $":{cmd}\r\n";
- return Rs485Client.SendData(msg, "\r\n");
- }
- /// <summary>
- /// 从寄存器startpos,读取1个字节数据
- /// </summary>
- /// <param name="memAddr">起始位置,4个字符长度,如"1000"</param>
- /// <returns>读取结果</returns>
- protected double ReadByte(string memAddr)
- {
- double retval = -1;
- try
- {
- if (Rs485Client.IsValid)
- {
- // 读数指令
- string cmd = AddrNum.ToString("X").PadLeft(2, '0') + "03" + memAddr + "0001";
- cmd += LRC(cmd);
- RetrieveDatas = SendCommand(cmd);
- #region 解析温度值
- if (!string.IsNullOrEmpty(RetrieveDatas) && RetrieveDatas.Length >= (13)) // 例如 ": 06 03 02 00C8 CF \CR\LF"
- {
- string strPre = string.Format(":{0}03", AddrNum.ToString("X").PadLeft(2, '0'));
- if (RetrieveDatas.IndexOf(strPre) >= 0)
- {
- RetrieveDatas = RetrieveDatas.Remove(0, 5); // 移除":0103"
- int rlen = Convert.ToInt16(RetrieveDatas.Substring(0, 2), 16); // 数据长度
- RetrieveDatas = RetrieveDatas.Remove(0, 2); // 数据长度
- if (RetrieveDatas.Length >= 4)
- {
- retval = Convert.ToInt16(RetrieveDatas.Substring(0, 4), 16);
- RetrieveDatas = RetrieveDatas.Remove(0, 4);
- }
- }
- }
- #endregion
- RetrieveDatas = "";
- }
- }
- catch (Exception ex)
- {
- logger.LogError(ex.ToString());
- }
- return retval;
- }
- /// <summary>
- /// 从寄存器读取startpos开始的length字节数据
- /// </summary>
- /// <param name="startpos">起始位置,4个字符长度,如"1000"</param>
- /// <param name="length">读取长度,不大于6</param>
- /// <returns></returns>
- protected double[] ReadBytes(string startpos, int length)
- {
- if (length > 6) { length = 6; }
- double[] retval = new double[length];
- try
- {
- if (Rs485Client.IsValid)
- {
- // 读数指令
- string cmd = AddrNum.ToString("X").PadLeft(2, '0') + "03" + startpos + length.ToString("X").PadLeft(4, '0');
- cmd += LRC(cmd);
- string resdata = SendCommand(cmd);
- #region 解析读数值
- if (!string.IsNullOrEmpty(resdata) && resdata.Length >= (9 + length * 4)) // 例如 ": 06 03 02 00C8 CF \CR\LF"
- {
- string strPre = string.Format(":{0}03", AddrNum.ToString("X").PadLeft(2, '0'));
- if (resdata.IndexOf(strPre) >= 0)
- {
- resdata = resdata.Remove(0, 5); // 移除":0103"
- int rlen = Convert.ToInt16(resdata.Substring(0, 2), 16); // 数据长度
- resdata = resdata.Remove(0, 2); // 数据长度
- for (int i = 0; i < length; i++)
- {
- if (resdata.Length < 4) break;
- retval[i] = Convert.ToInt16(resdata.Substring(0, 4), 16);
- resdata = resdata.Remove(0, 4);
- }
- }
- }
- #endregion
- }
- }
- catch (Exception ex)
- {
- logger.LogError(ex.ToString());
- }
- return retval;
- }
- /// <summary>
- /// 写入数据到指定地址
- /// </summary>
- /// <param name="startpos">写入地址,4位字符</param>
- /// <param name="datas">要写入的数据</param>
- /// <returns>成功=true;失败=false</returns>
- protected bool writeBytes(string startpos, string datas)
- {
- bool bRet = false;
- try
- {
- if (Rs485Client.IsValid)
- {
- string cmd = string.Format("{0}06{1}{2}", AddrNum.ToString("X").PadLeft(2, '0'), startpos, datas);
- cmd += LRC(cmd);
- string resDatas = SendCommand(cmd);
- // 确定是否成功,返回字符串与查询指令相同
- if (!string.IsNullOrEmpty(resDatas) && resDatas.IndexOf(cmd) >= 0)
- {
- bRet = true;
- }
- }
- }
- catch (Exception ex)
- {
- logger.LogError(ex.ToString());
- }
- return bRet;
- }
- #endregion
- #region static CRC
- /// <summary>
- /// 计算CRC校验码,16位
- /// </summary>
- /// <param name="buff">指令</param>
- /// <returns>校验码数值</returns>
- public static UInt32 CRC16(byte[] buff, int startpos = 0, int length = -1)
- {
- UInt32 crcval = 0xFFFF;
- if (length < 1 || length > buff.Length) { length = buff.Length; }
- int i = startpos;
- while (length > 0)
- {
- crcval ^= buff[i++];
- for (int j = 0; j < 8; j++)
- {
- if ((crcval & 0x01) > 0)
- crcval = (crcval >> 1) ^ 0xa001;
- else
- crcval >>= 1;
- }
- length--;
- }
- return crcval;
- }
- /// <summary>
- /// 计算LRC校验码,字节码
- /// </summary>
- /// <param name="auchMsg"></param>
- /// <returns></returns>
- public static byte LRC(byte[] auchMsg)
- {
- byte uchLRC = 0;
- foreach (byte item in auchMsg)
- {
- uchLRC += item;
- }
- return (byte)((uchLRC ^ 0xFF) + 1);
- }
- /// <summary>
- /// 计算LRC校验码,字符串
- /// </summary>
- /// <param name="auchMsg"></param>
- /// <returns></returns>
- public static string LRC(string auchMsg)
- {
- int uchLRC = 0;
- for (int i = 0; i < auchMsg.Length; i++)
- {
- string chs = auchMsg.Substring(i, 2);
- uchLRC += Convert.ToInt32(chs, 16);
- i++;
- }
- byte btlrc = (byte)((uchLRC ^ 0xFF) + 1);
- return btlrc.ToString("X").PadLeft(2, '0');
- }
- #endregion
- #region PID参数读写
- /// <summary>
- /// 读取PID参数中的比例Pd
- /// </summary>
- /// <returns>比例值0.0~9999.0</returns>
- public double ReadPidPb()
- {
- int startpos = 0x1009;
- return ReadByte(startpos.ToString("X").PadLeft(4, '0')) / 10.0;
- }
- /// <summary>
- /// 读取PID参数中的积分Ti
- /// </summary>
- /// <returns>积分值0~9999</returns>
- public int ReadPidTi()
- {
- int startpos = 0x100A;
- return (int)ReadByte(startpos.ToString("X").PadLeft(4, '0'));
- }
- /// <summary>
- /// 读取PID参数中的微分Td
- /// </summary>
- /// <returns>微分值0~9999</returns>
- public int ReadPidTd()
- {
- int startpos = 0x100B;
- return (int)ReadByte(startpos.ToString("X").PadLeft(4, '0'));
- }
- /// <summary>
- /// 读取PID参数中的积分量默认值
- /// </summary>
- /// <returns>积分量默认值,0.0~100.0%</returns>
- public double ReadPidTiDeft()
- {
- int startpos = 0x100C;
- return ReadByte(startpos.ToString("X").PadLeft(4, '0')) / 10.0;
- }
- /// <summary>
- /// 写入PID参数中的比例Pb
- /// </summary>
- /// <param name="newval">比例值,0.0~9999.0</param>
- /// <returns>成功=true;失败=false</returns>
- public bool WritePidPb(double newval)
- {
- int startpos = 0x1009;
- return writeBytes(startpos.ToString("X").PadLeft(4, '0'), ((Int32)(newval * 10)).ToString("X").PadLeft(4, '0'));
- }
- /// <summary>
- /// 写入PID参数中的积分Ti
- /// </summary>
- /// <param name="newval">积分值,0~9999</param>
- /// <returns>成功=true;失败=false</returns>
- public bool WritePidTi(int newval)
- {
- int startpos = 0x100A;
- return writeBytes(startpos.ToString("X").PadLeft(4, '0'), newval.ToString("X").PadLeft(4, '0'));
- }
- /// <summary>
- /// 写入PID参数中的微分Td
- /// </summary>
- /// <param name="newval">微分值,0~9999</param>
- /// <returns>成功=true;失败=false</returns>
- public bool WritePidTd(int newval)
- {
- int startpos = 0x100B;
- return writeBytes(startpos.ToString("X").PadLeft(4, '0'), newval.ToString("X").PadLeft(4, '0'));
- }
- /// <summary>
- /// 写入PID参数中的积分量默认值
- /// </summary>
- /// <param name="newval">积分量默认值,0.0~100.0%</param>
- /// <returns>成功=true;失败=false</returns>
- public bool WritePidTiDeft(double newval)
- {
- int startpos = 0x100C;
- return writeBytes(startpos.ToString("X").PadLeft(4, '0'), ((Int32)(newval * 10)).ToString("X").PadLeft(4, '0'));
- }
- /// <summary>
- /// 读取一个通道的pid参数值
- /// </summary>
- /// <param name="pb">比例带,0.0~9999.0</param>
- /// <param name="ti">积分值,0~9999</param>
- /// <param name="td">微分值,0~9999</param>
- /// <param name="tideft">积分量默认值,0.0~100.0 (%)</param>
- public void ReadPid(ref double pb, ref int ti, ref int td, ref double tideft)
- {
- pb = ReadPidPb();
- ti = ReadPidTi();
- td = ReadPidTd();
- tideft = ReadPidTiDeft();
- }
- /// <summary>
- /// 写入一个通道的pid参数值
- /// </summary>
- /// <param name="pb">比例带(P),0.0~9999.0</param>
- /// <param name="ti">积分值(I),0~9999</param>
- /// <param name="td">微分值(D),0~9999</param>
- /// <param name="tideft">积分量默认值,0.0~100.0 (%)</param>
- /// <returns></returns>
- public bool WritePid(double pb, int ti, int td, double tideft)
- {
- bool retval = true;
- retval = retval && WritePidPb(pb);
- retval = retval && WritePidTi(ti);
- retval = retval && WritePidTd(td);
- retval = retval && WritePidTiDeft(tideft);
- return retval;
- }
- #endregion
- #region public methods
- /// <summary>
- /// 读取实际温度
- /// </summary>
- /// <returns></returns>
- public double ReadPV()
- {
- #region 控制查询频率,避免频繁查询
- if (DateTime.Now.Subtract(LastReadPVtime).TotalMilliseconds < IntervalMin)
- {
- return CurrTempPv;
- }
- LastReadPVtime = DateTime.Now;
- #endregion
- try
- {
- if (Rs485Client.IsValid)
- {
- DateTime tmstart = DateTime.Now;
- // 读取设定温度
- string addr = "1000";
- string cmd = string.Format("{0}03{1}0001", AddrNum.ToString("X").PadLeft(2, '0'), addr);
- cmd += LRC(cmd);
- RetrieveDatas = SendCommand(cmd);
- #region 解析温度值
- if (!string.IsNullOrEmpty(RetrieveDatas) && RetrieveDatas.Length >= 15)
- {
- string strPre = string.Format(":{0}03", AddrNum.ToString("X").PadLeft(2, '0'));
- if (RetrieveDatas.IndexOf(strPre) >= 0)
- {
- RetrieveDatas = RetrieveDatas.Remove(0, 5); // 移除":0103"
- RetrieveDatas = RetrieveDatas.Remove(0, 2);
- if (RetrieveDatas.Length >= 4)
- {
- CurrTempPv = Convert.ToInt16(RetrieveDatas.Substring(0, 4), 16) / 10.0;
- RetrieveDatas = RetrieveDatas.Remove(0, 4);
- //if (this.LogProgram != null) { this.LogProgram.Info(".." + this.AddrNum.ToString() + ".."); }
- }
- }
- }
- #endregion
- RetrieveDatas = "";
- logger.LogInformation("dtc start query pv = " + DateTime.Now.Subtract(tmstart).TotalMilliseconds.ToString());
- }
- }
- catch (Exception ex)
- {
- logger.LogError(ex.ToString());
- }
- return CurrTempPv;
- }
- /// <summary>
- /// 读取设定温度
- /// </summary>
- /// <returns></returns>
- public double ReadSV()
- {
- #region 控制查询频率,避免频繁查询
- if (DateTime.Now.Subtract(LastReadSVtime).TotalMilliseconds < IntervalMin)
- {
- return CurrTempSv;
- }
- LastReadSVtime = DateTime.Now;
- #endregion
- try
- {
- if (Rs485Client.IsValid)
- {
- // 读取设定温度
- string addr = "1001";
- string cmd = string.Format("{0}03{1}0001", AddrNum.ToString("X").PadLeft(2, '0'), addr);
- cmd += LRC(cmd);
- RetrieveDatas = SendCommand(cmd);
- #region 解析温度值
- if (!string.IsNullOrEmpty(RetrieveDatas) && RetrieveDatas.Length >= 15)
- {
- string strPre = string.Format(":{0}03", AddrNum.ToString("X").PadLeft(2, '0'));
- if (RetrieveDatas.IndexOf(strPre) >= 0)
- {
- RetrieveDatas = RetrieveDatas.Remove(0, 5); // 移除":0103"
- RetrieveDatas = RetrieveDatas.Remove(0, 2);
- if (RetrieveDatas.Length >= 4)
- {
- CurrTempSv = Convert.ToInt16(RetrieveDatas.Substring(0, 4), 16) / 10.0;
- RetrieveDatas = RetrieveDatas.Remove(0, 4);
- }
- }
- }
- #endregion
- RetrieveDatas = "";
- }
- }
- catch (Exception ex)
- {
- logger.LogError(ex.ToString());
- }
- return CurrTempSv;
- }
- /// <summary>
- /// 写入设定温度
- /// </summary>
- /// <param name="tempVal"></param>
- /// <returns></returns>
- public bool WriteSV(double tempVal)
- {
- bool bRet = false;
- try
- {
- if (Rs485Client.IsValid)
- {
- string addr = "1001";
- string strtmp = ((Int32)(tempVal * 10)).ToString("X").PadLeft(4, '0');//2000转为十六进制7D0,左边补0,直到长度为4
- if (strtmp.Length > 4) { strtmp = strtmp.Substring(strtmp.Length - 4, 4); }//若长度大于4,取后4位
- string cmd = string.Format("{0}06{1}{2}", AddrNum.ToString("X").PadLeft(2, '0'), addr, strtmp);//端口地址+06+1001+07D0+校验
- cmd += LRC(cmd);//0706100107D00B--cmd
- RetrieveDatas = SendCommand(cmd);
- // 确定是否成功
- if (!string.IsNullOrEmpty(RetrieveDatas) && RetrieveDatas.Length > 0)
- {
- if (RetrieveDatas == string.Format(":{0}\r\n", cmd))
- {
- bRet = true;
- }
- }
- RetrieveDatas = "";
- }
- }
- catch (Exception ex)
- {
- logger.LogError(ex.ToString());
- }
- return bRet;
- }
- /// <summary>
- /// 读取PV和SV两个值
- /// </summary>
- /// <param name="pv">实际温度</param>
- /// <param name="sv">设定温度</param>
- public void ReadPVnSV(ref double pv, ref double sv)
- {
- DateTime tmstart = DateTime.Now;
- #region 控制查询频率,避免频繁查询
- if (DateTime.Now.Subtract(LastReadPVtime).TotalMilliseconds < IntervalMin &&
- DateTime.Now.Subtract(LastReadSVtime).TotalMilliseconds < IntervalMin)
- {
- pv = CurrTempPv;
- sv = CurrTempSv;
- return;
- }
- //_log.Info(string.Format("{0} pvtime = {1}, svtime = {2}, interval = {3}, sub = {4}", AddrNum, LastReadPVtime.ToString("HHmmss.fff"), LastReadSVtime.ToString("HHmmss.fff"), IntervalMin, DateTime.Now.Subtract(LastReadPVtime).TotalMilliseconds));
- #endregion
- try
- {
- if (Rs485Client.IsValid)
- {
- // 读取2个温度值(2个word)
- string addr = "1000";
- string cmd = string.Format("{0}03{1}0002", AddrNum.ToString("X").PadLeft(2, '0'), addr);
- cmd += LRC(cmd);
- RetrieveDatas = SendCommand(cmd);
- #region 解析温度值
- if (!string.IsNullOrEmpty(RetrieveDatas) && RetrieveDatas.Length >= 17)
- {
- string strPre = string.Format(":{0}03", AddrNum.ToString("X").PadLeft(2, '0'));
- if (RetrieveDatas.IndexOf(strPre) >= 0)
- {
- RetrieveDatas = RetrieveDatas.Remove(0, 5); // 移除起始符信息":0103"
- RetrieveDatas = RetrieveDatas.Remove(0, 2); // 移除2位length信息
- if (RetrieveDatas.Length >= 4)
- {
- // 解析1000H数据
- CurrTempPv = Convert.ToInt16(RetrieveDatas.Substring(0, 4), 16) / 10.0;
- RetrieveDatas = RetrieveDatas.Remove(0, 4);
- }
- if (RetrieveDatas.Length >= 4)
- {
- // 解析1001H数据
- CurrTempSv = Convert.ToInt16(RetrieveDatas.Substring(0, 4), 16) / 10.0;
- RetrieveDatas = RetrieveDatas.Remove(0, 4);
- }
- }
- }
- #endregion
- RetrieveDatas = "";
- //_log.Info(string.Format("{0} dtc start query pv = {1}", AddrNum.ToString(), DateTime.Now.Subtract(tmstart).TotalMilliseconds.ToString()));
- }
- pv = CurrTempPv;
- sv = CurrTempSv;
- LastReadPVtime = LastReadSVtime = DateTime.Now;
- }
- catch (UnauthorizedAccessException exo)
- {
- logger.LogError(exo.ToString());
- pv = CurrTempPv;
- sv = CurrTempSv;
- LastReadPVtime = LastReadSVtime = DateTime.Now;
- }
- catch (Exception ex)
- {
- logger.LogError(ex.ToString());
- pv = CurrTempPv;
- sv = CurrTempSv;
- LastReadPVtime = LastReadSVtime = DateTime.Now;
- }
- }
- /// <summary>
- /// 读取输出量
- /// </summary>
- /// <returns></returns>
- public double ReadOutput()
- {
- #region 控制查询频率,避免频繁查询
- if (DateTime.Now.Subtract(LastReadOutputTime).TotalMilliseconds < IntervalMin * 3)
- {
- return CurrTempOutput;
- }
- LastReadOutputTime = DateTime.Now;
- #endregion
- int startpos = 0x1012;
- CurrTempOutput = ReadByte(startpos.ToString("X").PadLeft(4, '0'));
- return CurrTempOutput;
- }
- /// <summary>
- /// 写入输出量(仅温控器处于手动控制模式下有效)
- /// </summary>
- /// <param name="newvals"></param>
- /// <returns></returns>
- public bool WriteOutput(double newVal)
- {
- int startpos = 0x1012;
- string datas = ((Int32)(newVal * 10)).ToString("X").PadLeft(4, '0');
- return writeBytes(startpos.ToString("X").PadLeft(4, '0'), datas);
- }
- #endregion
- }
- }
|