GXDLT645 parser = new GXDLT645();
int addr = 0;//TODO: Update register address to read here.
byte[] data = parser.ReadValue(addr);
lock (media.Synchronous)
{
media.Send(data, null);
ReceiveParameters<byte[]> p = new ReceiveParameters<byte[]>()
{
Eop = media.Eop,
WaitTime = device.WaitTime
};
while (!parser.IsPacketComplete(addr, p.Reply)))
{
if (!media.Receive<byte[]>(p))
{
break;
}
}
//
// --------------------------------------------------------------------------
// Gurux Ltd
//
//
//
// Filename: $HeadURL$
//
// Version: $Revision$,
// $Date$
// $Author$
//
// Copyright (c) Gurux Ltd
//
//---------------------------------------------------------------------------
//
// DESCRIPTION
//
// This file is a part of Gurux Device Framework.
//
// Gurux Device Framework is Open Source software; you can redistribute it
// and/or modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; version 2 of the License.
// Gurux Device Framework is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
// See the GNU General Public License for more details.
//
// More information of Gurux products: http://www.gurux.org
//
// This code is licensed under the GNU General Public License v2.
// Full text may be retrieved at http://www.gnu.org/licenses/gpl-2.0.txt
//---------------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
namespace Gurux.DLT645
{
public class GXDLT645
{
string m_OperatorCode, m_MeterPassword, m_RelayControlPassword, m_LowClassPassword;
enum Function
{
Read = 1,
ReadSubsequent = 2,
ReRead = 3,
Write = 4,
CorrectTimeByBroadcast = 8,
WriteDeviceAddress = 10,
ChangeCommunicationSpeed = 12,
ChangePassword = 15,
ClearMaximumDemand = 16
}
/// <summary>
/// Make control code.
/// </summary>
/// <param name="func"></param>
/// <param name="send"></param>
/// <param name="subSequent"></param>
/// <returns></returns>
byte MakeControlCode(Function func, bool send, bool subSequent)
{
byte value = (byte)((byte)func & 0x1F);
if (!send)
{
value += 0x80;
}
if (subSequent)
{
value += 0x20;
}
return value;
}
/// <summary>
/// Device OperatorCode.
/// </summary>
/// <remarks>
/// Operator Code maximum length is four charachters.
/// </remarks>
public string OperatorCode
{
get
{
return m_OperatorCode;
}
set
{
if (m_OperatorCode != null && m_OperatorCode.Length > 4)
{
throw new ArgumentException("Operator Code is maximum four charachters.");
}
m_OperatorCode = value;
}
}
/// <summary>
/// Collector address.
/// </summary>
public ulong CollectorAddress
{
get;
set;
}
/// <summary>
/// Receiver awake bytes.
/// </summary>
/// <remarks>
/// If awaker is used 0xFE is usually send one to four times.
/// </remarks>
public byte[] FrontLeadingBytes
{
get;
set;
}
/// <summary>
/// Password of the meter.
/// </summary>
/// <remarks>
/// Maimum size is eight charachters.
/// </remarks>
public string MeterPassword
{
get
{
return m_MeterPassword;
}
set
{
if (value != null && value.Length > 8)
{
throw new ArgumentException("Length of the meter password is maximum eight charachters.");
}
m_MeterPassword = value;
}
}
/// <summary>
/// Password of the relay control.
/// </summary>
/// <remarks>
/// Maimum size is eight charachters.
/// </remarks>
public string RelayControlPassword
{
get
{
return m_RelayControlPassword;
}
set
{
if (value != null && value.Length > 8)
{
throw new ArgumentException("Length of the relay control password is maximum eight charachters.");
}
m_RelayControlPassword = value;
}
}
/// <summary>
/// Password of the low class.
/// </summary>
/// <remarks>
/// Maimum size is eight charachters.
/// </remarks>
public string LowClassPassword
{
get
{
return m_LowClassPassword;
}
set
{
if (value != null && value.Length > 8)
{
throw new ArgumentException("Length of the low class password is maximum eight charachters.");
}
m_LowClassPassword = value;
}
}
const byte Bop = 0x68;
const byte Eop = 0x16;
/// <summary>
/// Are BOP, EOP and Checksum added to data.
/// </summary>
[DefaultValue(false)]
public bool IgnoreFrame
{
get;
set;
}
public ulong DeviceAddress
{
get;
set;
}
void AddFrame(List<byte> data)
{
if (!IgnoreFrame)
{
int pos = 0;
if (FrontLeadingBytes != null)
{
pos = FrontLeadingBytes.Length;
}
data.Insert(pos, Bop);
byte crc = 0;
for (int a = pos; a != data.Count; ++a)
{
crc = (byte)((crc + data[a]) & 0xFF);
}
data.Add(crc);
data.Add(Eop);
}
}
void AddDeviceAddress(List<byte> data)
{
int add = 0;
if (CollectorAddress != 0)
{
add = 0x33;
}
string str = string.Format("{0:000000000000}", DeviceAddress);
for (int pos = str.Length; pos > 0; pos -= 2)
{
string tmp = str.Substring(pos - 2, 2);
data.Add((byte)(Convert.ToInt32(tmp, 16) + add));
}
}
void AddDataID(List<byte> data, ulong dataID)
{
string str = string.Format("{0:x8}", dataID);
for (int pos = str.Length; pos > 0; pos -= 2)
{
string tmp = str.Substring(pos - 2, 2);
data.Add((byte)(Convert.ToInt32(tmp, 16) + 0x33));
}
}
byte[] MakePacket(Function func, ulong dataID, object value)
{
List<byte> data = new List<byte>(11);
if (FrontLeadingBytes != null)
{
data.AddRange(FrontLeadingBytes);
}
int LenPos = 0;
if (CollectorAddress != 0)
{
data.Add((byte)(CollectorAddress & 0xff));
data.Add((byte)((CollectorAddress >> 8) & 0xff));
data.Add((byte)((CollectorAddress >> 16) & 0xff));
data.Add((byte)((CollectorAddress >> 24) & 0xff));
data.Add((byte)((CollectorAddress >> 32) & 0xff));
data.Add((byte)((CollectorAddress >> 40) & 0xff));
data.Add(Bop);
// Add Control Code.
data.Add(MakeControlCode(func, true, false));
LenPos = data.Count;
AddDataID(data, dataID);
AddDeviceAddress(data);
}
else
{
AddDeviceAddress(data);
data.Add(Bop);
// Add Control Code.
data.Add(MakeControlCode(func, true, false));
LenPos = data.Count;
AddDataID(data, dataID);
}
if ((func & Function.Write) != 0)
{
//Password is always eight digits.
string tmp = "";
for (int pos = MeterPassword.Length; pos != 8; ++pos)
{
tmp += "0" + tmp;
}
tmp += MeterPassword;
for (int pos = tmp.Length; pos > 0; pos -= 2)
{
string tmp2 = tmp.Substring(pos - 2, 2);
data.Add((byte)(Convert.ToInt32(tmp2, 16) + 0x33));
}
//Operator code is always found digits.
tmp = "";
for (int a = 0; a != 4 - OperatorCode.Length; ++a)
{
tmp += "0";
}
tmp += OperatorCode;
char[] items = tmp.ToCharArray();
Array.Reverse(items);
foreach (byte it in items)
{
data.Add((byte)(it + 0x33));
}
}
if (value != null)
{
if (value is DateTime)
{
DateTime dt = (DateTime)value;
data.Add((byte)((int)dt.DayOfWeek + 0x33));
data.Add((byte)(dt.Day + 0x33));
data.Add((byte)(dt.Month + 0x33));
data.Add((byte)((dt.Year - 2000 + 6 + 0x33)));
}
}
data.Insert(LenPos, (byte)(data.Count - LenPos));
AddFrame(data);
return data.ToArray();
}
/// <summary>
/// Generates read message.
/// </summary>
/// <param name="dataID"></param>
/// <returns></returns>
/// <seealso cref="GetValue"/>
public byte[] ReadValue(ulong dataID)
{
return MakePacket(Function.Read | Function.ClearMaximumDemand, dataID, null);
}
/// <summary>
/// Generates Write message.
/// </summary>
/// <param name="dataID"></param>
/// <param name="value"></param>
/// <returns></returns>
public byte[] WriteValue(ulong dataID, object value)
{
return MakePacket(Function.Write | Function.ClearMaximumDemand, dataID, value);
}
/// <summary>
/// Check is there more data available.
/// </summary>
/// <param name="dataID"></param>
/// <param name="reply"></param>
/// <returns></returns>
public bool IsMoreDataAvailable(ulong dataID, byte[] reply)
{
int index = -1;
int StartIndex = -1;
int cc = GetControlCode(reply, ref index, out StartIndex);
if ((cc & 0x80) != 0x80)
{
throw new Exception("Parse Failed. Not a reply packet.");
}
return (cc & 0x20) == 0x20;
}
/// <summary>
/// Generates an acknowledgment message, with which the server is informed to
/// send next packets.
/// </summary>
/// <returns></returns>
public byte[] ReceiverReady()
{
throw new NotImplementedException();
}
byte GetControlCode(byte[] reply, ref int index, out int StartIndex)
{
if (IgnoreFrame)
{
index = StartIndex = 0;
}
else
{
StartIndex = -1;
//Find Bop.
for (int pos = 0; pos != reply.Length; ++pos)
{
if (reply[pos] == Bop)
{
StartIndex = pos;
index = pos + 1;
break;
}
}
}
if (StartIndex == -1)
{
throw new OutOfMemoryException("BOP Not found.");
}
//Check that device addresses matchs.
ulong address = 0;
for (int pos = 0; pos != 6; ++pos)
{
byte val = (byte)reply[pos + index];
int tmp = (val >> 4) * 10 | val & 0xF;
address += (ulong)(tmp * Math.Pow(100, pos));
}
index += 6;
if (address != DeviceAddress)
{
throw new Exception("Parse Failed. Invalid Address.");
}
if (reply[index++] != 0x68)
{
throw new Exception("Parse Failed. Invalid data.");
}
//Get Control Code.
return reply[index++];
}
public bool IsPacketComplete(ulong dataID, byte[] reply)
{
try
{
if (reply == null)
{
return false;
}
GetValue(dataID, reply, typeof(byte[]));
return true;
}
catch
{
return false;
}
}
public bool IsError(byte[] reply)
{
int index = -1;
int StartIndex = -1;
int cc = GetControlCode(reply, ref index, out StartIndex);
return (cc & 0x40) == 0x40;
}
/// <summary>
/// Removes the DL/T 645 header from the packet, and returns payload data only.
/// </summary>
/// <param name="packet">The received packet, from the device, as byte array.</param>
/// <param name="data">The exported data.</param>
/// <returns>Received Data</returns>
public bool GetDataFromPacket(ulong dataID, byte[] reply, ref byte[] allData)
{
throw new NotImplementedException();
}
/// <summary>
/// Parse value.
/// </summary>
/// <param name="dataID"></param>
/// <param name="reply"></param>
/// <returns></returns>
/// <seealso cref="ReadValue"/>
public object GetValue(ulong dataID, byte[] reply, Type type)
{
int index = -1;
int StartIndex = -1;
int cc = GetControlCode(reply, ref index, out StartIndex);
if ((cc & 0x80) != 0x80)
{
throw new Exception("Parse Failed. Not a reply packet.");
}
int len = reply[index++];
if (reply.Length < index + len + (int)(IgnoreFrame ? 0 : 1))
{
throw new OutOfMemoryException();
}
int crcPos = index + len;
if (!IgnoreFrame)
{
//Check CRC.
byte readCrc = reply[crcPos];
byte countCrc = 0;
for (int pos = StartIndex; pos != index + len; ++pos)
{
countCrc = (byte)((countCrc + reply[pos]) & 0xFF);
}
if (countCrc != readCrc)
{
throw new Exception("Parse Failed. CRC do not match.");
}
if (reply[index + len + 1] != 0x16)
{
throw new Exception("Parse Failed. EOP not found.");
}
}
if (index + 2 < reply.Length)
{
ulong address = 0;
for (int pos = 0; pos != 4; ++pos)
{
byte val = (byte)(reply[index++] - 0x33);
address += (ulong)(val << (8 * pos));
}
if (dataID != address)
{
throw new Exception("Parse Failed. data identification do not match.");
}
byte[] buff = new byte[crcPos - index];
int a = 0;
while (index != crcPos)
{
buff[a++] = (byte)(reply[index++] - 0x33);
}
int cnt;
return Gurux.Shared.GXCommon.ByteArrayToObject(buff, type, out cnt);
}
return null;
}
}
}