/// <summary> /// Получить подходящий порядок байт по умолчанию /// </summary> public int[] GetDefByteOrder(int elemCnt) { switch (elemCnt) { case 1: if (defByteOrder2 == null) { defByteOrder2 = ModbusUtils.ParseByteOrder(DefByteOrder2); } return(defByteOrder2); case 2: if (defByteOrder4 == null) { defByteOrder4 = ModbusUtils.ParseByteOrder(DefByteOrder4); } return(defByteOrder4); case 4: if (defByteOrder8 == null) { defByteOrder8 = ModbusUtils.ParseByteOrder(DefByteOrder8); } return(defByteOrder8); default: return(null); } }
/// <summary> /// Loads the group from the XML node. /// </summary> public virtual void LoadFromXml(XmlElement groupElem) { if (groupElem == null) { throw new ArgumentNullException("groupElem"); } Name = groupElem.GetAttribute("name"); Address = (ushort)groupElem.GetAttrAsInt("address"); Active = groupElem.GetAttrAsBool("active", true); XmlNodeList elemNodes = groupElem.SelectNodes("Elem"); int maxElemCnt = MaxElemCnt; ElemType defElemType = DefElemType; foreach (XmlElement elemElem in elemNodes) { if (Elems.Count >= maxElemCnt) { break; } Elem elem = CreateElem(); elem.Name = elemElem.GetAttribute("name"); elem.ElemType = elemElem.GetAttrAsEnum("type", defElemType); if (ByteOrderEnabled) { elem.ByteOrderStr = elemElem.GetAttribute("byteOrder"); elem.ByteOrder = ModbusUtils.ParseByteOrder(elem.ByteOrderStr); } Elems.Add(elem); } }
/// <summary> /// Расшифровать PDU ответа /// </summary> public virtual bool DecodeRespPDU(byte[] buffer, int offset, int length, out string errMsg) { errMsg = ""; bool result = false; byte respFuncCode = buffer[offset]; if (respFuncCode == FuncCode) { if (length == RespPduLen) { result = true; } else { errMsg = ModbusPhrases.IncorrectPduLength; } } else if (respFuncCode == ExcFuncCode) { errMsg = length == 2 ? ModbusPhrases.DeviceError + ": " + ModbusUtils.GetExcDescr(buffer[offset + 1]) : ModbusPhrases.IncorrectPduLength; } else { errMsg = ModbusPhrases.IncorrectPduFuncCode; } return(result); }
/// <summary> /// Инициализировать PDU запроса, рассчитать длину ответа /// </summary> public override void InitReqPDU() { if (Multiple) { // формирование PDU для команды WriteMultipleCoils или WriteMultipleRegisters int dataLength = TableType == TableType.Coils ? ((ElemCnt % 8 == 0) ? ElemCnt / 8 : ElemCnt / 8 + 1) : ElemCnt *ModbusUtils.GetDataLength(ElemType); ReqPDU = new byte[6 + dataLength]; ReqPDU[0] = FuncCode; ReqPDU[1] = (byte)(Address / 256); ReqPDU[2] = (byte)(Address % 256); ReqPDU[3] = (byte)(ElemCnt / 256); ReqPDU[4] = (byte)(ElemCnt % 256); ReqPDU[5] = (byte)dataLength; ModbusUtils.ApplyByteOrder(Data, 0, ReqPDU, 6, dataLength, ByteOrder, false); // установка длины ответа RespPduLen = 5; } else { // формирование PDU для команды WriteSingleCoil или WriteSingleRegister int dataLength = TableType == TableType.Coils ? 2 : ModbusUtils.GetDataLength(ElemType); ReqPDU = new byte[3 + dataLength]; ReqPDU[0] = FuncCode; ReqPDU[1] = (byte)(Address / 256); ReqPDU[2] = (byte)(Address % 256); if (TableType == TableType.Coils) { ReqPDU[3] = Value > 0 ? (byte)0xFF : (byte)0x00; ReqPDU[4] = 0x00; } else { byte[] data = dataLength == 2 ? new byte[] // standard Modbus { (byte)(Value / 256), (byte)(Value % 256) } : Data; ModbusUtils.ApplyByteOrder(data, 0, ReqPDU, 3, dataLength, ByteOrder, false); } // установка длины ответа RespPduLen = ReqPDU.Length; // echo } }
/// <summary> /// Получить значение элемента в соответствии с его типом, преобразованное в double /// </summary> public double GetElemVal(int elemInd) { Elem elem = Elems[elemInd]; byte[] elemVal = ElemVals[elemInd]; byte[] buf; // перестановка байт в случае необходимости if (elem.ByteOrder == null) { buf = elemVal; } else { buf = new byte[elemVal.Length]; ModbusUtils.ApplyByteOrder(elemVal, buf, elem.ByteOrder); } // расчёт значения switch (elem.ElemType) { case ElemType.UShort: return(BitConverter.ToUInt16(buf, 0)); case ElemType.Short: return(BitConverter.ToInt16(buf, 0)); case ElemType.UInt: return(BitConverter.ToUInt32(buf, 0)); case ElemType.Int: return(BitConverter.ToInt32(buf, 0)); case ElemType.ULong: return(BitConverter.ToUInt64(buf, 0)); case ElemType.Long: return(BitConverter.ToInt64(buf, 0)); case ElemType.Float: return(BitConverter.ToSingle(buf, 0)); case ElemType.Double: return(BitConverter.ToDouble(buf, 0)); case ElemType.Bool: return(buf[0] > 0 ? 1.0 : 0.0); default: return(0.0); } }
/// <summary> /// Get the value of the element according to its type, converted to double /// </summary> public double GetElemVal(int elemInd) { var elem = Elems[elemInd]; byte[] elemVal = ElemVals[elemInd]; byte[] buf; // swapping bytes if necessary if (elem.ByteOrder == null) { buf = elemVal; } else { buf = new byte[elemVal.Length]; ModbusUtils.ApplyByteOrder(elemVal, buf, elem.ByteOrder); } // calculation of value switch (elem.ElemType) { case ElemType.UShort: return(BitConverter.ToUInt16(buf, 0)); case ElemType.Short: return(BitConverter.ToInt16(buf, 0)); case ElemType.UInt: return(BitConverter.ToUInt32(buf, 0)); case ElemType.Int: return(BitConverter.ToInt32(buf, 0)); case ElemType.ULong: return(BitConverter.ToUInt64(buf, 0)); case ElemType.Long: return(BitConverter.ToInt64(buf, 0)); case ElemType.Float: return(BitConverter.ToSingle(buf, 0)); case ElemType.Double: return(BitConverter.ToDouble(buf, 0)); case ElemType.Bool: return(buf[0] > 0 ? 1.0 : 0.0); default: return(0.0); } }
/// <summary> /// Инициализировать ADU запроса и рассчитать длину ответа /// </summary> public virtual void InitReqADU(byte devAddr, TransModes transMode) { if (ReqPDU != null) { int pduLen = ReqPDU.Length; switch (transMode) { case TransModes.RTU: ReqADU = new byte[pduLen + 3]; ReqADU[0] = devAddr; ReqPDU.CopyTo(ReqADU, 1); ushort crc = ModbusUtils.CalcCRC16(ReqADU, 0, pduLen + 1); ReqADU[pduLen + 1] = (byte)(crc % 256); ReqADU[pduLen + 2] = (byte)(crc / 256); RespAduLen = RespPduLen + 3; break; case TransModes.ASCII: byte[] aduBuf = new byte[pduLen + 2]; aduBuf[0] = devAddr; ReqPDU.CopyTo(aduBuf, 1); aduBuf[pduLen + 1] = ModbusUtils.CalcLRC(aduBuf, 0, pduLen + 1); StringBuilder sbADU = new StringBuilder(); foreach (byte b in aduBuf) { sbADU.Append(b.ToString("X2")); } ReqADU = Encoding.Default.GetBytes(sbADU.ToString()); ReqStr = ModbusUtils.Colon + sbADU; RespAduLen = RespPduLen + 2; break; default: // TransModes.TCP ReqADU = new byte[pduLen + 7]; ReqADU[0] = 0; ReqADU[1] = 0; ReqADU[2] = 0; ReqADU[3] = 0; ReqADU[4] = (byte)((pduLen + 1) / 256); ReqADU[5] = (byte)((pduLen + 1) % 256); ReqADU[6] = devAddr; ReqPDU.CopyTo(ReqADU, 7); RespAduLen = RespPduLen + 7; break; } } }
/// <summary> /// Загрузить настройки шаблона из XML-узла /// </summary> public void LoadFromXml(XmlElement settingsElem) { if (settingsElem == null) { throw new ArgumentNullException("settingsElem"); } ZeroAddr = settingsElem.GetChildAsBool("ZeroAddr", false); DecAddr = settingsElem.GetChildAsBool("DecAddr", true); DefByteOrder2 = settingsElem.GetChildAsString("DefByteOrder2"); DefByteOrder4 = settingsElem.GetChildAsString("DefByteOrder4"); DefByteOrder8 = settingsElem.GetChildAsString("DefByteOrder8"); defByteOrder2 = ModbusUtils.ParseByteOrder(DefByteOrder2); defByteOrder4 = ModbusUtils.ParseByteOrder(DefByteOrder4); defByteOrder8 = ModbusUtils.ParseByteOrder(DefByteOrder8); }
/// <summary> /// Инициализировать PDU запроса, рассчитать длину ответа /// </summary> public override void InitReqPDU() { if (Multiple) { // формирование PDU для команды WriteMultipleCoils или WriteMultipleRegisters int byteCnt = TableType == TableType.Coils ? ((ElemCnt % 8 == 0) ? ElemCnt / 8 : ElemCnt / 8 + 1) : ElemCnt * 2; ReqPDU = new byte[6 + byteCnt]; ReqPDU[0] = FuncCode; ReqPDU[1] = (byte)(Address / 256); ReqPDU[2] = (byte)(Address % 256); ReqPDU[3] = (byte)(ElemCnt / 256); ReqPDU[4] = (byte)(ElemCnt % 256); ReqPDU[5] = (byte)byteCnt; ModbusUtils.ApplyByteOrder(Data, 0, ReqPDU, 6, byteCnt, ByteOrder, false); } else { // формирование PDU для команды WriteSingleCoil или WriteSingleRegister ReqPDU = new byte[5]; ReqPDU[0] = FuncCode; ReqPDU[1] = (byte)(Address / 256); ReqPDU[2] = (byte)(Address % 256); if (TableType == TableType.Coils) { ReqPDU[3] = Value > 0 ? (byte)0xFF : (byte)0x00; ReqPDU[4] = 0x00; } else { byte[] data = new byte[] { (byte)(Value / 256), (byte)(Value % 256) }; ModbusUtils.ApplyByteOrder(data, 0, ReqPDU, 3, 2, ByteOrder, false); } } // установка длины ответа RespPduLen = 5; }
/// <summary> /// Loads the command from the XML node. /// </summary> public virtual void LoadFromXml(XmlElement cmdElem) { if (cmdElem == null) { throw new ArgumentNullException("cmdElem"); } Address = (ushort)cmdElem.GetAttrAsInt("address"); ElemType = cmdElem.GetAttrAsEnum("elemType", DefElemType); ElemCnt = cmdElem.GetAttrAsInt("elemCnt", 1); Name = cmdElem.GetAttribute("name"); CmdNum = cmdElem.GetAttrAsInt("cmdNum"); if (ByteOrderEnabled) { ByteOrderStr = cmdElem.GetAttribute("byteOrder"); ByteOrder = ModbusUtils.ParseByteOrder(ByteOrderStr); } }
/// <inheritdoc /> /// <summary> /// Initialize the request PDU, calculate the answer length /// </summary> public override void InitReqPDU() { if (Multiple) { // PDU generation for WriteMultipleCoils or WriteMultipleRegisters command int byteCnt = TableType == TableType.Coils ? ((ElemCnt % 8 == 0) ? ElemCnt / 8 : ElemCnt / 8 + 1) : ElemCnt * 2; ReqPDU = new byte[6 + byteCnt]; ReqPDU[0] = FuncCode; ReqPDU[1] = (byte)(Address / 256); ReqPDU[2] = (byte)(Address % 256); ReqPDU[3] = (byte)(ElemCnt / 256); ReqPDU[4] = (byte)(ElemCnt % 256); ReqPDU[5] = (byte)byteCnt; ModbusUtils.ApplyByteOrder(Data, ReqPDU, 6, byteCnt, ByteOrder); } else { // PDU generation for WriteSingleCoil or WriteSingleRegister ReqPDU = new byte[5]; ReqPDU[0] = FuncCode; ReqPDU[1] = (byte)(Address / 256); ReqPDU[2] = (byte)(Address % 256); if (TableType == TableType.Coils) { ReqPDU[3] = Value > 0 ? (byte)0xFF : (byte)0x00; ReqPDU[4] = 0x00; } else { var data = new byte[] { (byte)(Value / 256), (byte)(Value % 256) }; ModbusUtils.ApplyByteOrder(data, ReqPDU, 3, 2, ByteOrder); } } // setting the length of the response RespPduLen = 5; }
/// <summary> /// Загрузить шаблон устройства /// </summary> public bool Load(string fileName, out string errMsg) { SetToDefault(); try { // загрузка шаблона устройства XmlDocument xmlDoc = new XmlDocument(); xmlDoc.Load(fileName); // загрузка настроек шаблона XmlNode settingsNode = xmlDoc.DocumentElement.SelectSingleNode("Settings"); if (settingsNode != null) { Sett.LoadFromXml((XmlElement)settingsNode); } // загрузка групп элементов XmlNode elemGroupsNode = xmlDoc.DocumentElement.SelectSingleNode("ElemGroups"); if (elemGroupsNode != null) { int kpTagInd = 0; foreach (XmlElement elemGroupElem in elemGroupsNode.ChildNodes) { TableTypes tableType = elemGroupElem.GetAttrAsEnum <TableTypes>("tableType"); ElemGroup elemGroup = new ElemGroup(tableType); elemGroup.Name = elemGroupElem.GetAttribute("name"); elemGroup.Address = (ushort)elemGroupElem.GetAttrAsInt("address"); elemGroup.Active = elemGroupElem.GetAttrAsBool("active", true); elemGroup.StartKPTagInd = kpTagInd; elemGroup.StartSignal = kpTagInd + 1; XmlNodeList elemNodes = elemGroupElem.SelectNodes("Elem"); ElemTypes defElemType = elemGroup.DefElemType; foreach (XmlElement elemElem in elemNodes) { Elem elem = new Elem(); elem.Name = elemElem.GetAttribute("name"); elem.ElemType = elemElem.GetAttrAsEnum("type", defElemType); if (elemGroup.ByteOrderEnabled) { elem.ByteOrderStr = elemElem.GetAttribute("byteOrder"); elem.ByteOrder = ModbusUtils.ParseByteOrder(elem.ByteOrderStr); if (elem.ByteOrder == null) { elem.ByteOrder = Sett.GetDefByteOrder(elem.Length); } } elemGroup.Elems.Add(elem); } if (0 < elemGroup.Elems.Count && elemGroup.Elems.Count <= DataUnit.GetMaxElemCnt(tableType)) { ElemGroups.Add(elemGroup); kpTagInd += elemGroup.Elems.Count; } } } // загрузка команд XmlNode cmdsNode = xmlDoc.DocumentElement.SelectSingleNode("Cmds"); if (cmdsNode != null) { foreach (XmlElement cmdElem in cmdsNode.ChildNodes) { ModbusCmd cmd = new ModbusCmd( cmdElem.GetAttrAsEnum <TableTypes>("tableType"), cmdElem.GetAttrAsBool("multiple"), cmdElem.GetAttrAsInt("elemCnt", 1)); cmd.ElemType = cmdElem.GetAttrAsEnum("elemType", cmd.DefElemType); cmd.Address = (ushort)cmdElem.GetAttrAsInt("address"); cmd.Name = cmdElem.GetAttribute("name"); cmd.CmdNum = cmdElem.GetAttrAsInt("cmdNum"); if (cmd.ByteOrderEnabled) { cmd.ByteOrderStr = cmdElem.GetAttribute("byteOrder"); cmd.ByteOrder = ModbusUtils.ParseByteOrder(cmd.ByteOrderStr); if (cmd.ByteOrder == null) { cmd.ByteOrder = Sett.GetDefByteOrder(cmd.ElemCnt); } } if (0 < cmd.CmdNum && cmd.CmdNum <= ushort.MaxValue) { Cmds.Add(cmd); } } } errMsg = ""; return(true); } catch (Exception ex) { errMsg = ModbusPhrases.LoadTemplateError + ":" + Environment.NewLine + ex.Message; return(false); } }
/// <summary> /// Выполнить запрос в режиме ASCII /// </summary> public bool AsciiRequest(DataUnit dataUnit) { if (!CheckConnection()) { return(false); } bool result = false; // отправка запроса ExecWriteToLog(dataUnit.ReqDescr); Connection.WriteLine(dataUnit.ReqStr, out string logText); ExecWriteToLog(logText); // приём ответа string line = Connection.ReadLine(Timeout, out logText); ExecWriteToLog(logText); int lineLen = line == null ? 0 : line.Length; if (lineLen >= 3) { int aduLen = (lineLen - 1) / 2; if (aduLen == dataUnit.RespAduLen && lineLen % 2 == 1) { // получение ADU ответа byte[] aduBuf = new byte[aduLen]; bool parseOK = true; for (int i = 0, j = 1; i < aduLen && parseOK; i++, j += 2) { try { aduBuf[i] = byte.Parse(line.Substring(j, 2), NumberStyles.HexNumber); } catch { ExecWriteToLog(ModbusPhrases.IncorrectSymbol); parseOK = false; } } if (parseOK) { if (aduBuf[aduLen - 1] == ModbusUtils.CalcLRC(aduBuf, 0, aduLen - 1)) { // расшифровка ответа string errMsg; if (dataUnit.DecodeRespPDU(aduBuf, 1, aduLen - 2, out errMsg)) { ExecWriteToLog(ModbusPhrases.OK); result = true; } else { ExecWriteToLog(errMsg + "!"); } } else { ExecWriteToLog(ModbusPhrases.LrcError); } } } else { ExecWriteToLog(ModbusPhrases.IncorrectAduLength); } } else { ExecWriteToLog(ModbusPhrases.CommErrorWithExclamation); } return(result); }
/// <summary> /// Выполнить запрос в режиме RTU /// </summary> public bool RtuRequest(DataUnit dataUnit) { if (!CheckConnection()) { return(false); } bool result = false; // отправка запроса ExecWriteToLog(dataUnit.ReqDescr); Connection.Write(dataUnit.ReqADU, 0, dataUnit.ReqADU.Length, CommUtils.ProtocolLogFormats.Hex, out string logText); ExecWriteToLog(logText); // приём ответа // считывание начала ответа для определения длины PDU int readCnt = Connection.Read(InBuf, 0, 5, Timeout, CommUtils.ProtocolLogFormats.Hex, out logText); ExecWriteToLog(logText); if (readCnt == 5) { int pduLen; int count; if (InBuf[0] != dataUnit.ReqADU[0]) // проверка адреса устройства в ответе { ExecWriteToLog(ModbusPhrases.IncorrectDevAddr); } else if (!(InBuf[1] == dataUnit.FuncCode || InBuf[1] == dataUnit.ExcFuncCode)) { ExecWriteToLog(ModbusPhrases.IncorrectPduFuncCode); } else { if (InBuf[1] == dataUnit.FuncCode) { // считывание окончания ответа pduLen = dataUnit.RespPduLen; count = dataUnit.RespAduLen - 5; readCnt = Connection.Read(InBuf, 5, count, Timeout, CommUtils.ProtocolLogFormats.Hex, out logText); ExecWriteToLog(logText); } else // устройство вернуло исключение { pduLen = 2; count = 0; readCnt = 0; } if (readCnt == count) { if (InBuf[pduLen + 1] + InBuf[pduLen + 2] * 256 == ModbusUtils.CalcCRC16(InBuf, 0, pduLen + 1)) { // расшифровка ответа string errMsg; if (dataUnit.DecodeRespPDU(InBuf, 1, pduLen, out errMsg)) { ExecWriteToLog(ModbusPhrases.OK); result = true; } else { ExecWriteToLog(errMsg + "!"); } } else { ExecWriteToLog(ModbusPhrases.CrcError); } } else { ExecWriteToLog(ModbusPhrases.CommErrorWithExclamation); } } } else { ExecWriteToLog(ModbusPhrases.CommErrorWithExclamation); } return(result); }
/// <summary> /// Run ASCII query /// </summary> public bool AsciiRequest(DataUnit dataUnit) { if (!CheckConnection()) { return(false); } var result = false; // sending request ExecWriteToLog(dataUnit.ReqDescr); Connection.WriteLine(dataUnit.ReqStr, out string logText); ExecWriteToLog(logText); // reception of the answer string line = Connection.ReadLine(Timeout, out logText); ExecWriteToLog(logText); int lineLen = line == null ? 0 : line.Length; if (lineLen >= 3) { int aduLen = (lineLen - 1) / 2; if (aduLen == dataUnit.RespAduLen && lineLen % 2 == 1) { // getting ADU response var aduBuf = new byte[aduLen]; var parseOK = true; for (int i = 0, j = 1; i < aduLen && parseOK; i++, j += 2) { try { aduBuf[i] = byte.Parse(line.Substring(j, 2), NumberStyles.HexNumber); } catch { ExecWriteToLog(ModbusPhrases.IncorrectSymbol); parseOK = false; } } if (parseOK) { if (aduBuf[aduLen - 1] == ModbusUtils.CalcLRC(aduBuf, 0, aduLen - 1)) { // answer decryption string errMsg; if (dataUnit.DecodeRespPDU(aduBuf, 1, aduLen - 2, out errMsg)) { ExecWriteToLog(ModbusPhrases.OK); result = true; } else { ExecWriteToLog(errMsg + "!"); } } else { ExecWriteToLog(ModbusPhrases.LrcError); } } } else { ExecWriteToLog(ModbusPhrases.IncorrectAduLength); } } else { ExecWriteToLog(ModbusPhrases.CommErrorWithExclamation); } return(result); }
/// <summary> /// Run RTU query /// </summary> public bool RtuRequest(DataUnit dataUnit) { if (!CheckConnection()) { return(false); } var result = false; // sending request ExecWriteToLog(dataUnit.ReqDescr); Connection.Write(dataUnit.ReqADU, 0, dataUnit.ReqADU.Length, CommUtils.ProtocolLogFormats.Hex, out string logText); ExecWriteToLog(logText); // reception of the answer // reading the start of the response to determine the length of the PDU int readCnt = Connection.Read(InBuf, 0, 5, Timeout, CommUtils.ProtocolLogFormats.Hex, out logText); ExecWriteToLog(logText); if (readCnt == 5) { int pduLen; int count; if (InBuf[0] != dataUnit.ReqADU[0]) // checking device address in response { ExecWriteToLog(ModbusPhrases.IncorrectDevAddr); } else if (!(InBuf[1] == dataUnit.FuncCode || InBuf[1] == dataUnit.ExcFuncCode)) { ExecWriteToLog(ModbusPhrases.IncorrectPduFuncCode); } else { if (InBuf[1] == dataUnit.FuncCode) { // read end of response pduLen = dataUnit.RespPduLen; count = dataUnit.RespAduLen - 5; readCnt = Connection.Read(InBuf, 5, count, Timeout, CommUtils.ProtocolLogFormats.Hex, out logText); ExecWriteToLog(logText); } else // device returned exception { pduLen = 2; count = 0; readCnt = 0; } if (readCnt == count) { if (InBuf[pduLen + 1] + InBuf[pduLen + 2] * 256 == ModbusUtils.CalcCRC16(InBuf, 0, pduLen + 1)) { // answer decryption string errMsg; if (dataUnit.DecodeRespPDU(InBuf, 1, pduLen, out errMsg)) { ExecWriteToLog(ModbusPhrases.OK); result = true; } else { ExecWriteToLog(errMsg + "!"); } } else { ExecWriteToLog(ModbusPhrases.CrcError); } } else { ExecWriteToLog(ModbusPhrases.CommErrorWithExclamation); } } } else { ExecWriteToLog(ModbusPhrases.CommErrorWithExclamation); } return(result); }
/// <summary> /// Gets the default number of command elements depending on the element type. /// </summary> public virtual int GetDefElemCnt(ElemType elemType) { return(ModbusUtils.GetQuantity(elemType)); }