This commit is contained in:
pelikhan 2022-01-04 13:32:11 -08:00
Родитель d259bcab52
Коммит 184cce7ce5
26 изменённых файлов: 343 добавлений и 203 удалений

Просмотреть файл

@ -9,7 +9,6 @@
<ProjectReference Include="..\Jacdac.NET\Jacdac.NET.csproj" />
<ProjectReference Include="..\Jacdac.Transports.Spi\Jacdac.Transports.Spi.csproj" />
<ProjectReference Include="..\Jacdac.Transports.WebSockets\Jacdac.Transports.WebSockets.csproj" />
<ProjectReference Include="..\Jacdac\Jacdac.csproj" />
</ItemGroup>
</Project>

Просмотреть файл

@ -9,8 +9,8 @@ namespace Jacdac.NET.Playground
{
static void Main(string[] args)
{
NETPlatform.Init();
Console.WriteLine("jacdac: connecting...");
ServiceTwins twins = null;
var bus = new JDBus(null);
for (int i = 0; i < args.Length; i++)
{
@ -25,6 +25,10 @@ namespace Jacdac.NET.Playground
Console.WriteLine("adding devtools connection");
bus.AddTransport(new WebSocketTransport());
break;
case "twins":
Console.WriteLine("tracking twins");
twins = new ServiceTwins();
break;
}
}
foreach (var transport in bus.Transports)
@ -46,6 +50,12 @@ namespace Jacdac.NET.Playground
foreach (var service in services)
{
Console.WriteLine(service);
if (twins != null)
{
var spec = twins.ResolveSpecification(service.ServiceClass);
if (spec != null)
Console.WriteLine(spec);
}
}
};
};

Просмотреть файл

@ -0,0 +1,9 @@
{
"profiles": {
"Jacdac.NET.Playground": {
"commandName": "Project",
"commandLineArgs": "devtools twins",
"hotReloadEnabled": false
}
}
}

Просмотреть файл

@ -14,6 +14,15 @@
</PropertyGroup>
<ItemGroup>
<Compile Include="..\Jacdac\*.cs">
<Link>%(RecursiveDir)%(FileName)%(Extension)</Link>
</Compile>
<Compile Include="..\Jacdac\Servers\*.cs">
<Link>Servers\%(RecursiveDir)%(FileName)%(Extension)</Link>
</Compile>
<Compile Include="..\Jacdac\Constants\*.cs">
<Link>Constants\%(RecursiveDir)%(FileName)%(Extension)</Link>
</Compile>
<None Include="..\jacdac.png">
<Pack>True</Pack>
<PackagePath>\</PackagePath>
@ -26,10 +35,7 @@
<ItemGroup>
<PackageReference Include="DeviceId.Windows" Version="6.0.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Jacdac\Jacdac.csproj" />
<PackageReference Include="System.Text.Json" Version="6.0.1" />
</ItemGroup>
</Project>

Просмотреть файл

@ -1,13 +1,16 @@
using DeviceId;
using System;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Net;
using System.Text.Json;
namespace Jacdac.NET
namespace Jacdac
{
public static class NETPlatform
public partial class Platform
{
public static void Init()
static Platform()
{
var deviceId = new DeviceIdBuilder()
.AddMachineName()
@ -25,4 +28,59 @@ namespace Jacdac.NET
};
}
}
public partial class ServiceTwins
{
static ServiceTwins()
{
SpecificationReader = ServiceTwinReader;
SpecificationResolver = WebGet;
}
static ServiceTwinSpec ServiceTwinReader(byte[] buffer)
{
try
{
var s = System.Text.UTF8Encoding.UTF8.GetString(buffer);
return JsonSerializer.Deserialize<ServiceTwinSpec>(buffer, new JsonSerializerOptions
{
AllowTrailingCommas = true,
IncludeFields = true
});
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
return null;
}
}
static byte[] WebGet(string url)
{
var req = HttpWebRequest.Create(url) as HttpWebRequest;
{
req.KeepAlive = false;
req.ReadWriteTimeout = 2000;
req.Headers[HttpRequestHeader.Accept] = "application/json";
//req.HttpsAuthentCerts = certx509;
using (var res = req.GetResponse() as HttpWebResponse)
{
if (res.StatusCode == HttpStatusCode.OK)
using (var stream = res.GetResponseStream())
{
var mem = new MemoryStream();
var read = 0;
var buf = new byte[512];
do
{
read = stream.Read(buf, 0, buf.Length);
mem.Write(buf, 0, read);
} while (read > 0);
return mem.ToArray();
}
}
}
return null;
}
}
}

Просмотреть файл

@ -20,7 +20,7 @@
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Jacdac\Jacdac.csproj" />
<ProjectReference Include="..\Jacdac.NET\Jacdac.NET.csproj" />
</ItemGroup>
</Project>

Просмотреть файл

@ -7,6 +7,7 @@ using Jacdac;
using System;
using Jacdac.Servers;
using GHIElectronics.TinyCLR.Devices.Jacdac.Transport;
using Jacdac.Transports;
namespace Jacdac_RgbLed
{
@ -17,7 +18,7 @@ namespace Jacdac_RgbLed
new Program().Start();
}
ServiceTwins serviceTwins;
// ServiceTwins serviceTwins;
public void Start()
{
// Display enable
@ -39,7 +40,7 @@ namespace Jacdac_RgbLed
var ssidStorage = sdStorage.MountKeyStorage("wifi.json");
var serviceStorage = sdStorage.MountKeyStorage("servicestwins.json");
var settingsStorage = sdStorage.MountKeyStorage("settings.json");
this.serviceTwins = new ServiceTwins(serviceStorage);
// this.serviceTwins = new ServiceTwins(serviceStorage);
var rtc = new RealTimeClockServer(() => DateTime.Now, new RealTimeClockServerOptions { Variant = RealTimeClockVariant.Crystal });
var wifiServer = new WifiServer(ssidStorage);

Просмотреть файл

@ -45,7 +45,8 @@
<Link>Constants\%(RecursiveDir)%(FileName)%(Extension)</Link>
</Compile>
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="UartTransport.cs" />
<Compile Include="TinyCLRPlatform.cs" />
<Compile Include="Transports\UartTransport.cs" />
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />

Просмотреть файл

@ -0,0 +1,98 @@
using GHIElectronics.TinyCLR.Data.Json;
using GHIElectronics.TinyCLR.Devices.Jacdac.Transport;
using GHIElectronics.TinyCLR.Native;
using System;
using System.Diagnostics;
using System.IO;
using System.Net;
namespace Jacdac
{
public partial class Platform
{
static Platform()
{
Platform.Crc16 = JacdacSerialWireController.Crc;
var id = DeviceInformation.GetUniqueId();
// TODO: compress device id into 8 bytes
var jid = Util.Slice(id, 0, 8);
Platform.DeviceId = jid;
for (var i = 8; i < id.Length; ++i)
{
jid[i % jid.Length] |= (byte)(id[i] << 4);
}
Platform.DeviceId = jid;
Platform.DeviceDescription = DeviceInformation.DeviceName;
var version = DeviceInformation.Version;
var major = (ushort)((version >> 48) & 0xFFFF);
var minor = (ushort)((version >> 32) & 0xFFFF);
var build = (ushort)((version >> 16) & 0xFFFF);
var revision = (ushort)((version >> 0) & 0xFFFF);
Platform.FirmwareVersion = major + "." + minor + "." + build + "." + revision;
Platform.RealTimeClock = RealTimeClockVariant.Crystal;
Platform.CreateClock = () =>
{
var start = DateTime.Now;
return () => DateTime.Now - start;
};
}
}
public partial class ServiceTwins
{
static ServiceTwins()
{
SpecificationReader = ServiceTwinReader;
SpecificationResolver = WebGet;
}
static ServiceTwinSpec ServiceTwinReader(byte[] buffer)
{
try
{
var text = System.Text.UTF8Encoding.UTF8.GetString(buffer);
return (ServiceTwinSpec)JsonConverter.DeserializeObject(text, typeof(ServiceTwinSpec),
(string instancePath, JToken token, Type baseType, string fieldName, int length) =>
{
if (instancePath == "/")
return new ServiceTwinSpec();
return null;
});
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
return null;
}
}
static byte[] WebGet(string url)
{
//var certx509 = new X509Certificate[] { new X509Certificate(certificates) };
using (var req = HttpWebRequest.Create(url) as HttpWebRequest)
{
req.KeepAlive = false;
req.ReadWriteTimeout = 2000;
req.Headers.Add("Accept", "application/json");
//req.HttpsAuthentCerts = certx509;
using (var res = req.GetResponse() as HttpWebResponse)
{
if (res.StatusCode == HttpStatusCode.OK)
using (var stream = res.GetResponseStream())
{
var mem = new MemoryStream();
var read = 0;
var buf = new byte[512];
do
{
read = stream.Read(buf, 0, buf.Length);
mem.Write(buf, 0, buf.Length);
} while (read != 0);
return mem.ToArray();
}
}
}
return null;
}
}
}

Просмотреть файл

@ -0,0 +1,84 @@
using GHIElectronics.TinyCLR.Data.Json;
using GHIElectronics.TinyCLR.Native;
using System;
using System.Diagnostics;
using System.IO;
using System.Net;
using GHIElectronics.TinyCLR.Devices.Jacdac.Transport;
namespace Jacdac.Transports
{
public sealed partial class UartTransport : Jacdac.Transport
{
public readonly GHIElectronics.TinyCLR.Devices.Jacdac.Transport.JacdacSerialWireController controller;
public UartTransport(GHIElectronics.TinyCLR.Devices.Jacdac.Transport.JacdacSerialWireController controller)
: base("uart")
{
this.controller = controller;
}
public override event FrameReceivedEvent FrameReceived
{
add
{
this.controller.PacketReceived += (JacdacSerialWireController sender, PacketReceivedEventArgs packet) =>
{
var frame = packet.Data;
value(this, frame);
};
}
remove
{
throw new InvalidOperationException();
// not supported
}
}
public override event TransportErrorReceivedEvent ErrorReceived
{
add
{
this.controller.ErrorReceived += (JacdacSerialWireController sender, ErrorReceivedEventArgs args) =>
{
value(this, new TransportErrorReceivedEventArgs((TransportError)(uint)args.Error, args.Timestamp, args.Data));
};
}
remove
{
throw new InvalidOperationException();
// not supported
}
}
protected override void InternalConnect()
{
try
{
this.controller.Enable();
this.SetConnectionState(ConnectionState.Connected);
}
catch (Exception)
{
this.SetConnectionState(ConnectionState.Disconnected);
}
}
protected override void InternalDisconnect()
{
this.controller.Disable();
}
public override void Dispose()
{
this.controller.Dispose();
}
public override void SendFrame(byte[] data)
{
TransportStats.FrameSent++;
this.controller.Write(data);
}
}
}

Просмотреть файл

@ -1,157 +0,0 @@
using GHIElectronics.TinyCLR.Data.Json;
using GHIElectronics.TinyCLR.Native;
using System;
using System.Diagnostics;
using System.IO;
using System.Net;
using GHIElectronics.TinyCLR.Devices.Jacdac.Transport;
namespace Jacdac
{
public sealed class UartTransport : Transport
{
static UartTransport()
{
Platform.Crc16 = JacdacSerialWireController.Crc;
var id = DeviceInformation.GetUniqueId();
// TODO: compress device id into 8 bytes
var jid = Util.Slice(id, 0, 8);
Platform.DeviceId = jid;
for (var i = 8; i < id.Length; ++i)
{
jid[i % jid.Length] |= (byte)(id[i] << 4);
}
Platform.DeviceId = jid;
Platform.DeviceDescription = DeviceInformation.DeviceName;
var version = DeviceInformation.Version;
var major = (ushort)((version >> 48) & 0xFFFF);
var minor = (ushort)((version >> 32) & 0xFFFF);
var build = (ushort)((version >> 16) & 0xFFFF);
var revision = (ushort)((version >> 0) & 0xFFFF);
Platform.FirmwareVersion = major + "." + minor + "." + build + "." + revision;
Platform.RealTimeClock = RealTimeClockVariant.Crystal;
Platform.CreateClock = () =>
{
var start = DateTime.Now;
return () => DateTime.Now - start;
};
Platform.ServiceTwinReader = (byte[] buffer) =>
{
try
{
var text = System.Text.UTF8Encoding.UTF8.GetString(buffer);
return (ServiceTwinSpec)JsonConverter.DeserializeObject(text, typeof(ServiceTwinSpec),
(string instancePath, JToken token, Type baseType, string fieldName, int length) =>
{
if (instancePath == "/")
return new ServiceTwinSpec();
return null;
});
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
return null;
}
};
Platform.WebGet = (string url) =>
{
//var certx509 = new X509Certificate[] { new X509Certificate(certificates) };
using (var req = HttpWebRequest.Create(url) as HttpWebRequest)
{
req.KeepAlive = false;
req.ReadWriteTimeout = 2000;
//req.HttpsAuthentCerts = certx509;
using (var res = req.GetResponse() as HttpWebResponse)
{
if (res.StatusCode == HttpStatusCode.OK)
using (var stream = res.GetResponseStream())
{
var mem = new MemoryStream();
var read = 0;
var buf = new byte[512];
do
{
read = stream.Read(buf, 0, buf.Length);
mem.Write(buf, 0, buf.Length);
} while (read != 0);
return mem.ToArray();
}
}
}
return null;
};
}
public readonly GHIElectronics.TinyCLR.Devices.Jacdac.Transport.JacdacSerialWireController controller;
public UartTransport(GHIElectronics.TinyCLR.Devices.Jacdac.Transport.JacdacSerialWireController controller)
: base("uart")
{
this.controller = controller;
}
public override event FrameReceivedEvent FrameReceived
{
add
{
this.controller.PacketReceived += (JacdacSerialWireController sender, PacketReceivedEventArgs packet) =>
{
var frame = packet.Data;
value(this, frame);
};
}
remove
{
throw new InvalidOperationException();
// not supported
}
}
public override event TransportErrorReceivedEvent ErrorReceived
{
add
{
this.controller.ErrorReceived += (JacdacSerialWireController sender, ErrorReceivedEventArgs args) =>
{
value(this, new TransportErrorReceivedEventArgs((TransportError)(uint)args.Error, args.Timestamp, args.Data));
};
}
remove
{
throw new InvalidOperationException();
// not supported
}
}
protected override void InternalConnect()
{
try
{
this.controller.Enable();
this.SetConnectionState(ConnectionState.Connected);
}
catch (Exception)
{
this.SetConnectionState(ConnectionState.Disconnected);
}
}
protected override void InternalDisconnect()
{
this.controller.Disable();
}
public override void Dispose()
{
this.controller.Dispose();
}
public override void SendFrame(byte[] data)
{
TransportStats.FrameSent++;
this.controller.Write(data);
}
}
}

Просмотреть файл

@ -23,7 +23,7 @@
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Jacdac\Jacdac.csproj" />
<ProjectReference Include="..\Jacdac.NET\Jacdac.NET.csproj" />
</ItemGroup>
</Project>

Просмотреть файл

@ -27,7 +27,7 @@
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Jacdac\Jacdac.csproj" />
<ProjectReference Include="..\Jacdac.NET\Jacdac.NET.csproj" />
</ItemGroup>
</Project>

Просмотреть файл

@ -31,7 +31,6 @@
<ItemGroup>
<ProjectReference Include="..\Jacdac.NET\Jacdac.NET.csproj" />
<ProjectReference Include="..\Jacdac.Transports.Hf2\Jacdac.Transports.Hf2.csproj" />
<ProjectReference Include="..\Jacdac\Jacdac.csproj" />
</ItemGroup>
</Project>

Просмотреть файл

@ -26,7 +26,6 @@
<ItemGroup>
<ProjectReference Include="..\Jacdac.NET\Jacdac.NET.csproj" />
<ProjectReference Include="..\Jacdac\Jacdac.csproj" />
</ItemGroup>
</Project>

Просмотреть файл

@ -17,7 +17,7 @@ namespace Jacdac
public SetStatusLightHandler SetStatusLight = Platform.SetStatusLight;
}
public sealed class JDBus : JDNode
public sealed partial class JDBus : JDNode
{
// updated concurrently, locked by this
private JDDevice[] devices;

Просмотреть файл

@ -2,7 +2,7 @@
namespace Jacdac
{
public sealed class ControlServer : JDServiceServer
public sealed partial class ControlServer : JDServiceServer
{
private SetStatusLightHandler setStatusLight;

Просмотреть файл

@ -5,7 +5,7 @@ using System.Threading;
namespace Jacdac
{
public sealed class JDDevice : JDNode
public sealed partial class JDDevice : JDNode
{
private JDBus bus;
public readonly string DeviceId;

Просмотреть файл

@ -3,7 +3,7 @@ using System.Collections;
namespace Jacdac
{
public sealed class JDDeviceServer
public sealed partial class JDDeviceServer
{
public readonly JDBus Bus;
public readonly string DeviceId;

Просмотреть файл

@ -1,6 +1,6 @@
namespace Jacdac
{
public sealed class JDEvent : JDServiceNode
public sealed partial class JDEvent : JDServiceNode
{
private Packet _lastReportPkt;
public uint Count = 0;

Просмотреть файл

@ -5,7 +5,7 @@
<TargetFramework>net5.0</TargetFramework>
<ApplicationIcon />
<StartupObject />
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
<GeneratePackageOnBuild>False</GeneratePackageOnBuild>
<Description>Jacdac runtime for .NET</Description>
<Copyright>Microsoft Corporation</Copyright>
<PackageProjectUrl>https://aka.ms/jacdac</PackageProjectUrl>

Просмотреть файл

@ -9,7 +9,7 @@ namespace Jacdac
public delegate short McuTemperatureCalculator();
public delegate void SetStatusLightHandler(byte red, byte green, byte blue, byte speed);
public static class Platform
public static partial class Platform
{
public static byte[] DeviceId;
public static string FirmwareVersion;
@ -30,8 +30,6 @@ namespace Jacdac
return crc;
};
public static McuTemperatureCalculator McuTemperature;
public static ServiceTwinSpecReader ServiceTwinReader;
public static HttpWebRequestGet WebGet;
public static ControlAnnounceFlags StatusLight = ControlAnnounceFlags.StatusLightNone;
public static SetStatusLightHandler SetStatusLight = null;
@ -45,6 +43,4 @@ namespace Jacdac
void Delete(string key);
void Clear();
}
public delegate byte[] HttpWebRequestGet(string url);
}

Просмотреть файл

@ -3,7 +3,7 @@ using System.Collections;
namespace Jacdac
{
public sealed class JDService : JDNode
public sealed partial class JDService : JDNode
{
JDDevice _device;
public readonly byte ServiceIndex;

Просмотреть файл

@ -19,7 +19,7 @@ namespace Jacdac
public string InstanceName;
}
public abstract class JDServiceServer : JDNode
public abstract partial class JDServiceServer : JDNode
{
public byte ServiceIndex;
public JDDeviceServer Device;

Просмотреть файл

@ -20,6 +20,12 @@ namespace Jacdac
public ServiceTwinRegisterFlag flags;
public string packf;
public string[] fields;
public override string ToString()
{
var c = this.code.ToString("x2");
return $"{this.name} ({c})";
}
}
[Serializable]
@ -28,49 +34,79 @@ namespace Jacdac
public uint serviceClass;
public string name;
public ServiceTwinRegisterSpec[] registers;
public override string ToString()
{
var sc = this.serviceClass.ToString("x8");
return $"{this.name} (0x{sc})";
}
}
public delegate ServiceTwinSpec ServiceTwinSpecReader(byte[] buffer);
public delegate byte[] ServiceTwinSpecResolver(string url);
public sealed class ServiceTwins
public sealed partial class ServiceTwins : JDNode
{
public string Root = "https://microsoft.github.io/jacdac-docs/";
public readonly IKeyStorage Storage;
public readonly ServiceTwinSpecReader SpecificationReader;
public static ServiceTwinSpecReader SpecificationReader;
public static ServiceTwinSpecResolver SpecificationResolver;
private ServiceTwinSpec[] specifications;
public ServiceTwins(IKeyStorage storage)
public ServiceTwins(IKeyStorage storage = null)
{
Debug.Assert(SpecificationReader != null);
Debug.Assert(SpecificationResolver != null);
this.Storage = storage;
this.SpecificationReader = Platform.ServiceTwinReader;
this.specifications = new ServiceTwinSpec[0];
}
public ServiceTwinSpec ResolveSpecification(uint serviceClass)
{
var spec = this.ReadFromStorage(serviceClass);
if (spec == null)
spec = this.DownloadSpecification(serviceClass);
return spec;
lock (this)
{
// in memory cache, need limit?
var specifications = this.specifications;
foreach (var specification in specifications)
if (specification.serviceClass == serviceClass)
return specification;
// look cached spec
var spec = this.ReadFromStorage(serviceClass);
if (spec == null)
spec = this.DownloadSpecification(serviceClass);
if (spec != null)
{
var newSpecs = new ServiceTwinSpec[specifications.Length + 1];
specifications.CopyTo(newSpecs, 0);
newSpecs[newSpecs.Length - 1] = spec;
this.specifications = newSpecs;
}
return spec;
}
}
private ServiceTwinSpec ReadFromStorage(uint serviceClass)
{
Debug.WriteLine($"twins: read {serviceClass} from storage");
var key = serviceClass.ToString("x8");
var buffer = this.Storage?.Read(key);
if (buffer == null) return null;
else return this.SpecificationReader(buffer);
else return SpecificationReader(buffer);
}
private ServiceTwinSpec DownloadSpecification(uint serviceClass)
{
var key = serviceClass.ToString("x8");
var url = $"http://microsoft.github.io/jacdac-docs/services/twin/x{serviceClass.ToString("x8")}.json";
var url = $"http://microsoft.github.io/jacdac-docs/services/twin/x{serviceClass.ToString("x")}.json";
Debug.WriteLine($"fetch {url}");
try
{
var buffer = Platform.WebGet(url);
var buffer = SpecificationResolver(url);
if (buffer != null)
{
var spec = this.SpecificationReader(buffer);
var spec = SpecificationReader(buffer);
this.Storage?.Write(key, buffer);
return spec;
}

Просмотреть файл

@ -19,17 +19,18 @@ This layer is still under construction.
This repository contains a C# implementation of the Jacdac protocol for various .NET runtime, including desktop or TinyClR.
To avoid mscorlib issues, each platform has a different set of assemblies where C# files are simply shared as links.
- `Jacdac`, core Jacdac library, .NET5
- `Jacdac.NET`, HF2 protocol layer and .NET famework specific platform, .NET5
- `Jacdac`, C# Jacdac library, .NET5. This package serves as a placeholder for C# files and
and is not referenced anywhere.
- `Jacdac.NET`, .NET famework specific platform, .NET5
- `Jacdac.NET.Playground`, .NET5 test application using jacdac development server
- `Jacdac.Transports.WebSockets`, WebSocket transport, .NET5
- `Jacdac.Transports.Spi`, SPI transport layer for SPI Jacdapter using .NET IoT, .NET5
- `Jacdac.TinyCLR`, mirror of `Jacdac` library, TinyCLR
- `Jacdac.TinyCLR.Playground`, TinyCLR test application
- `Jacdac.Tests`, unit tests, .NET6
- `Jacdac.Transports.Hf2`, HF2 protocol layer, .NET6, under construction
- `Jacdac.Transports.Usb`, Usb transport, .NET6, under construction
- `Jacdac.Transports.Spi`, SPI transport layer for SPI Jacdapter using .NET IoT, .NET5, under construction
## Developer setup