Files
2015-10-07 11:15:30 -04:00

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
}
}