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

257 lines
7.6 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.
*/
/**
* 09/Apr/2012:
* The original class ModbusTcpCodec has been splitted in two parts,
* a base and its derivation. In this way, the base ModbusCodecBase
* can be shared for both TCP and RTU mode
**/
using System;
namespace ModbusLib.Protocols
{
public class ModbusTcpCodec
: 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);
//calculate length field
var length = 2 + body.Length;
//create a writer for the outgoing data
var writer = new ByteArrayWriter();
//transaction-id
writer.WriteUInt16BE((ushort)command.TransId);
//protocol-identifier (always zero)
writer.WriteInt16BE(0);
//message length
writer.WriteInt16BE((short)length);
//unit identifier (address)
writer.WriteByte(client.Address);
//function code
writer.WriteByte(fncode);
//body data
writer.WriteBytes(body);
data.OutgoingData = writer.ToReader();
}
CommResponse IProtocolCodec.ClientDecode(CommDataBase data)
{
var client = (ModbusClient)data.OwnerProtocol;
var command = (ModbusCommand)data.UserData;
var incoming = data.IncomingData;
//validate header first
if (incoming.Length >= 6 &&
incoming.ReadUInt16BE() == (ushort)command.TransId &&
incoming.ReadInt16BE() == 0 //protocol-identifier
)
{
//message length
var length = incoming.ReadInt16BE();
//validate address
if (incoming.Length >= (length + 6) &&
incoming.ReadByte() == client.Address
)
{
//validate function code
var fncode = incoming.ReadByte();
if ((fncode & 0x7F) == command.FunctionCode)
{
if (fncode <= 0x7F)
{
//
var body = new ByteArrayReader(incoming.ReadToEnd());
//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
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();
//transaction-id
writer.WriteUInt16BE((ushort)command.TransId);
//protocol-identifier
writer.WriteInt16BE(0);
//message length
writer.WriteInt16BE((short)length);
//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);
}
data.OutgoingData = writer.ToReader();
}
CommResponse IProtocolCodec.ServerDecode(CommDataBase data)
{
var server = (ModbusServer)data.OwnerProtocol;
var incoming = data.IncomingData;
//validate header first
if (incoming.Length < 6)
goto LabelUnknown;
//transaction-id
var transId = incoming.ReadUInt16BE();
//protocol-identifier
var protId = incoming.ReadInt16BE();
if (protId != 0)
goto LabelIgnore;
//message length
var length = incoming.ReadInt16BE();
if (incoming.Length < (length + 6))
goto LabelUnknown;
//address
var address = incoming.ReadByte();
if (address == server.Address)
{
//function code
var fncode = incoming.ReadByte();
if (!CommandCodecs.ContainsKey(fncode))
{
return new CommResponse(null, CommResponse.Unknown);
}
//create a new command
var command = new ModbusCommand(fncode);
data.UserData = command;
command.TransId = transId;
//
var body = new ByteArrayReader(incoming.ReadToEnd());
//encode the command body, if applies
var codec = CommandCodecs[fncode];
if (codec != null)
codec.ServerDecode(command, body);
return new CommResponse(
data,
CommResponse.Ack);
}
//exception
LabelIgnore:
return new CommResponse(
data,
CommResponse.Ignore);
LabelUnknown:
return new CommResponse(
data,
CommResponse.Unknown);
}
#endregion
}
}