Skip to content

Commit

Permalink
Refactor library to support multiple drivers. (#62)
Browse files Browse the repository at this point in the history
* Refactor library to support multiple drivers.

* Patch server class to handle multiple hosts + check for used ports

* Bump schema + swtich to yao-pkg/pkg

---------

Co-authored-by: Marcus Davies <[email protected]>
  • Loading branch information
spudwebb and marcus-j-davies authored Mar 12, 2024
1 parent 6c3db91 commit 3fe7bdc
Show file tree
Hide file tree
Showing 8 changed files with 264 additions and 178 deletions.
8 changes: 4 additions & 4 deletions PSI/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@
"name": "server",
"bin": "./server.js",
"dependencies": {
"@zwave-js/server": "1.33.0",
"zwave-js": "12.2.1"
"@zwave-js/server": "1.34.0",
"zwave-js": "12.4.4"
},
"devDependencies": {
"pkg": "^5.8.1",
"esbuild": "^0.19.5"
"@yao-pkg/pkg": "^5.11.4",
"esbuild": "^0.20.1"
},
"scripts": {
"build": "npm run do_esbuild && npm run do_pkgbuild",
Expand Down
127 changes: 64 additions & 63 deletions Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/Controller.cs

Large diffs are not rendered by default.

79 changes: 51 additions & 28 deletions Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/Driver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ namespace ZWaveJS.NET
{
public class Driver
{
internal static volatile Driver Instance;
// Global List of Socket Ports that are registered
internal static List<int> UsedPorts = new List<int>();

internal Websocket.Client.WebsocketClient ClientWebSocket;
internal Dictionary<Guid, Action<JObject>> Callbacks;
Expand All @@ -22,13 +23,15 @@ public class Driver
private Dictionary<string, Action<JObject>> NodeEventMap;
private Dictionary<string, Action<JObject>> ControllerEventMap;
private Dictionary<string, Action<JObject>> DriverEventMap;
private static Semver.SemVersion SchemaVersionID = new Semver.SemVersion(1, 33, 0);
private Semver.SemVersion SchemaVersionID = new Semver.SemVersion(1, 34, 0);
private string SerialPort;
private bool RequestedExit = false;
private JsonSerializer _jsonSerializer;


private Uri WSAddress;
private bool Host = true;
private Server _server;

private string _ZWaveJSDriverVersion;
public string ZWJSS_DriverVersion
Expand All @@ -48,8 +51,8 @@ public string ZWJSS_ServerVersion
}
}

public static int ServerCommunicationPort = 50001;
public static int ServerErrorThrottleTime = 10000;
public int ServerCommunicationPort { get; private set; }
public int ServerErrorThrottleTime { get; private set; }
private DateTime LastError;

public Controller Controller { get; internal set; }
Expand Down Expand Up @@ -226,7 +229,7 @@ private void MapNodeEvents()
NodeEventMap.Add("ready", (JO) =>
{
int NID = JO.SelectToken("event.nodeId").ToObject<int>();
ZWaveNode NNI = JO.SelectToken("event.nodeState").ToObject<ZWaveNode>();
ZWaveNode NNI = JO.SelectToken("event.nodeState").ToObject<ZWaveNode>(_jsonSerializer);

ZWaveNode N = this.Controller.Nodes.Get(NID);
this.Controller.Nodes.ReplaceInformation(NNI, N);
Expand Down Expand Up @@ -402,7 +405,7 @@ private void MapControllerEvents()
int NID = JO.SelectToken("event.node.nodeId").ToObject<int>();
InclusionResultArgs IR = JO.SelectToken("event.result").ToObject<InclusionResultArgs>();

ZWaveNode NN = new ZWaveNode();
ZWaveNode NN = new ZWaveNode(this);
NN.id = NID;

this.Controller.Nodes.AddNodeToCollection(NN);
Expand Down Expand Up @@ -516,15 +519,19 @@ private void MapEvents()
}

// Client Mode
public Driver(Uri Server, int SchemaVersion = 0)
public Driver(Uri Server, int SchemaVersion = 0, int ServerErrorThrottleTime = 10000)
{

Instance = this;

Newtonsoft.Json.JsonConvert.DefaultSettings = () => new JsonSerializerSettings
{
NullValueHandling = NullValueHandling.Ignore,
NullValueHandling = NullValueHandling.Ignore
};

var settings = new JsonSerializerSettings
{
NullValueHandling = NullValueHandling.Ignore
};
settings.Converters.Add(new ZWJSSJsonConverter(this));
_jsonSerializer = JsonSerializer.Create(settings);

if (SchemaVersion > 0)
{
Expand All @@ -536,43 +543,56 @@ public Driver(Uri Server, int SchemaVersion = 0)

this.WSAddress = Server;
this.Host = false;
this.ServerErrorThrottleTime = ServerErrorThrottleTime;

InternalPrep();

}

// Host Mode
public Driver(string SerialPort, ZWaveOptions Options)
public Driver(string SerialPort, ZWaveOptions Options, int ServerCommunicationPort = 50001, int ServerErrorThrottleTime = 10000)
{

Instance = this;

if (UsedPorts.Contains(ServerCommunicationPort))
{
throw new Exception(string.Format("Web Socket Port: {0} already in use", ServerCommunicationPort));
}

UsedPorts.Add(ServerCommunicationPort);

Newtonsoft.Json.JsonConvert.DefaultSettings = () => new JsonSerializerSettings
{
NullValueHandling = NullValueHandling.Ignore,
NullValueHandling = NullValueHandling.Ignore
};

var settings = new JsonSerializerSettings
{
NullValueHandling = NullValueHandling.Ignore
};
settings.Converters.Add(new ZWJSSJsonConverter(this));
_jsonSerializer = JsonSerializer.Create(settings);

Callbacks = new Dictionary<Guid, Action<JObject>>();
MapEvents();

this.SerialPort = SerialPort;
this.Options = Options;
this.ServerCommunicationPort = ServerCommunicationPort;
this.WSAddress = new Uri("ws://localhost:" + ServerCommunicationPort);
this.Host = true;
this.ServerErrorThrottleTime = ServerErrorThrottleTime;
this._server = new Server();

InternalPrep();

}

// Prep
private void InternalPrep()
{
if (this.Host)
{

Server.Start(SerialPort, Options, ServerCommunicationPort);
Server.Exited += Server_Exited;
Server.FatalError += Server_FatalError;
_server.Start(SerialPort, Options, ServerCommunicationPort);
_server.Exited += Server_Exited;
_server.FatalError += Server_FatalError;
}

var Factory = new Func<ClientWebSocket>(() => new ClientWebSocket
Expand Down Expand Up @@ -657,14 +677,19 @@ private void DestroySocket()
ClientWebSocket.Stop(WebSocketCloseStatus.NormalClosure, "Destroy");
}

if (Host)
{
UsedPorts.Remove(WSAddress.Port);
}

ClientWebSocket.Dispose();
ClientWebSocket = null;
}
}

private void DestroyServer()
{
Server.Terminate();
_server?.Terminate();
}

public void Destroy()
Expand All @@ -678,10 +703,7 @@ public void Destroy()
}

DestroySocket();
DestroyServer();



DestroyServer();
}

async internal void Restart()
Expand Down Expand Up @@ -767,9 +789,10 @@ private void StartListetningCB(JObject JO)
{
if (JO.Value<bool>("success"))
{
Controller C = JO.SelectToken("result.state.controller").ToObject<Controller>();
Controller C = JO.SelectToken("result.state.controller").ToObject<Controller>(_jsonSerializer);


ZWaveNode[] Nodes = JO.SelectToken("result.state.nodes").ToObject<ZWaveNode[]>();
ZWaveNode[] Nodes = JO.SelectToken("result.state.nodes").ToObject<ZWaveNode[]>(_jsonSerializer);
C.deviceConfig = Nodes.FirstOrDefault((N) => N.isControllerNode).deviceConfig;
Nodes = Nodes.Where((N) => !N.isControllerNode).ToArray();

Expand Down
16 changes: 10 additions & 6 deletions Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/Endpoint.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,19 @@ namespace ZWaveJS.NET
{
public class Endpoint
{
internal Endpoint() { }

private Driver _driver;
internal Endpoint(Driver driver = null)
{
_driver = driver;
}

// CHECKED
public Task<CMDResult> SupportsCCAPI(int CommandClass)
{
Guid ID = Guid.NewGuid();

TaskCompletionSource<CMDResult> Result = new TaskCompletionSource<CMDResult>();
Driver.Instance.Callbacks.Add(ID, (JO) =>
_driver.Callbacks.Add(ID, (JO) =>
{
CMDResult Res = new CMDResult(JO);
if (Res.Success)
Expand All @@ -36,7 +40,7 @@ public Task<CMDResult> SupportsCCAPI(int CommandClass)
Request.Add("commandClass", CommandClass);

string RequestPL = JsonConvert.SerializeObject(Request);
Driver.Instance.ClientWebSocket.SendInstant(RequestPL);
_driver.ClientWebSocket.SendInstant(RequestPL);

return Result.Task;
}
Expand All @@ -48,7 +52,7 @@ public Task<CMDResult> InvokeCCAPI(int CommandClass, string Method, params objec
Guid ID = Guid.NewGuid();

TaskCompletionSource<CMDResult> Result = new TaskCompletionSource<CMDResult>();
Driver.Instance.Callbacks.Add(ID, (JO) =>
_driver.Callbacks.Add(ID, (JO) =>
{
CMDResult Res = new CMDResult(JO);
if (Res.Success)
Expand All @@ -71,7 +75,7 @@ public Task<CMDResult> InvokeCCAPI(int CommandClass, string Method, params objec


string RequestPL = JsonConvert.SerializeObject(Request);
Driver.Instance.ClientWebSocket.SendInstant(RequestPL);
_driver.ClientWebSocket.SendInstant(RequestPL);

return Result.Task;
}
Expand Down
29 changes: 16 additions & 13 deletions Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/Server.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,17 @@ namespace ZWaveJS.NET
internal class Server
{

private static Process ServerProcess;


private Process ServerProcess;

internal delegate void FatalErrorEvent();
internal static event FatalErrorEvent FatalError;
internal event FatalErrorEvent FatalError;

internal delegate void ProcessdExitedEvent();
internal static event ProcessdExitedEvent Exited;
internal event ProcessdExitedEvent Exited;

internal static void Terminate()
internal void Terminate()
{
if (ServerProcess != null && !ServerProcess.HasExited)
{
Expand All @@ -26,21 +28,26 @@ internal static void Terminate()
}
}

internal static void Start(string SerialPort, ZWaveOptions Config, int WSPort)
internal void Start(string SerialPort, ZWaveOptions Config, int WSPort)
{


Process[] Zombies = Process.GetProcessesByName("server.psi");
string ProcessName = string.Format("server.{0}.psi", WSPort);

Process[] Zombies = Process.GetProcessesByName(ProcessName);
foreach(Process Zombie in Zombies)
{
Zombie.Kill();
File.Delete(ProcessName);
}

if (!File.Exists("server.psi"))
{
throw new FileNotFoundException("No Platform Snapshot Image (server.psi) found");
}

File.Copy("server.psi",ProcessName, true);

JsonSerializerSettings JSS = new JsonSerializerSettings();
JSS.NullValueHandling = NullValueHandling.Ignore;
string _Config = JsonConvert.SerializeObject(Config, JSS);
Expand All @@ -58,7 +65,7 @@ internal static void Start(string SerialPort, ZWaveOptions Config, int WSPort)
PSI.EnvironmentVariables.Add("WS_PORT", WSPort.ToString());
PSI.EnvironmentVariables.Add("NODE_ENV", "production");

PSI.FileName = "server.psi";
PSI.FileName = ProcessName;
PSI.UseShellExecute = false;
PSI.WindowStyle = ProcessWindowStyle.Hidden;
PSI.CreateNoWindow = true;
Expand All @@ -70,17 +77,15 @@ internal static void Start(string SerialPort, ZWaveOptions Config, int WSPort)
ServerProcess.StartInfo = PSI;
ServerProcess.Start();
ServerProcess.BeginErrorReadLine();


}

private static void ServerProcess_Exited(object sender, EventArgs e)
private void ServerProcess_Exited(object sender, EventArgs e)
{
// Exited?.Invoke(); I think this will be indirectly handled by the socket client now
ServerProcess.Dispose();
}

private static void ServerProcess_ErrorDataReceived(object sender, DataReceivedEventArgs e)
private void ServerProcess_ErrorDataReceived(object sender, DataReceivedEventArgs e)
{
int Code;
if (int.TryParse(e.Data, out Code))
Expand All @@ -92,8 +97,6 @@ private static void ServerProcess_ErrorDataReceived(object sender, DataReceivedE
break;
}
}


}
}
}
Loading

0 comments on commit 3fe7bdc

Please sign in to comment.