mirror of
https://github.com/ClassicDIY/ModbusTool.git
synced 2026-10-19 23:01:15 +08:00
249 lines
7.7 KiB
C#
249 lines
7.7 KiB
C#
|
|
/*
|
|
* Copyright 2012 Mario Vernari (http://cetdevelop.codeplex.com/)
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
/**
|
|
* 20/Apr/2012
|
|
* Cut off the server-side codec, being not yet implemented
|
|
**/
|
|
namespace ModbusLib.Protocols
|
|
{
|
|
public class ModbusRtuCodec
|
|
: ModbusCodecBase, IProtocolCodec
|
|
{
|
|
#region IProtocolCodec members
|
|
|
|
void IProtocolCodec.ClientEncode(CommDataBase data)
|
|
{
|
|
var client = (ModbusClient)data.OwnerProtocol;
|
|
var command = (ModbusCommand)data.UserData;
|
|
var fncode = command.FunctionCode;
|
|
|
|
//encode the command body, if applies
|
|
var body = new ByteArrayWriter();
|
|
var codec = CommandCodecs[fncode];
|
|
if (codec != null)
|
|
codec.ClientEncode(command, body);
|
|
|
|
//create a writer for the outgoing data
|
|
var writer = new ByteArrayWriter();
|
|
|
|
//unit identifier (address)
|
|
writer.WriteByte(client.Address);
|
|
|
|
//function code
|
|
writer.WriteByte(fncode);
|
|
|
|
//body data
|
|
writer.WriteBytes(body);
|
|
|
|
//CRC-16
|
|
ushort crc = ByteArrayHelpers.CalcCRC16(
|
|
writer.ToArray(),
|
|
0,
|
|
writer.Length);
|
|
|
|
writer.WriteInt16LE((short)crc);
|
|
|
|
data.OutgoingData = writer.ToReader();
|
|
}
|
|
|
|
|
|
|
|
CommResponse IProtocolCodec.ClientDecode(CommDataBase data)
|
|
{
|
|
var client = (ModbusClient)data.OwnerProtocol;
|
|
var command = (ModbusCommand)data.UserData;
|
|
var incoming = data.IncomingData;
|
|
var bodyLen = incoming.Length - 4;
|
|
|
|
//validate address first
|
|
if (bodyLen >= 0 &&
|
|
incoming.ReadByte() == client.Address)
|
|
{
|
|
//extract function code
|
|
var fncode = incoming.ReadByte();
|
|
|
|
//extract the message body
|
|
var body = new ByteArrayReader(incoming.ReadBytes(bodyLen));
|
|
|
|
//calculate the CRC-16 over the received stream
|
|
ushort crc = ByteArrayHelpers.CalcCRC16(
|
|
incoming.ToArray(),
|
|
0,
|
|
incoming.Length - 2);
|
|
|
|
//validate the CRC-16
|
|
short u = incoming.ReadInt16LE();
|
|
if (u == (short)crc)
|
|
{
|
|
//message looks consistent (although the body can be empty)
|
|
if ((fncode & 0x7F) == command.FunctionCode)
|
|
{
|
|
if (fncode <= 0x7F)
|
|
{
|
|
//
|
|
//encode the command body, if applies
|
|
var codec = CommandCodecs[fncode];
|
|
if (codec != null)
|
|
codec.ClientDecode(command, body);
|
|
|
|
return new CommResponse(
|
|
data,
|
|
CommResponse.Ack);
|
|
}
|
|
else
|
|
{
|
|
//exception
|
|
if (incoming.CanRead(1))
|
|
command.ExceptionCode = incoming.ReadByte();
|
|
|
|
return new CommResponse(
|
|
data,
|
|
CommResponse.Critical);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return new CommResponse(
|
|
data,
|
|
CommResponse.Unknown);
|
|
}
|
|
|
|
|
|
|
|
void IProtocolCodec.ServerEncode(CommDataBase data)
|
|
{
|
|
var server = (ModbusServer)data.OwnerProtocol;
|
|
var command = (ModbusCommand)data.UserData;
|
|
var fncode = command.FunctionCode;
|
|
|
|
//encode the command body, if applies
|
|
var body = new ByteArrayWriter();
|
|
var codec = CommandCodecs[fncode];
|
|
if (codec != null)
|
|
codec.ServerEncode(command, body);
|
|
|
|
//calculate length field
|
|
var length = (command.ExceptionCode == 0)
|
|
? 2 + body.Length
|
|
: 3;
|
|
|
|
//create a writer for the outgoing data
|
|
var writer = new ByteArrayWriter();
|
|
|
|
//unit identifier (address)
|
|
writer.WriteByte(server.Address);
|
|
|
|
if (command.ExceptionCode == 0)
|
|
{
|
|
//function code
|
|
writer.WriteByte(command.FunctionCode);
|
|
|
|
//body data
|
|
writer.WriteBytes(body);
|
|
}
|
|
else
|
|
{
|
|
//function code
|
|
writer.WriteByte((byte)(command.FunctionCode | 0x80));
|
|
|
|
//exception code
|
|
writer.WriteByte(command.ExceptionCode);
|
|
}
|
|
|
|
//CRC-16
|
|
ushort crc = ByteArrayHelpers.CalcCRC16(
|
|
writer.ToArray(),
|
|
0,
|
|
writer.Length);
|
|
|
|
writer.WriteInt16LE((short)crc);
|
|
|
|
data.OutgoingData = writer.ToReader();
|
|
}
|
|
|
|
|
|
|
|
CommResponse IProtocolCodec.ServerDecode(CommDataBase data)
|
|
{
|
|
var server = (ModbusServer)data.OwnerProtocol;
|
|
var incoming = data.IncomingData;
|
|
|
|
//validate header first
|
|
var length = incoming.Length;
|
|
if (length < 4)
|
|
goto LabelUnknown;
|
|
|
|
//address
|
|
var address = incoming.ReadByte();
|
|
|
|
if (address == server.Address)
|
|
{
|
|
//function code
|
|
var fncode = incoming.ReadByte();
|
|
|
|
if (CommandCodecs.ContainsKey(fncode))
|
|
{
|
|
//create a new command
|
|
var command = new ModbusCommand(fncode);
|
|
data.UserData = command;
|
|
command.QueryTotalLength = 6; //= addr + fn + offset + crc
|
|
|
|
//get the command codec
|
|
var codec = CommandCodecs[fncode];
|
|
|
|
//decode the command, where possible
|
|
var body = new ByteArrayReader(incoming.ReadBytes(length - 4));
|
|
codec.ServerDecode(command, body);
|
|
|
|
//calculate the CRC-16 over the received stream
|
|
ushort crcCalc = ByteArrayHelpers.CalcCRC16(
|
|
incoming.ToArray(),
|
|
0,
|
|
command.QueryTotalLength - 2);
|
|
|
|
//validate the CRC-16
|
|
var crcRead = ByteArrayHelpers.ReadInt16LE(
|
|
((IByteArray)incoming).Data,
|
|
command.QueryTotalLength - 2);
|
|
|
|
if (crcRead == (short)crcCalc)
|
|
{
|
|
return new CommResponse(
|
|
data,
|
|
CommResponse.Ack);
|
|
}
|
|
}
|
|
}
|
|
|
|
//exception
|
|
return new CommResponse(
|
|
data,
|
|
CommResponse.Ignore);
|
|
|
|
LabelUnknown:
|
|
return new CommResponse(
|
|
data,
|
|
CommResponse.Unknown);
|
|
}
|
|
|
|
#endregion
|
|
|
|
}
|
|
}
|