Added controller config message

Added circuit data to pool status message
Sanitized byte signedness
Added helper to get pool/spa on/off state from pool status
Added simple color class
Removed debug output
Added basic status output to test program
This commit is contained in:
2018-03-27 17:29:34 -05:00
parent 97c4febdf5
commit ba089e3c00
11 changed files with 427 additions and 40 deletions

21
BodyDataStructure.cs Normal file
View File

@ -0,0 +1,21 @@
using System;
namespace ScreenLogicConnect
{
public class BodyDataStructure
{
public byte m_Pad1;
public byte m_Pad2;
public int m_circuitID;
public byte m_colorPos;
public byte m_colorSet;
public byte m_colorStagger;
public byte m_deviceID;
public short m_dfaultRT;
public byte m_flags;
public byte m_function;
public byte m_interface;
public String m_name;
public byte m_nameIndex;
}
}

View File

@ -54,9 +54,9 @@ namespace ScreenLogicConnect
return (short)((data[startIndex + 1] & 255) + ((short)(((short)((data[startIndex + 0] & 255) + 0)) << 8)));
}
public static sbyte getUnsignedByteFromByteArray(sbyte[] data, int startIndex)
public static byte getUnsignedByteFromByteArray(sbyte[] data, int startIndex)
{
return (sbyte)(data[startIndex] & 255);
return (byte)(data[startIndex] & 255);
}
public static sbyte getHighByte(short v)

View File

@ -0,0 +1,15 @@
namespace ScreenLogicConnect
{
public class CircuitUpdateDataStructure
{
public byte colorPos;
public byte colorSet;
public byte colorStagger;
public byte delay;
public int id;
public int state;
public const int SPA_CIRCUIT_ID = 500;
public const int POOL_CIRCUIT_ID = 505;
}
}

View File

@ -0,0 +1,219 @@
using System;
namespace ScreenLogicConnect.Messages
{
public class GetControllerConfig : HLMessage
{
private const int PUM_CIRC_COUNT = 8;
private BodyDataStructure[] bodyArray;
private int colorCount;
private int m_CircuitCount;
private PentLightColor[] m_ColorArray;
private byte m_ControllerData;
private int m_ControllerID;
private byte m_ControllerType;
private byte m_DegC;
private int m_EquipFlags;
private byte m_HWType;
private int m_InterfaceTabFlags;
private byte[] m_MaxSetPoint;
private byte[] m_MinSetPoint;
private byte[] m_PumpCircArray;
private int m_ShowAlarms;
private String m_genCircuitName;
public const short HLM_POOL_GETCTLRCONFIGQ = (short)12532;
public static GetControllerConfig QUERY(short senderID)
{
return new GetControllerConfig(senderID, (short)HLM_POOL_GETCTLRCONFIGQ);
}
private GetControllerConfig(short senderID, short msgID)
: base(senderID, msgID)
{
}
public GetControllerConfig(sbyte[] header, sbyte[] data)
: base(header, data)
{
}
public GetControllerConfig(HLMessage msg)
: base(msg)
{
}
public override sbyte[] asByteArray()
{
putInteger(0);
putInteger(0);
return base.asByteArray();
}
protected override void decode()
{
int i;
this.m_MinSetPoint = new byte[2];
this.m_MaxSetPoint = new byte[2];
this.startIndex = 0;
this.m_ControllerID = ByteHelper.getIntFromByteArrayLittleEndian(this.data, this.startIndex);
this.startIndex += 4;
this.m_MinSetPoint[0] = ByteHelper.getUnsignedByteFromByteArray(this.data, this.startIndex);
this.startIndex++;
this.m_MaxSetPoint[0] = ByteHelper.getUnsignedByteFromByteArray(this.data, this.startIndex);
this.startIndex++;
this.m_MinSetPoint[1] = ByteHelper.getUnsignedByteFromByteArray(this.data, this.startIndex);
this.startIndex++;
this.m_MaxSetPoint[1] = ByteHelper.getUnsignedByteFromByteArray(this.data, this.startIndex);
this.startIndex++;
this.m_DegC = ByteHelper.getUnsignedByteFromByteArray(this.data, this.startIndex);
this.startIndex++;
this.m_ControllerType = ByteHelper.getUnsignedByteFromByteArray(this.data, this.startIndex);
this.startIndex++;
this.m_HWType = ByteHelper.getUnsignedByteFromByteArray(this.data, this.startIndex);
this.startIndex++;
this.m_ControllerData = ByteHelper.getUnsignedByteFromByteArray(this.data, this.startIndex);
this.startIndex++;
this.m_EquipFlags = ByteHelper.getIntFromByteArrayLittleEndian(this.data, this.startIndex);
this.startIndex += 4;
this.m_genCircuitName = HLMessageTypeHelper.extractString(this.data, ref this.startIndex);
this.m_CircuitCount = ByteHelper.getIntFromByteArrayLittleEndian(this.data, this.startIndex);
this.startIndex += 4;
this.bodyArray = new BodyDataStructure[this.m_CircuitCount];
for (i = 0; i < this.m_CircuitCount; i++)
{
this.bodyArray[i] = new BodyDataStructure();
this.bodyArray[i].m_circuitID = ByteHelper.getIntFromByteArrayLittleEndian(this.data, this.startIndex);
this.startIndex += 4;
this.bodyArray[i].m_name = HLMessageTypeHelper.extractString(this.data, ref this.startIndex);
this.bodyArray[i].m_nameIndex = ByteHelper.getUnsignedByteFromByteArray(this.data, this.startIndex);
this.startIndex++;
this.bodyArray[i].m_function = ByteHelper.getUnsignedByteFromByteArray(this.data, this.startIndex);
this.startIndex++;
this.bodyArray[i].m_interface = ByteHelper.getUnsignedByteFromByteArray(this.data, this.startIndex);
this.startIndex++;
this.bodyArray[i].m_flags = ByteHelper.getUnsignedByteFromByteArray(this.data, this.startIndex);
this.startIndex++;
this.bodyArray[i].m_colorSet = ByteHelper.getUnsignedByteFromByteArray(this.data, this.startIndex);
this.startIndex++;
this.bodyArray[i].m_colorPos = ByteHelper.getUnsignedByteFromByteArray(this.data, this.startIndex);
this.startIndex++;
this.bodyArray[i].m_colorStagger = ByteHelper.getUnsignedByteFromByteArray(this.data, this.startIndex);
this.startIndex++;
this.bodyArray[i].m_deviceID = ByteHelper.getUnsignedByteFromByteArray(this.data, this.startIndex);
this.startIndex++;
this.bodyArray[i].m_dfaultRT = ByteHelper.getShortFromByteArrayAsLittleEndian(this.data, this.startIndex);
this.startIndex += 2;
this.bodyArray[i].m_Pad1 = ByteHelper.getUnsignedByteFromByteArray(this.data, this.startIndex);
this.startIndex++;
this.bodyArray[i].m_Pad2 = ByteHelper.getUnsignedByteFromByteArray(this.data, this.startIndex);
this.startIndex++;
}
this.colorCount = ByteHelper.getIntFromByteArrayLittleEndian(this.data, this.startIndex);
this.startIndex += 4;
this.m_ColorArray = new PentLightColor[this.colorCount];
for (i = 0; i < this.colorCount; i++)
{
String name = HLMessageTypeHelper.extractString(this.data, ref this.startIndex);
RgbColor color = HLMessageTypeHelper.extractColor(this.data, ref this.startIndex);
this.m_ColorArray[i] = new PentLightColor(name, color);
}
this.m_PumpCircArray = new byte[8];
for (i = 0; i < 8; i++)
{
this.m_PumpCircArray[i] = ByteHelper.getUnsignedByteFromByteArray(this.data, this.startIndex);
this.startIndex++;
}
this.m_InterfaceTabFlags = ByteHelper.getIntFromByteArrayLittleEndian(this.data, this.startIndex);
this.startIndex += 4;
this.m_ShowAlarms = ByteHelper.getIntFromByteArrayLittleEndian(this.data, this.startIndex);
this.startIndex += 4;
}
public int getM_ControllerID()
{
return this.m_ControllerID;
}
public byte getM_DegC()
{
return this.m_DegC;
}
public byte getM_ControllerType()
{
return this.m_ControllerType;
}
public byte getM_HWType()
{
return this.m_HWType;
}
public byte getM_ControllerData()
{
return this.m_ControllerData;
}
public int getM_EquipFlags()
{
return this.m_EquipFlags;
}
public String getM_genCircuitName()
{
return this.m_genCircuitName;
}
public int getM_CircuitCount()
{
return this.m_CircuitCount;
}
public byte[] getMinSetPoint()
{
return this.m_MinSetPoint;
}
public byte[] getMaxSetPoint()
{
return this.m_MaxSetPoint;
}
public BodyDataStructure[] getBodyArray()
{
return this.bodyArray;
}
public int getM_interfaceTabFlags()
{
return this.m_InterfaceTabFlags;
}
public int getM_showAlarms()
{
return this.m_ShowAlarms;
}
public PentLightColor[] getColorLightList()
{
return this.m_ColorArray;
}
public int getColorCount()
{
return this.colorCount;
}
public int getPumpCirCount()
{
return 8;
}
public byte[] getPumpCirList()
{
return this.m_PumpCircArray;
}
}
}

27
Messages/GetMode.cs Normal file
View File

@ -0,0 +1,27 @@
namespace ScreenLogicConnect.Messages
{
public class GetMode : HLMessage
{
public const short HLM_MODE_GETMODEQ = 110;
public static GetMode QUERY(short senderID)
{
return new GetMode(senderID, (short)HLM_MODE_GETMODEQ);
}
private GetMode(short senderID, short msgID)
: base(senderID, msgID)
{
}
public GetMode(sbyte[] header, sbyte[] data)
: base(header, data)
{
}
public GetMode(HLMessage msg)
: base(msg)
{
}
}
}

View File

@ -1,16 +1,18 @@
namespace ScreenLogicConnect.Messages
using System.Linq;
namespace ScreenLogicConnect.Messages
{
public class GetPoolStatus : HLMessage
{
//private CircuitUpdateDataStructure[] circuitArray;
private CircuitUpdateDataStructure[] circuitArray;
private int m_AirTemp;
private int m_Alarms;
private int m_BodiesCount;
private int m_CircuitCount;
private sbyte m_CleanerDelay;
private byte m_CleanerDelay;
private int[] m_CoolSetPoint;
private int[] m_CurrentTemp;
private sbyte m_FreezeMode;
private byte m_FreezeMode;
private int[] m_HeatMode;
private int[] m_HeatStatus;
private int m_ORP;
@ -18,13 +20,13 @@
private int m_Ok;
private int m_PH;
private int m_PHTank;
private sbyte m_Padding;
private sbyte m_PoolDelay;
private sbyte m_Remotes;
private byte m_Padding;
private byte m_PoolDelay;
private byte m_Remotes;
private int m_SaltPPM;
private int m_Saturation;
private int[] m_SetPoint;
private sbyte m_SpaDelay;
private byte m_SpaDelay;
public static GetPoolStatus QUERY(short senderID)
{
@ -108,21 +110,21 @@
}
int m_CircuitCount = ByteHelper.getIntFromByteArrayLittleEndian(this.data, this.startIndex);
this.startIndex += 4;
//this.circuitArray = new CircuitUpdateDataStructure[m_CircuitCount];
this.circuitArray = new CircuitUpdateDataStructure[m_CircuitCount];
for (i = 0; i < m_CircuitCount; i++)
{
//this.circuitArray[i] = new CircuitUpdateDataStructure();
//this.circuitArray[i].id = ByteHelper.getIntFromByteArrayLittleEndian(this.data, this.startIndex);
this.circuitArray[i] = new CircuitUpdateDataStructure();
this.circuitArray[i].id = ByteHelper.getIntFromByteArrayLittleEndian(this.data, this.startIndex);
this.startIndex += 4;
//this.circuitArray[i].state = ByteHelper.getIntFromByteArrayLittleEndian(this.data, this.startIndex);
this.circuitArray[i].state = ByteHelper.getIntFromByteArrayLittleEndian(this.data, this.startIndex);
this.startIndex += 4;
//this.circuitArray[i].colorSet = ByteHelper.getUnsignedByteFromByteArray(this.data, this.startIndex);
this.circuitArray[i].colorSet = ByteHelper.getUnsignedByteFromByteArray(this.data, this.startIndex);
this.startIndex++;
//this.circuitArray[i].colorPos = ByteHelper.getUnsignedByteFromByteArray(this.data, this.startIndex);
this.circuitArray[i].colorPos = ByteHelper.getUnsignedByteFromByteArray(this.data, this.startIndex);
this.startIndex++;
//this.circuitArray[i].colorStagger = ByteHelper.getUnsignedByteFromByteArray(this.data, this.startIndex);
this.circuitArray[i].colorStagger = ByteHelper.getUnsignedByteFromByteArray(this.data, this.startIndex);
this.startIndex++;
//this.circuitArray[i].delay = ByteHelper.getUnsignedByteFromByteArray(this.data, this.startIndex);
this.circuitArray[i].delay = ByteHelper.getUnsignedByteFromByteArray(this.data, this.startIndex);
this.startIndex++;
}
this.m_PH = ByteHelper.getIntFromByteArrayLittleEndian(this.data, this.startIndex);
@ -146,32 +148,32 @@
return this.m_Ok;
}
public sbyte getM_FreezeMode()
public byte getM_FreezeMode()
{
return this.m_FreezeMode;
}
public sbyte getM_Remotes()
public byte getM_Remotes()
{
return this.m_Remotes;
}
public sbyte getM_PoolDelay()
public byte getM_PoolDelay()
{
return this.m_PoolDelay;
}
public sbyte getM_SpaDelay()
public byte getM_SpaDelay()
{
return this.m_SpaDelay;
}
public sbyte getM_CleanerDelay()
public byte getM_CleanerDelay()
{
return this.m_CleanerDelay;
}
public sbyte getM_Padding()
public byte getM_Padding()
{
return this.m_Padding;
}
@ -245,12 +247,12 @@
{
return this.m_Alarms;
}
/*
public CircuitUpdateDataStructure[] getCircuitArray()
{
return this.circuitArray;
}
*/
public CircuitUpdateDataStructure[] getCircuitArray()
{
return this.circuitArray;
}
public bool isDeviceready()
{
return this.m_Ok == 1;
@ -265,5 +267,15 @@
{
return this.m_Ok == 3;
}
public bool isSpaActive()
{
return this.circuitArray != null && this.circuitArray.Any(x => x.id == CircuitUpdateDataStructure.SPA_CIRCUIT_ID && x.state == 1);
}
public bool isPoolActive()
{
return this.circuitArray != null && this.circuitArray.Any(x => x.id == CircuitUpdateDataStructure.POOL_CIRCUIT_ID && x.state == 1);
}
}
}

View File

@ -45,8 +45,8 @@ namespace ScreenLogicConnect.Messages
idx = ((startIndex + bufferLength) + alignToNext4Boundary(bufferLength));
return result;
}
/*
public static int extractColor(sbyte[] data, ref int idx)
public static RgbColor extractColor(sbyte[] data, ref int idx)
{
int startIndex = idx;
int r = ByteHelper.getIntFromByteArrayLittleEndian(data, startIndex);
@ -55,9 +55,9 @@ namespace ScreenLogicConnect.Messages
startIndex += 4;
int b = ByteHelper.getIntFromByteArrayLittleEndian(data, startIndex);
idx = startIndex + 4;
return Color.rgb(r, g, b);
return new RgbColor((byte)(r & 0xff), (byte)(g & 0xff), (byte)(b & 0xff));
}
*/
private static int alignToNext4Boundary(int val)
{
int sub = val % 4;

21
PentLightColor.cs Normal file
View File

@ -0,0 +1,21 @@
using System;
namespace ScreenLogicConnect
{
public class PentLightColor
{
public RgbColor color { get; private set; }
public String name { get; private set; }
public PentLightColor(String name, RgbColor color)
{
this.name = name;
this.color = color;
}
public override string ToString()
{
return $"Name: {name}, color: {color}";
}
}
}

21
RgbColor.cs Normal file
View File

@ -0,0 +1,21 @@
namespace ScreenLogicConnect
{
public class RgbColor
{
public byte r { get; private set; }
public byte g { get; private set; }
public byte b { get; private set; }
public RgbColor(byte inR, byte inG, byte inB)
{
r = inR;
g = inG;
b = inB;
}
public override string ToString()
{
return $"R {r}, G {g}, B {b}";
}
}
}

View File

@ -1,4 +1,5 @@
using System.Threading.Tasks;
using System;
using System.Threading.Tasks;
namespace Test
{
@ -14,6 +15,41 @@ namespace Test
var connection = new ScreenLogicConnect.UnitConnection();
await connection.ConnectTo(server);
var status = connection.GetPoolStatus();
var config = connection.GetControllerConfig();
var degSymbol = config.getM_DegC() == 1 ? "C" : "F";
Console.WriteLine($"Air temp: {status.getM_AirTemp()} degrees {degSymbol}");
var currTempList = status.getM_CurrentTemp();
int poolTemp = 0;
int spaTemp = 0;
if (currTempList.Length > 0)
{
poolTemp = currTempList[0];
}
if (currTempList.Length > 1)
{
spaTemp = currTempList[1];
}
if (poolTemp != 0)
{
Console.WriteLine($"Pool temp: {poolTemp} degrees {degSymbol}{(status.isPoolActive() ? "" : " (Last)")}");
}
else
{
Console.WriteLine("Couldn't get pool temperature.");
}
if (spaTemp != 0)
{
Console.WriteLine($"Spa temp: {spaTemp} degrees {degSymbol}{(status.isSpaActive() ? "" : " (Last)")}");
}
else
{
Console.WriteLine("Couldn't get spa temperature.");
}
Console.WriteLine($"ORP: {status.getM_ORP()}");
Console.WriteLine($"pH: {status.getM_PH() / 100.0f:0.00}");
Console.WriteLine($"Salt: {status.getM_SaltPPM() * 50} PPM");
Console.WriteLine($"Saturation: {status.getM_Saturation() / 100.0f}");
break;
}
}

View File

@ -1,4 +1,5 @@
using System;
using System.Diagnostics;
using System.Net.Sockets;
using System.Threading.Tasks;
@ -22,24 +23,38 @@ namespace ScreenLogicConnect
var stream = client.GetStream();
stream.Write(connMsg, 0, connMsg.Length);
stream.SendHLMessage(Messages.ChallengeString.QUERY(0));
Console.WriteLine("sent challenge string");
Debug.WriteLine("sent challenge string");
var recvBuf = new byte[1024];
var readBytes = stream.Read(recvBuf, 0, recvBuf.Length);
Console.WriteLine("read {0}", readBytes);
Debug.WriteLine("read {0}", readBytes);
stream.SendHLMessage(createLoginMessage(new sbyte[16]));
Console.WriteLine("sent login message");
Debug.WriteLine("sent login message");
readBytes = stream.Read(recvBuf, 0, recvBuf.Length);
Console.WriteLine("read {0}", readBytes);
Debug.WriteLine("read {0}", readBytes);
}
public Messages.GetPoolStatus GetPoolStatus()
{
client.GetStream().SendHLMessage(Messages.GetPoolStatus.QUERY(0));
Console.WriteLine("sent status message");
Debug.WriteLine("sent status message");
return new Messages.GetPoolStatus(getMessage(client.GetStream()));
}
public Messages.GetControllerConfig GetControllerConfig()
{
client.GetStream().SendHLMessage(Messages.GetControllerConfig.QUERY(0));
Debug.WriteLine("sent controller config message");
return new Messages.GetControllerConfig(getMessage(client.GetStream()));
}
public Messages.GetMode GetMode()
{
client.GetStream().SendHLMessage(Messages.GetMode.QUERY(0));
Debug.WriteLine("sent get-mode message");
return new Messages.GetMode(getMessage(client.GetStream()));
}
private static Messages.HLMessage getMessage(NetworkStream ns)
{
int bytesRead = 0;