namespace SHJX.Service.PortClient.TempController
{
///
/// 台达DTC1000V温控器通信控制类
///
public class DeltaDTC
{
private static readonly ILogger logger = LogFactory.BuildLogger(typeof(SerialPortImp));
private DeltaDTC()
{
RetrieveDatas = "";
LastReadPVtime = DateTime.Now;
LastReadSVtime = DateTime.Now;
LastReadOutputTime = DateTime.Now;
CurrTempPv = 0;
CurrTempSv = 0;
}
private static readonly object obj_locker = new();
public static DeltaDTC StartNew
{
get
{
lock (obj_locker)
{
return new();
}
}
}
public DeltaDTC SetAddrNum(byte nodeID)
{
AddrNum = nodeID;
return this;
}
#region protected properties
///
/// 温控器RS485通信号
///
private byte AddrNum { get; set; }
///
/// 最小查询间隔,毫秒
///
protected const int IntervalMin = 3000;
///
/// 发送指令后收到的响应数据
///
private string RetrieveDatas { get; set; }
///
/// 记录最后一次查询PV温度的时间,避免频繁查询
///
private DateTime LastReadPVtime { get; set; }
///
/// 记录最后一次查询SV温度的时间,避免频繁查询
///
private DateTime LastReadSVtime { get; set; }
///
/// 输出量最后查询时间
///
private DateTime LastReadOutputTime { get; set; }
///
/// 最近一次读到的实测温度
///
private double CurrTempPv { get; set; }
///
/// 最近一次读到的设置温度
///
private double CurrTempSv { get; set; }
///
/// 当前输出量
///
private double CurrTempOutput { get; set; }
#endregion
#region protected methods
///
/// 发送ASCII指令给温控器
///
/// 功能码+数据+LRC码
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");
}
///
/// 从寄存器startpos,读取1个字节数据
///
/// 起始位置,4个字符长度,如"1000"
/// 读取结果
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;
}
///
/// 从寄存器读取startpos开始的length字节数据
///
/// 起始位置,4个字符长度,如"1000"
/// 读取长度,不大于6
///
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;
}
///
/// 写入数据到指定地址
///
/// 写入地址,4位字符
/// 要写入的数据
/// 成功=true;失败=false
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
///
/// 计算CRC校验码,16位
///
/// 指令
/// 校验码数值
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;
}
///
/// 计算LRC校验码,字节码
///
///
///
public static byte LRC(byte[] auchMsg)
{
byte uchLRC = 0;
foreach (byte item in auchMsg)
{
uchLRC += item;
}
return (byte)((uchLRC ^ 0xFF) + 1);
}
///
/// 计算LRC校验码,字符串
///
///
///
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参数读写
///
/// 读取PID参数中的比例Pd
///
/// 比例值0.0~9999.0
public double ReadPidPb()
{
int startpos = 0x1009;
return ReadByte(startpos.ToString("X").PadLeft(4, '0')) / 10.0;
}
///
/// 读取PID参数中的积分Ti
///
/// 积分值0~9999
public int ReadPidTi()
{
int startpos = 0x100A;
return (int)ReadByte(startpos.ToString("X").PadLeft(4, '0'));
}
///
/// 读取PID参数中的微分Td
///
/// 微分值0~9999
public int ReadPidTd()
{
int startpos = 0x100B;
return (int)ReadByte(startpos.ToString("X").PadLeft(4, '0'));
}
///
/// 读取PID参数中的积分量默认值
///
/// 积分量默认值,0.0~100.0%
public double ReadPidTiDeft()
{
int startpos = 0x100C;
return ReadByte(startpos.ToString("X").PadLeft(4, '0')) / 10.0;
}
///
/// 写入PID参数中的比例Pb
///
/// 比例值,0.0~9999.0
/// 成功=true;失败=false
public bool WritePidPb(double newval)
{
int startpos = 0x1009;
return WriteBytes(startpos.ToString("X").PadLeft(4, '0'), ((Int32)(newval * 10)).ToString("X").PadLeft(4, '0'));
}
///
/// 写入PID参数中的积分Ti
///
/// 积分值,0~9999
/// 成功=true;失败=false
public bool WritePidTi(int newval)
{
int startpos = 0x100A;
return WriteBytes(startpos.ToString("X").PadLeft(4, '0'), newval.ToString("X").PadLeft(4, '0'));
}
///
/// 写入PID参数中的微分Td
///
/// 微分值,0~9999
/// 成功=true;失败=false
public bool WritePidTd(int newval)
{
int startpos = 0x100B;
return WriteBytes(startpos.ToString("X").PadLeft(4, '0'), newval.ToString("X").PadLeft(4, '0'));
}
///
/// 写入PID参数中的积分量默认值
///
/// 积分量默认值,0.0~100.0%
/// 成功=true;失败=false
public bool WritePidTiDeft(double newval)
{
int startpos = 0x100C;
return WriteBytes(startpos.ToString("X").PadLeft(4, '0'), ((Int32)(newval * 10)).ToString("X").PadLeft(4, '0'));
}
///
/// 读取一个通道的pid参数值
///
/// 比例带,0.0~9999.0
/// 积分值,0~9999
/// 微分值,0~9999
/// 积分量默认值,0.0~100.0 (%)
public void ReadPid(ref double pb, ref int ti, ref int td, ref double tideft)
{
pb = ReadPidPb();
ti = ReadPidTi();
td = ReadPidTd();
tideft = ReadPidTiDeft();
}
///
/// 写入一个通道的pid参数值
///
/// 比例带(P),0.0~9999.0
/// 积分值(I),0~9999
/// 微分值(D),0~9999
/// 积分量默认值,0.0~100.0 (%)
///
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
///
/// 读取实际温度
///
///
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;
}
///
/// 读取设定温度
///
///
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;
}
///
/// 写入设定温度
///
///
///
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;
}
///
/// 读取PV和SV两个值
///
/// 实际温度
/// 设定温度
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;
}
#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 = "";
}
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;
}
}
///
/// 读取输出量
///
///
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;
}
///
/// 写入输出量(仅温控器处于手动控制模式下有效)
///
///
///
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
}
}