diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 000000000..4af53c05c --- /dev/null +++ b/.editorconfig @@ -0,0 +1,4 @@ +[*.cs] + +# CS0162: Unreachable code detected +dotnet_diagnostic.CS0162.severity = none diff --git a/.gitignore b/.gitignore index d033613f3..9e80f1699 100644 --- a/.gitignore +++ b/.gitignore @@ -268,3 +268,6 @@ paket-files/ /_site/ /.sass-cache/ Gemfile.lock + +#nugetGenerated +/Build/PsiPackages/ \ No newline at end of file diff --git a/Psi.sln b/Psi.sln index 952ed4d66..58f099f0b 100644 --- a/Psi.sln +++ b/Psi.sln @@ -259,324 +259,648 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SigmaApp", "Applications\Si EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SigmaComputeServer", "Applications\Sigma\SigmaComputeServer\SigmaComputeServer.csproj", "{365D836D-AD15-428C-96D1-6C4870525FEF}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Psi.Interop.Android", "Sources\Runtime\Microsoft.Psi.InteropAndroid\Microsoft.Psi.Interop.Android.csproj", "{DF585F32-54AA-4553-AF91-6BBD1732E147}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {04147400-0AB0-4F07-9975-D4B7E58150DB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {04147400-0AB0-4F07-9975-D4B7E58150DB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {04147400-0AB0-4F07-9975-D4B7E58150DB}.Debug|x64.ActiveCfg = Debug|Any CPU + {04147400-0AB0-4F07-9975-D4B7E58150DB}.Debug|x64.Build.0 = Debug|Any CPU {04147400-0AB0-4F07-9975-D4B7E58150DB}.Release|Any CPU.ActiveCfg = Release|Any CPU {04147400-0AB0-4F07-9975-D4B7E58150DB}.Release|Any CPU.Build.0 = Release|Any CPU + {04147400-0AB0-4F07-9975-D4B7E58150DB}.Release|x64.ActiveCfg = Release|Any CPU + {04147400-0AB0-4F07-9975-D4B7E58150DB}.Release|x64.Build.0 = Release|Any CPU {2CC39BCB-7798-474C-AC97-1C5F664101E2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {2CC39BCB-7798-474C-AC97-1C5F664101E2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2CC39BCB-7798-474C-AC97-1C5F664101E2}.Debug|x64.ActiveCfg = Debug|Any CPU + {2CC39BCB-7798-474C-AC97-1C5F664101E2}.Debug|x64.Build.0 = Debug|Any CPU {2CC39BCB-7798-474C-AC97-1C5F664101E2}.Release|Any CPU.ActiveCfg = Release|Any CPU {2CC39BCB-7798-474C-AC97-1C5F664101E2}.Release|Any CPU.Build.0 = Release|Any CPU + {2CC39BCB-7798-474C-AC97-1C5F664101E2}.Release|x64.ActiveCfg = Release|Any CPU + {2CC39BCB-7798-474C-AC97-1C5F664101E2}.Release|x64.Build.0 = Release|Any CPU {855FD8BE-6938-4784-B1EE-D90A8B5B2496}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {855FD8BE-6938-4784-B1EE-D90A8B5B2496}.Debug|Any CPU.Build.0 = Debug|Any CPU + {855FD8BE-6938-4784-B1EE-D90A8B5B2496}.Debug|x64.ActiveCfg = Debug|Any CPU + {855FD8BE-6938-4784-B1EE-D90A8B5B2496}.Debug|x64.Build.0 = Debug|Any CPU {855FD8BE-6938-4784-B1EE-D90A8B5B2496}.Release|Any CPU.ActiveCfg = Release|Any CPU {855FD8BE-6938-4784-B1EE-D90A8B5B2496}.Release|Any CPU.Build.0 = Release|Any CPU + {855FD8BE-6938-4784-B1EE-D90A8B5B2496}.Release|x64.ActiveCfg = Release|Any CPU + {855FD8BE-6938-4784-B1EE-D90A8B5B2496}.Release|x64.Build.0 = Release|Any CPU {9BF2E5EF-186A-4179-B753-AE11EE90E026}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {9BF2E5EF-186A-4179-B753-AE11EE90E026}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9BF2E5EF-186A-4179-B753-AE11EE90E026}.Debug|x64.ActiveCfg = Debug|Any CPU + {9BF2E5EF-186A-4179-B753-AE11EE90E026}.Debug|x64.Build.0 = Debug|Any CPU {9BF2E5EF-186A-4179-B753-AE11EE90E026}.Release|Any CPU.ActiveCfg = Release|Any CPU {9BF2E5EF-186A-4179-B753-AE11EE90E026}.Release|Any CPU.Build.0 = Release|Any CPU + {9BF2E5EF-186A-4179-B753-AE11EE90E026}.Release|x64.ActiveCfg = Release|Any CPU + {9BF2E5EF-186A-4179-B753-AE11EE90E026}.Release|x64.Build.0 = Release|Any CPU {AC5745DA-570C-4E57-9AE4-D1974F629428}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {AC5745DA-570C-4E57-9AE4-D1974F629428}.Debug|Any CPU.Build.0 = Debug|Any CPU + {AC5745DA-570C-4E57-9AE4-D1974F629428}.Debug|x64.ActiveCfg = Debug|Any CPU + {AC5745DA-570C-4E57-9AE4-D1974F629428}.Debug|x64.Build.0 = Debug|Any CPU {AC5745DA-570C-4E57-9AE4-D1974F629428}.Release|Any CPU.ActiveCfg = Release|Any CPU {AC5745DA-570C-4E57-9AE4-D1974F629428}.Release|Any CPU.Build.0 = Release|Any CPU + {AC5745DA-570C-4E57-9AE4-D1974F629428}.Release|x64.ActiveCfg = Release|Any CPU + {AC5745DA-570C-4E57-9AE4-D1974F629428}.Release|x64.Build.0 = Release|Any CPU {C048E3FB-CDC4-4577-A40E-8C9B1B7CEDF1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {C048E3FB-CDC4-4577-A40E-8C9B1B7CEDF1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C048E3FB-CDC4-4577-A40E-8C9B1B7CEDF1}.Debug|x64.ActiveCfg = Debug|Any CPU + {C048E3FB-CDC4-4577-A40E-8C9B1B7CEDF1}.Debug|x64.Build.0 = Debug|Any CPU {C048E3FB-CDC4-4577-A40E-8C9B1B7CEDF1}.Release|Any CPU.ActiveCfg = Release|Any CPU {C048E3FB-CDC4-4577-A40E-8C9B1B7CEDF1}.Release|Any CPU.Build.0 = Release|Any CPU + {C048E3FB-CDC4-4577-A40E-8C9B1B7CEDF1}.Release|x64.ActiveCfg = Release|Any CPU + {C048E3FB-CDC4-4577-A40E-8C9B1B7CEDF1}.Release|x64.Build.0 = Release|Any CPU {80C75A20-920A-4B30-B05E-970BE844456A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {80C75A20-920A-4B30-B05E-970BE844456A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {80C75A20-920A-4B30-B05E-970BE844456A}.Debug|x64.ActiveCfg = Debug|Any CPU + {80C75A20-920A-4B30-B05E-970BE844456A}.Debug|x64.Build.0 = Debug|Any CPU {80C75A20-920A-4B30-B05E-970BE844456A}.Release|Any CPU.ActiveCfg = Release|Any CPU {80C75A20-920A-4B30-B05E-970BE844456A}.Release|Any CPU.Build.0 = Release|Any CPU + {80C75A20-920A-4B30-B05E-970BE844456A}.Release|x64.ActiveCfg = Release|Any CPU + {80C75A20-920A-4B30-B05E-970BE844456A}.Release|x64.Build.0 = Release|Any CPU {3FB6CD31-0941-4372-9BDB-9E6830385DD4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {3FB6CD31-0941-4372-9BDB-9E6830385DD4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3FB6CD31-0941-4372-9BDB-9E6830385DD4}.Debug|x64.ActiveCfg = Debug|Any CPU + {3FB6CD31-0941-4372-9BDB-9E6830385DD4}.Debug|x64.Build.0 = Debug|Any CPU {3FB6CD31-0941-4372-9BDB-9E6830385DD4}.Release|Any CPU.ActiveCfg = Release|Any CPU {3FB6CD31-0941-4372-9BDB-9E6830385DD4}.Release|Any CPU.Build.0 = Release|Any CPU + {3FB6CD31-0941-4372-9BDB-9E6830385DD4}.Release|x64.ActiveCfg = Release|Any CPU + {3FB6CD31-0941-4372-9BDB-9E6830385DD4}.Release|x64.Build.0 = Release|Any CPU {8AC7DE3C-DF3C-44A8-9E69-E1F21BF3E564}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {8AC7DE3C-DF3C-44A8-9E69-E1F21BF3E564}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8AC7DE3C-DF3C-44A8-9E69-E1F21BF3E564}.Debug|x64.ActiveCfg = Debug|Any CPU + {8AC7DE3C-DF3C-44A8-9E69-E1F21BF3E564}.Debug|x64.Build.0 = Debug|Any CPU {8AC7DE3C-DF3C-44A8-9E69-E1F21BF3E564}.Release|Any CPU.ActiveCfg = Release|Any CPU {8AC7DE3C-DF3C-44A8-9E69-E1F21BF3E564}.Release|Any CPU.Build.0 = Release|Any CPU + {8AC7DE3C-DF3C-44A8-9E69-E1F21BF3E564}.Release|x64.ActiveCfg = Release|Any CPU + {8AC7DE3C-DF3C-44A8-9E69-E1F21BF3E564}.Release|x64.Build.0 = Release|Any CPU {FFFD905A-1672-4920-B790-EEA6A961383C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {FFFD905A-1672-4920-B790-EEA6A961383C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FFFD905A-1672-4920-B790-EEA6A961383C}.Debug|x64.ActiveCfg = Debug|Any CPU + {FFFD905A-1672-4920-B790-EEA6A961383C}.Debug|x64.Build.0 = Debug|Any CPU {FFFD905A-1672-4920-B790-EEA6A961383C}.Release|Any CPU.ActiveCfg = Release|Any CPU {FFFD905A-1672-4920-B790-EEA6A961383C}.Release|Any CPU.Build.0 = Release|Any CPU + {FFFD905A-1672-4920-B790-EEA6A961383C}.Release|x64.ActiveCfg = Release|Any CPU + {FFFD905A-1672-4920-B790-EEA6A961383C}.Release|x64.Build.0 = Release|Any CPU {7D481D0D-75C2-4E9F-9FE3-43EB63403F2B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {7D481D0D-75C2-4E9F-9FE3-43EB63403F2B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7D481D0D-75C2-4E9F-9FE3-43EB63403F2B}.Debug|x64.ActiveCfg = Debug|Any CPU + {7D481D0D-75C2-4E9F-9FE3-43EB63403F2B}.Debug|x64.Build.0 = Debug|Any CPU {7D481D0D-75C2-4E9F-9FE3-43EB63403F2B}.Release|Any CPU.ActiveCfg = Release|Any CPU {7D481D0D-75C2-4E9F-9FE3-43EB63403F2B}.Release|Any CPU.Build.0 = Release|Any CPU + {7D481D0D-75C2-4E9F-9FE3-43EB63403F2B}.Release|x64.ActiveCfg = Release|Any CPU + {7D481D0D-75C2-4E9F-9FE3-43EB63403F2B}.Release|x64.Build.0 = Release|Any CPU {191DF615-3D8F-45A3-B763-DD4A604A712A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {191DF615-3D8F-45A3-B763-DD4A604A712A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {191DF615-3D8F-45A3-B763-DD4A604A712A}.Debug|x64.ActiveCfg = Debug|Any CPU + {191DF615-3D8F-45A3-B763-DD4A604A712A}.Debug|x64.Build.0 = Debug|Any CPU {191DF615-3D8F-45A3-B763-DD4A604A712A}.Release|Any CPU.ActiveCfg = Release|Any CPU {191DF615-3D8F-45A3-B763-DD4A604A712A}.Release|Any CPU.Build.0 = Release|Any CPU + {191DF615-3D8F-45A3-B763-DD4A604A712A}.Release|x64.ActiveCfg = Release|Any CPU + {191DF615-3D8F-45A3-B763-DD4A604A712A}.Release|x64.Build.0 = Release|Any CPU {57B57050-5044-4EA9-AC08-030F49E69D62}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {57B57050-5044-4EA9-AC08-030F49E69D62}.Debug|Any CPU.Build.0 = Debug|Any CPU + {57B57050-5044-4EA9-AC08-030F49E69D62}.Debug|x64.ActiveCfg = Debug|Any CPU + {57B57050-5044-4EA9-AC08-030F49E69D62}.Debug|x64.Build.0 = Debug|Any CPU {57B57050-5044-4EA9-AC08-030F49E69D62}.Release|Any CPU.ActiveCfg = Release|Any CPU {57B57050-5044-4EA9-AC08-030F49E69D62}.Release|Any CPU.Build.0 = Release|Any CPU + {57B57050-5044-4EA9-AC08-030F49E69D62}.Release|x64.ActiveCfg = Release|Any CPU + {57B57050-5044-4EA9-AC08-030F49E69D62}.Release|x64.Build.0 = Release|Any CPU {F843DAFA-A02B-4B63-8985-6890E513312E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {F843DAFA-A02B-4B63-8985-6890E513312E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F843DAFA-A02B-4B63-8985-6890E513312E}.Debug|x64.ActiveCfg = Debug|Any CPU + {F843DAFA-A02B-4B63-8985-6890E513312E}.Debug|x64.Build.0 = Debug|Any CPU {F843DAFA-A02B-4B63-8985-6890E513312E}.Release|Any CPU.ActiveCfg = Release|Any CPU {F843DAFA-A02B-4B63-8985-6890E513312E}.Release|Any CPU.Build.0 = Release|Any CPU + {F843DAFA-A02B-4B63-8985-6890E513312E}.Release|x64.ActiveCfg = Release|Any CPU + {F843DAFA-A02B-4B63-8985-6890E513312E}.Release|x64.Build.0 = Release|Any CPU {D9EB512A-9F4F-4D46-86F1-57065CCC933D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {D9EB512A-9F4F-4D46-86F1-57065CCC933D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D9EB512A-9F4F-4D46-86F1-57065CCC933D}.Debug|x64.ActiveCfg = Debug|Any CPU + {D9EB512A-9F4F-4D46-86F1-57065CCC933D}.Debug|x64.Build.0 = Debug|Any CPU {D9EB512A-9F4F-4D46-86F1-57065CCC933D}.Release|Any CPU.ActiveCfg = Release|Any CPU {D9EB512A-9F4F-4D46-86F1-57065CCC933D}.Release|Any CPU.Build.0 = Release|Any CPU + {D9EB512A-9F4F-4D46-86F1-57065CCC933D}.Release|x64.ActiveCfg = Release|Any CPU + {D9EB512A-9F4F-4D46-86F1-57065CCC933D}.Release|x64.Build.0 = Release|Any CPU {91184A9B-5AB9-4715-B853-8E95E5065AA3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {91184A9B-5AB9-4715-B853-8E95E5065AA3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {91184A9B-5AB9-4715-B853-8E95E5065AA3}.Debug|x64.ActiveCfg = Debug|Any CPU + {91184A9B-5AB9-4715-B853-8E95E5065AA3}.Debug|x64.Build.0 = Debug|Any CPU {91184A9B-5AB9-4715-B853-8E95E5065AA3}.Release|Any CPU.ActiveCfg = Release|Any CPU {91184A9B-5AB9-4715-B853-8E95E5065AA3}.Release|Any CPU.Build.0 = Release|Any CPU + {91184A9B-5AB9-4715-B853-8E95E5065AA3}.Release|x64.ActiveCfg = Release|Any CPU + {91184A9B-5AB9-4715-B853-8E95E5065AA3}.Release|x64.Build.0 = Release|Any CPU {3A3F1C2C-A805-4EA2-B5AE-80371B565A15}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {3A3F1C2C-A805-4EA2-B5AE-80371B565A15}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3A3F1C2C-A805-4EA2-B5AE-80371B565A15}.Debug|x64.ActiveCfg = Debug|Any CPU + {3A3F1C2C-A805-4EA2-B5AE-80371B565A15}.Debug|x64.Build.0 = Debug|Any CPU {3A3F1C2C-A805-4EA2-B5AE-80371B565A15}.Release|Any CPU.ActiveCfg = Release|Any CPU {3A3F1C2C-A805-4EA2-B5AE-80371B565A15}.Release|Any CPU.Build.0 = Release|Any CPU + {3A3F1C2C-A805-4EA2-B5AE-80371B565A15}.Release|x64.ActiveCfg = Release|Any CPU + {3A3F1C2C-A805-4EA2-B5AE-80371B565A15}.Release|x64.Build.0 = Release|Any CPU {84CE1FE5-8141-4C2A-AC30-21BDC87F5D0A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {84CE1FE5-8141-4C2A-AC30-21BDC87F5D0A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {84CE1FE5-8141-4C2A-AC30-21BDC87F5D0A}.Debug|x64.ActiveCfg = Debug|Any CPU + {84CE1FE5-8141-4C2A-AC30-21BDC87F5D0A}.Debug|x64.Build.0 = Debug|Any CPU {84CE1FE5-8141-4C2A-AC30-21BDC87F5D0A}.Release|Any CPU.ActiveCfg = Release|Any CPU {84CE1FE5-8141-4C2A-AC30-21BDC87F5D0A}.Release|Any CPU.Build.0 = Release|Any CPU + {84CE1FE5-8141-4C2A-AC30-21BDC87F5D0A}.Release|x64.ActiveCfg = Release|Any CPU + {84CE1FE5-8141-4C2A-AC30-21BDC87F5D0A}.Release|x64.Build.0 = Release|Any CPU {5348A94F-7B3A-4B42-8555-2A1491971090}.Debug|Any CPU.ActiveCfg = Debug|x64 {5348A94F-7B3A-4B42-8555-2A1491971090}.Debug|Any CPU.Build.0 = Debug|x64 + {5348A94F-7B3A-4B42-8555-2A1491971090}.Debug|x64.ActiveCfg = Debug|x64 + {5348A94F-7B3A-4B42-8555-2A1491971090}.Debug|x64.Build.0 = Debug|x64 {5348A94F-7B3A-4B42-8555-2A1491971090}.Release|Any CPU.ActiveCfg = Release|x64 {5348A94F-7B3A-4B42-8555-2A1491971090}.Release|Any CPU.Build.0 = Release|x64 + {5348A94F-7B3A-4B42-8555-2A1491971090}.Release|x64.ActiveCfg = Release|x64 + {5348A94F-7B3A-4B42-8555-2A1491971090}.Release|x64.Build.0 = Release|x64 {16B58AE0-0E00-46FB-B114-72600DF6A78A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {16B58AE0-0E00-46FB-B114-72600DF6A78A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {16B58AE0-0E00-46FB-B114-72600DF6A78A}.Debug|x64.ActiveCfg = Debug|Any CPU + {16B58AE0-0E00-46FB-B114-72600DF6A78A}.Debug|x64.Build.0 = Debug|Any CPU {16B58AE0-0E00-46FB-B114-72600DF6A78A}.Release|Any CPU.ActiveCfg = Release|Any CPU {16B58AE0-0E00-46FB-B114-72600DF6A78A}.Release|Any CPU.Build.0 = Release|Any CPU + {16B58AE0-0E00-46FB-B114-72600DF6A78A}.Release|x64.ActiveCfg = Release|Any CPU + {16B58AE0-0E00-46FB-B114-72600DF6A78A}.Release|x64.Build.0 = Release|Any CPU {02A92F0E-98F1-4B42-883A-761272BAC185}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {02A92F0E-98F1-4B42-883A-761272BAC185}.Debug|Any CPU.Build.0 = Debug|Any CPU + {02A92F0E-98F1-4B42-883A-761272BAC185}.Debug|x64.ActiveCfg = Debug|Any CPU + {02A92F0E-98F1-4B42-883A-761272BAC185}.Debug|x64.Build.0 = Debug|Any CPU {02A92F0E-98F1-4B42-883A-761272BAC185}.Release|Any CPU.ActiveCfg = Release|Any CPU {02A92F0E-98F1-4B42-883A-761272BAC185}.Release|Any CPU.Build.0 = Release|Any CPU + {02A92F0E-98F1-4B42-883A-761272BAC185}.Release|x64.ActiveCfg = Release|Any CPU + {02A92F0E-98F1-4B42-883A-761272BAC185}.Release|x64.Build.0 = Release|Any CPU {808E6F51-9810-4461-AF4E-EF42EE47C806}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {808E6F51-9810-4461-AF4E-EF42EE47C806}.Debug|Any CPU.Build.0 = Debug|Any CPU + {808E6F51-9810-4461-AF4E-EF42EE47C806}.Debug|x64.ActiveCfg = Debug|Any CPU + {808E6F51-9810-4461-AF4E-EF42EE47C806}.Debug|x64.Build.0 = Debug|Any CPU {808E6F51-9810-4461-AF4E-EF42EE47C806}.Release|Any CPU.ActiveCfg = Release|Any CPU {808E6F51-9810-4461-AF4E-EF42EE47C806}.Release|Any CPU.Build.0 = Release|Any CPU + {808E6F51-9810-4461-AF4E-EF42EE47C806}.Release|x64.ActiveCfg = Release|Any CPU + {808E6F51-9810-4461-AF4E-EF42EE47C806}.Release|x64.Build.0 = Release|Any CPU {7F967CC6-905A-4198-A667-F9953C8A2139}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {7F967CC6-905A-4198-A667-F9953C8A2139}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7F967CC6-905A-4198-A667-F9953C8A2139}.Debug|x64.ActiveCfg = Debug|Any CPU + {7F967CC6-905A-4198-A667-F9953C8A2139}.Debug|x64.Build.0 = Debug|Any CPU {7F967CC6-905A-4198-A667-F9953C8A2139}.Release|Any CPU.ActiveCfg = Release|Any CPU {7F967CC6-905A-4198-A667-F9953C8A2139}.Release|Any CPU.Build.0 = Release|Any CPU + {7F967CC6-905A-4198-A667-F9953C8A2139}.Release|x64.ActiveCfg = Release|Any CPU + {7F967CC6-905A-4198-A667-F9953C8A2139}.Release|x64.Build.0 = Release|Any CPU {CFB5E6D3-C2FD-4D46-B8AC-7E39634E2CA9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {CFB5E6D3-C2FD-4D46-B8AC-7E39634E2CA9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CFB5E6D3-C2FD-4D46-B8AC-7E39634E2CA9}.Debug|x64.ActiveCfg = Debug|Any CPU + {CFB5E6D3-C2FD-4D46-B8AC-7E39634E2CA9}.Debug|x64.Build.0 = Debug|Any CPU {CFB5E6D3-C2FD-4D46-B8AC-7E39634E2CA9}.Release|Any CPU.ActiveCfg = Release|Any CPU {CFB5E6D3-C2FD-4D46-B8AC-7E39634E2CA9}.Release|Any CPU.Build.0 = Release|Any CPU + {CFB5E6D3-C2FD-4D46-B8AC-7E39634E2CA9}.Release|x64.ActiveCfg = Release|Any CPU + {CFB5E6D3-C2FD-4D46-B8AC-7E39634E2CA9}.Release|x64.Build.0 = Release|Any CPU {334726D9-6524-4318-82F1-4B72A1027E6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {334726D9-6524-4318-82F1-4B72A1027E6E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {334726D9-6524-4318-82F1-4B72A1027E6E}.Debug|x64.ActiveCfg = Debug|Any CPU + {334726D9-6524-4318-82F1-4B72A1027E6E}.Debug|x64.Build.0 = Debug|Any CPU {334726D9-6524-4318-82F1-4B72A1027E6E}.Release|Any CPU.ActiveCfg = Release|Any CPU {334726D9-6524-4318-82F1-4B72A1027E6E}.Release|Any CPU.Build.0 = Release|Any CPU + {334726D9-6524-4318-82F1-4B72A1027E6E}.Release|x64.ActiveCfg = Release|Any CPU + {334726D9-6524-4318-82F1-4B72A1027E6E}.Release|x64.Build.0 = Release|Any CPU {2AE339CD-1D81-46F4-AA19-703EB0CF86FB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {2AE339CD-1D81-46F4-AA19-703EB0CF86FB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2AE339CD-1D81-46F4-AA19-703EB0CF86FB}.Debug|x64.ActiveCfg = Debug|Any CPU + {2AE339CD-1D81-46F4-AA19-703EB0CF86FB}.Debug|x64.Build.0 = Debug|Any CPU {2AE339CD-1D81-46F4-AA19-703EB0CF86FB}.Release|Any CPU.ActiveCfg = Release|Any CPU {2AE339CD-1D81-46F4-AA19-703EB0CF86FB}.Release|Any CPU.Build.0 = Release|Any CPU + {2AE339CD-1D81-46F4-AA19-703EB0CF86FB}.Release|x64.ActiveCfg = Release|Any CPU + {2AE339CD-1D81-46F4-AA19-703EB0CF86FB}.Release|x64.Build.0 = Release|Any CPU {BE4A63D6-8153-458C-AC0E-744C320AA521}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {BE4A63D6-8153-458C-AC0E-744C320AA521}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BE4A63D6-8153-458C-AC0E-744C320AA521}.Debug|x64.ActiveCfg = Debug|Any CPU + {BE4A63D6-8153-458C-AC0E-744C320AA521}.Debug|x64.Build.0 = Debug|Any CPU {BE4A63D6-8153-458C-AC0E-744C320AA521}.Release|Any CPU.ActiveCfg = Release|Any CPU {BE4A63D6-8153-458C-AC0E-744C320AA521}.Release|Any CPU.Build.0 = Release|Any CPU + {BE4A63D6-8153-458C-AC0E-744C320AA521}.Release|x64.ActiveCfg = Release|Any CPU + {BE4A63D6-8153-458C-AC0E-744C320AA521}.Release|x64.Build.0 = Release|Any CPU {E2810FAA-8EB3-4B3B-8667-C3C586137D28}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {E2810FAA-8EB3-4B3B-8667-C3C586137D28}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E2810FAA-8EB3-4B3B-8667-C3C586137D28}.Debug|x64.ActiveCfg = Debug|Any CPU + {E2810FAA-8EB3-4B3B-8667-C3C586137D28}.Debug|x64.Build.0 = Debug|Any CPU {E2810FAA-8EB3-4B3B-8667-C3C586137D28}.Release|Any CPU.ActiveCfg = Release|Any CPU {E2810FAA-8EB3-4B3B-8667-C3C586137D28}.Release|Any CPU.Build.0 = Release|Any CPU + {E2810FAA-8EB3-4B3B-8667-C3C586137D28}.Release|x64.ActiveCfg = Release|Any CPU + {E2810FAA-8EB3-4B3B-8667-C3C586137D28}.Release|x64.Build.0 = Release|Any CPU {C8E18D2B-A445-42C4-B6C6-D6CAC51065DC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {C8E18D2B-A445-42C4-B6C6-D6CAC51065DC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C8E18D2B-A445-42C4-B6C6-D6CAC51065DC}.Debug|x64.ActiveCfg = Debug|Any CPU + {C8E18D2B-A445-42C4-B6C6-D6CAC51065DC}.Debug|x64.Build.0 = Debug|Any CPU {C8E18D2B-A445-42C4-B6C6-D6CAC51065DC}.Release|Any CPU.ActiveCfg = Release|Any CPU {C8E18D2B-A445-42C4-B6C6-D6CAC51065DC}.Release|Any CPU.Build.0 = Release|Any CPU + {C8E18D2B-A445-42C4-B6C6-D6CAC51065DC}.Release|x64.ActiveCfg = Release|Any CPU + {C8E18D2B-A445-42C4-B6C6-D6CAC51065DC}.Release|x64.Build.0 = Release|Any CPU {C52E282C-FA0A-4847-8D28-E9C4FDD8B729}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {C52E282C-FA0A-4847-8D28-E9C4FDD8B729}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C52E282C-FA0A-4847-8D28-E9C4FDD8B729}.Debug|x64.ActiveCfg = Debug|Any CPU + {C52E282C-FA0A-4847-8D28-E9C4FDD8B729}.Debug|x64.Build.0 = Debug|Any CPU {C52E282C-FA0A-4847-8D28-E9C4FDD8B729}.Release|Any CPU.ActiveCfg = Release|Any CPU {C52E282C-FA0A-4847-8D28-E9C4FDD8B729}.Release|Any CPU.Build.0 = Release|Any CPU + {C52E282C-FA0A-4847-8D28-E9C4FDD8B729}.Release|x64.ActiveCfg = Release|Any CPU + {C52E282C-FA0A-4847-8D28-E9C4FDD8B729}.Release|x64.Build.0 = Release|Any CPU {C72AED47-F5FF-4B7B-BD11-62149542FB22}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {C72AED47-F5FF-4B7B-BD11-62149542FB22}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C72AED47-F5FF-4B7B-BD11-62149542FB22}.Debug|x64.ActiveCfg = Debug|Any CPU + {C72AED47-F5FF-4B7B-BD11-62149542FB22}.Debug|x64.Build.0 = Debug|Any CPU {C72AED47-F5FF-4B7B-BD11-62149542FB22}.Release|Any CPU.ActiveCfg = Release|Any CPU {C72AED47-F5FF-4B7B-BD11-62149542FB22}.Release|Any CPU.Build.0 = Release|Any CPU + {C72AED47-F5FF-4B7B-BD11-62149542FB22}.Release|x64.ActiveCfg = Release|Any CPU + {C72AED47-F5FF-4B7B-BD11-62149542FB22}.Release|x64.Build.0 = Release|Any CPU {5600A9AF-934C-4225-B05D-F11DBC29E0FF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {5600A9AF-934C-4225-B05D-F11DBC29E0FF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5600A9AF-934C-4225-B05D-F11DBC29E0FF}.Debug|x64.ActiveCfg = Debug|Any CPU + {5600A9AF-934C-4225-B05D-F11DBC29E0FF}.Debug|x64.Build.0 = Debug|Any CPU {5600A9AF-934C-4225-B05D-F11DBC29E0FF}.Release|Any CPU.ActiveCfg = Release|Any CPU {5600A9AF-934C-4225-B05D-F11DBC29E0FF}.Release|Any CPU.Build.0 = Release|Any CPU + {5600A9AF-934C-4225-B05D-F11DBC29E0FF}.Release|x64.ActiveCfg = Release|Any CPU + {5600A9AF-934C-4225-B05D-F11DBC29E0FF}.Release|x64.Build.0 = Release|Any CPU {D69636BA-CCE9-4A85-845E-A378A2B03D62}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {D69636BA-CCE9-4A85-845E-A378A2B03D62}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D69636BA-CCE9-4A85-845E-A378A2B03D62}.Debug|x64.ActiveCfg = Debug|Any CPU + {D69636BA-CCE9-4A85-845E-A378A2B03D62}.Debug|x64.Build.0 = Debug|Any CPU {D69636BA-CCE9-4A85-845E-A378A2B03D62}.Release|Any CPU.ActiveCfg = Release|Any CPU {D69636BA-CCE9-4A85-845E-A378A2B03D62}.Release|Any CPU.Build.0 = Release|Any CPU + {D69636BA-CCE9-4A85-845E-A378A2B03D62}.Release|x64.ActiveCfg = Release|Any CPU + {D69636BA-CCE9-4A85-845E-A378A2B03D62}.Release|x64.Build.0 = Release|Any CPU {B7C52FC7-9678-442B-9B1E-F19F09B05606}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {B7C52FC7-9678-442B-9B1E-F19F09B05606}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B7C52FC7-9678-442B-9B1E-F19F09B05606}.Debug|x64.ActiveCfg = Debug|Any CPU + {B7C52FC7-9678-442B-9B1E-F19F09B05606}.Debug|x64.Build.0 = Debug|Any CPU {B7C52FC7-9678-442B-9B1E-F19F09B05606}.Release|Any CPU.ActiveCfg = Release|Any CPU {B7C52FC7-9678-442B-9B1E-F19F09B05606}.Release|Any CPU.Build.0 = Release|Any CPU + {B7C52FC7-9678-442B-9B1E-F19F09B05606}.Release|x64.ActiveCfg = Release|Any CPU + {B7C52FC7-9678-442B-9B1E-F19F09B05606}.Release|x64.Build.0 = Release|Any CPU {3F40EF71-126D-4D2B-84DB-37A574091B13}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {3F40EF71-126D-4D2B-84DB-37A574091B13}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3F40EF71-126D-4D2B-84DB-37A574091B13}.Debug|x64.ActiveCfg = Debug|Any CPU + {3F40EF71-126D-4D2B-84DB-37A574091B13}.Debug|x64.Build.0 = Debug|Any CPU {3F40EF71-126D-4D2B-84DB-37A574091B13}.Release|Any CPU.ActiveCfg = Release|Any CPU {3F40EF71-126D-4D2B-84DB-37A574091B13}.Release|Any CPU.Build.0 = Release|Any CPU + {3F40EF71-126D-4D2B-84DB-37A574091B13}.Release|x64.ActiveCfg = Release|Any CPU + {3F40EF71-126D-4D2B-84DB-37A574091B13}.Release|x64.Build.0 = Release|Any CPU {B9F00634-88A1-40EF-9DAD-814A307AD81F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {B9F00634-88A1-40EF-9DAD-814A307AD81F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B9F00634-88A1-40EF-9DAD-814A307AD81F}.Debug|x64.ActiveCfg = Debug|Any CPU + {B9F00634-88A1-40EF-9DAD-814A307AD81F}.Debug|x64.Build.0 = Debug|Any CPU {B9F00634-88A1-40EF-9DAD-814A307AD81F}.Release|Any CPU.ActiveCfg = Release|Any CPU {B9F00634-88A1-40EF-9DAD-814A307AD81F}.Release|Any CPU.Build.0 = Release|Any CPU + {B9F00634-88A1-40EF-9DAD-814A307AD81F}.Release|x64.ActiveCfg = Release|Any CPU + {B9F00634-88A1-40EF-9DAD-814A307AD81F}.Release|x64.Build.0 = Release|Any CPU {BE194924-7162-405D-BF6E-E6086BAA12F1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {BE194924-7162-405D-BF6E-E6086BAA12F1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BE194924-7162-405D-BF6E-E6086BAA12F1}.Debug|x64.ActiveCfg = Debug|Any CPU + {BE194924-7162-405D-BF6E-E6086BAA12F1}.Debug|x64.Build.0 = Debug|Any CPU {BE194924-7162-405D-BF6E-E6086BAA12F1}.Release|Any CPU.ActiveCfg = Release|Any CPU {BE194924-7162-405D-BF6E-E6086BAA12F1}.Release|Any CPU.Build.0 = Release|Any CPU + {BE194924-7162-405D-BF6E-E6086BAA12F1}.Release|x64.ActiveCfg = Release|Any CPU + {BE194924-7162-405D-BF6E-E6086BAA12F1}.Release|x64.Build.0 = Release|Any CPU {4478A162-4FE9-4737-A630-3899DC5935C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {4478A162-4FE9-4737-A630-3899DC5935C6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4478A162-4FE9-4737-A630-3899DC5935C6}.Debug|x64.ActiveCfg = Debug|Any CPU + {4478A162-4FE9-4737-A630-3899DC5935C6}.Debug|x64.Build.0 = Debug|Any CPU {4478A162-4FE9-4737-A630-3899DC5935C6}.Release|Any CPU.ActiveCfg = Release|Any CPU {4478A162-4FE9-4737-A630-3899DC5935C6}.Release|Any CPU.Build.0 = Release|Any CPU + {4478A162-4FE9-4737-A630-3899DC5935C6}.Release|x64.ActiveCfg = Release|Any CPU + {4478A162-4FE9-4737-A630-3899DC5935C6}.Release|x64.Build.0 = Release|Any CPU {7B73D864-9997-4637-8765-44C17FD09CE1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {7B73D864-9997-4637-8765-44C17FD09CE1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7B73D864-9997-4637-8765-44C17FD09CE1}.Debug|x64.ActiveCfg = Debug|Any CPU + {7B73D864-9997-4637-8765-44C17FD09CE1}.Debug|x64.Build.0 = Debug|Any CPU {7B73D864-9997-4637-8765-44C17FD09CE1}.Release|Any CPU.ActiveCfg = Release|Any CPU {7B73D864-9997-4637-8765-44C17FD09CE1}.Release|Any CPU.Build.0 = Release|Any CPU + {7B73D864-9997-4637-8765-44C17FD09CE1}.Release|x64.ActiveCfg = Release|Any CPU + {7B73D864-9997-4637-8765-44C17FD09CE1}.Release|x64.Build.0 = Release|Any CPU {DAB8847B-DE0A-45E2-A7DA-30432A36525B}.Debug|Any CPU.ActiveCfg = Debug|x64 {DAB8847B-DE0A-45E2-A7DA-30432A36525B}.Debug|Any CPU.Build.0 = Debug|x64 + {DAB8847B-DE0A-45E2-A7DA-30432A36525B}.Debug|x64.ActiveCfg = Debug|x64 + {DAB8847B-DE0A-45E2-A7DA-30432A36525B}.Debug|x64.Build.0 = Debug|x64 {DAB8847B-DE0A-45E2-A7DA-30432A36525B}.Release|Any CPU.ActiveCfg = Release|x64 {DAB8847B-DE0A-45E2-A7DA-30432A36525B}.Release|Any CPU.Build.0 = Release|x64 + {DAB8847B-DE0A-45E2-A7DA-30432A36525B}.Release|x64.ActiveCfg = Release|x64 + {DAB8847B-DE0A-45E2-A7DA-30432A36525B}.Release|x64.Build.0 = Release|x64 {825B11E7-9BF3-43B7-9BCE-4309EE404AEE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {825B11E7-9BF3-43B7-9BCE-4309EE404AEE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {825B11E7-9BF3-43B7-9BCE-4309EE404AEE}.Debug|x64.ActiveCfg = Debug|Any CPU + {825B11E7-9BF3-43B7-9BCE-4309EE404AEE}.Debug|x64.Build.0 = Debug|Any CPU {825B11E7-9BF3-43B7-9BCE-4309EE404AEE}.Release|Any CPU.ActiveCfg = Release|Any CPU {825B11E7-9BF3-43B7-9BCE-4309EE404AEE}.Release|Any CPU.Build.0 = Release|Any CPU + {825B11E7-9BF3-43B7-9BCE-4309EE404AEE}.Release|x64.ActiveCfg = Release|Any CPU + {825B11E7-9BF3-43B7-9BCE-4309EE404AEE}.Release|x64.Build.0 = Release|Any CPU {896CE6A5-59AA-4F7B-90EB-562F47D3C49E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {896CE6A5-59AA-4F7B-90EB-562F47D3C49E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {896CE6A5-59AA-4F7B-90EB-562F47D3C49E}.Debug|x64.ActiveCfg = Debug|Any CPU + {896CE6A5-59AA-4F7B-90EB-562F47D3C49E}.Debug|x64.Build.0 = Debug|Any CPU {896CE6A5-59AA-4F7B-90EB-562F47D3C49E}.Release|Any CPU.ActiveCfg = Release|Any CPU {896CE6A5-59AA-4F7B-90EB-562F47D3C49E}.Release|Any CPU.Build.0 = Release|Any CPU + {896CE6A5-59AA-4F7B-90EB-562F47D3C49E}.Release|x64.ActiveCfg = Release|Any CPU + {896CE6A5-59AA-4F7B-90EB-562F47D3C49E}.Release|x64.Build.0 = Release|Any CPU {FE571017-BC81-4B70-A876-58A52CAB40B4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {FE571017-BC81-4B70-A876-58A52CAB40B4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FE571017-BC81-4B70-A876-58A52CAB40B4}.Debug|x64.ActiveCfg = Debug|Any CPU + {FE571017-BC81-4B70-A876-58A52CAB40B4}.Debug|x64.Build.0 = Debug|Any CPU {FE571017-BC81-4B70-A876-58A52CAB40B4}.Release|Any CPU.ActiveCfg = Release|Any CPU {FE571017-BC81-4B70-A876-58A52CAB40B4}.Release|Any CPU.Build.0 = Release|Any CPU + {FE571017-BC81-4B70-A876-58A52CAB40B4}.Release|x64.ActiveCfg = Release|Any CPU + {FE571017-BC81-4B70-A876-58A52CAB40B4}.Release|x64.Build.0 = Release|Any CPU {F1D041E0-DDB2-41B8-97EE-5539B10D91BE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {F1D041E0-DDB2-41B8-97EE-5539B10D91BE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F1D041E0-DDB2-41B8-97EE-5539B10D91BE}.Debug|x64.ActiveCfg = Debug|Any CPU + {F1D041E0-DDB2-41B8-97EE-5539B10D91BE}.Debug|x64.Build.0 = Debug|Any CPU {F1D041E0-DDB2-41B8-97EE-5539B10D91BE}.Release|Any CPU.ActiveCfg = Release|Any CPU {F1D041E0-DDB2-41B8-97EE-5539B10D91BE}.Release|Any CPU.Build.0 = Release|Any CPU + {F1D041E0-DDB2-41B8-97EE-5539B10D91BE}.Release|x64.ActiveCfg = Release|Any CPU + {F1D041E0-DDB2-41B8-97EE-5539B10D91BE}.Release|x64.Build.0 = Release|Any CPU {6B572F54-0E2F-4223-8283-14B3BAB7534A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {6B572F54-0E2F-4223-8283-14B3BAB7534A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6B572F54-0E2F-4223-8283-14B3BAB7534A}.Debug|x64.ActiveCfg = Debug|Any CPU + {6B572F54-0E2F-4223-8283-14B3BAB7534A}.Debug|x64.Build.0 = Debug|Any CPU {6B572F54-0E2F-4223-8283-14B3BAB7534A}.Release|Any CPU.ActiveCfg = Release|Any CPU {6B572F54-0E2F-4223-8283-14B3BAB7534A}.Release|Any CPU.Build.0 = Release|Any CPU + {6B572F54-0E2F-4223-8283-14B3BAB7534A}.Release|x64.ActiveCfg = Release|Any CPU + {6B572F54-0E2F-4223-8283-14B3BAB7534A}.Release|x64.Build.0 = Release|Any CPU {084FB05C-4022-40FD-B00B-E3229B882F08}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {084FB05C-4022-40FD-B00B-E3229B882F08}.Debug|Any CPU.Build.0 = Debug|Any CPU + {084FB05C-4022-40FD-B00B-E3229B882F08}.Debug|x64.ActiveCfg = Debug|Any CPU + {084FB05C-4022-40FD-B00B-E3229B882F08}.Debug|x64.Build.0 = Debug|Any CPU {084FB05C-4022-40FD-B00B-E3229B882F08}.Release|Any CPU.ActiveCfg = Release|Any CPU {084FB05C-4022-40FD-B00B-E3229B882F08}.Release|Any CPU.Build.0 = Release|Any CPU + {084FB05C-4022-40FD-B00B-E3229B882F08}.Release|x64.ActiveCfg = Release|Any CPU + {084FB05C-4022-40FD-B00B-E3229B882F08}.Release|x64.Build.0 = Release|Any CPU {8D33307F-0E96-491A-9D31-9025709310F6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {8D33307F-0E96-491A-9D31-9025709310F6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8D33307F-0E96-491A-9D31-9025709310F6}.Debug|x64.ActiveCfg = Debug|Any CPU + {8D33307F-0E96-491A-9D31-9025709310F6}.Debug|x64.Build.0 = Debug|Any CPU {8D33307F-0E96-491A-9D31-9025709310F6}.Release|Any CPU.ActiveCfg = Release|Any CPU {8D33307F-0E96-491A-9D31-9025709310F6}.Release|Any CPU.Build.0 = Release|Any CPU + {8D33307F-0E96-491A-9D31-9025709310F6}.Release|x64.ActiveCfg = Release|Any CPU + {8D33307F-0E96-491A-9D31-9025709310F6}.Release|x64.Build.0 = Release|Any CPU {C91D0412-1BB2-40D2-8DCA-A48B6C5B7E67}.Debug|Any CPU.ActiveCfg = Debug|x64 {C91D0412-1BB2-40D2-8DCA-A48B6C5B7E67}.Debug|Any CPU.Build.0 = Debug|x64 + {C91D0412-1BB2-40D2-8DCA-A48B6C5B7E67}.Debug|x64.ActiveCfg = Debug|x64 + {C91D0412-1BB2-40D2-8DCA-A48B6C5B7E67}.Debug|x64.Build.0 = Debug|x64 {C91D0412-1BB2-40D2-8DCA-A48B6C5B7E67}.Release|Any CPU.ActiveCfg = Release|x64 {C91D0412-1BB2-40D2-8DCA-A48B6C5B7E67}.Release|Any CPU.Build.0 = Release|x64 + {C91D0412-1BB2-40D2-8DCA-A48B6C5B7E67}.Release|x64.ActiveCfg = Release|x64 + {C91D0412-1BB2-40D2-8DCA-A48B6C5B7E67}.Release|x64.Build.0 = Release|x64 {F31606FF-3737-45DC-8E89-6256AACD841F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {F31606FF-3737-45DC-8E89-6256AACD841F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F31606FF-3737-45DC-8E89-6256AACD841F}.Debug|x64.ActiveCfg = Debug|Any CPU + {F31606FF-3737-45DC-8E89-6256AACD841F}.Debug|x64.Build.0 = Debug|Any CPU {F31606FF-3737-45DC-8E89-6256AACD841F}.Release|Any CPU.ActiveCfg = Release|Any CPU {F31606FF-3737-45DC-8E89-6256AACD841F}.Release|Any CPU.Build.0 = Release|Any CPU + {F31606FF-3737-45DC-8E89-6256AACD841F}.Release|x64.ActiveCfg = Release|Any CPU + {F31606FF-3737-45DC-8E89-6256AACD841F}.Release|x64.Build.0 = Release|Any CPU {5AC206E0-6B2E-4DBC-9B8A-74A47C907C6A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {5AC206E0-6B2E-4DBC-9B8A-74A47C907C6A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5AC206E0-6B2E-4DBC-9B8A-74A47C907C6A}.Debug|x64.ActiveCfg = Debug|Any CPU + {5AC206E0-6B2E-4DBC-9B8A-74A47C907C6A}.Debug|x64.Build.0 = Debug|Any CPU {5AC206E0-6B2E-4DBC-9B8A-74A47C907C6A}.Release|Any CPU.ActiveCfg = Release|Any CPU {5AC206E0-6B2E-4DBC-9B8A-74A47C907C6A}.Release|Any CPU.Build.0 = Release|Any CPU + {5AC206E0-6B2E-4DBC-9B8A-74A47C907C6A}.Release|x64.ActiveCfg = Release|Any CPU + {5AC206E0-6B2E-4DBC-9B8A-74A47C907C6A}.Release|x64.Build.0 = Release|Any CPU {D18F494B-3639-4094-BD5E-BE8D81D5CD39}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {D18F494B-3639-4094-BD5E-BE8D81D5CD39}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D18F494B-3639-4094-BD5E-BE8D81D5CD39}.Debug|x64.ActiveCfg = Debug|Any CPU + {D18F494B-3639-4094-BD5E-BE8D81D5CD39}.Debug|x64.Build.0 = Debug|Any CPU {D18F494B-3639-4094-BD5E-BE8D81D5CD39}.Release|Any CPU.ActiveCfg = Release|Any CPU {D18F494B-3639-4094-BD5E-BE8D81D5CD39}.Release|Any CPU.Build.0 = Release|Any CPU + {D18F494B-3639-4094-BD5E-BE8D81D5CD39}.Release|x64.ActiveCfg = Release|Any CPU + {D18F494B-3639-4094-BD5E-BE8D81D5CD39}.Release|x64.Build.0 = Release|Any CPU {CAE417E8-0E2D-499D-B11D-78A95155AF8A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {CAE417E8-0E2D-499D-B11D-78A95155AF8A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CAE417E8-0E2D-499D-B11D-78A95155AF8A}.Debug|x64.ActiveCfg = Debug|Any CPU + {CAE417E8-0E2D-499D-B11D-78A95155AF8A}.Debug|x64.Build.0 = Debug|Any CPU {CAE417E8-0E2D-499D-B11D-78A95155AF8A}.Release|Any CPU.ActiveCfg = Release|Any CPU {CAE417E8-0E2D-499D-B11D-78A95155AF8A}.Release|Any CPU.Build.0 = Release|Any CPU + {CAE417E8-0E2D-499D-B11D-78A95155AF8A}.Release|x64.ActiveCfg = Release|Any CPU + {CAE417E8-0E2D-499D-B11D-78A95155AF8A}.Release|x64.Build.0 = Release|Any CPU {D649D56B-777C-4246-8D59-C32749D94D39}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {D649D56B-777C-4246-8D59-C32749D94D39}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D649D56B-777C-4246-8D59-C32749D94D39}.Debug|x64.ActiveCfg = Debug|Any CPU + {D649D56B-777C-4246-8D59-C32749D94D39}.Debug|x64.Build.0 = Debug|Any CPU {D649D56B-777C-4246-8D59-C32749D94D39}.Release|Any CPU.ActiveCfg = Release|Any CPU {D649D56B-777C-4246-8D59-C32749D94D39}.Release|Any CPU.Build.0 = Release|Any CPU + {D649D56B-777C-4246-8D59-C32749D94D39}.Release|x64.ActiveCfg = Release|Any CPU + {D649D56B-777C-4246-8D59-C32749D94D39}.Release|x64.Build.0 = Release|Any CPU {8B4F93E0-86D7-474C-8DD5-14E0AAD8BF91}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {8B4F93E0-86D7-474C-8DD5-14E0AAD8BF91}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8B4F93E0-86D7-474C-8DD5-14E0AAD8BF91}.Debug|x64.ActiveCfg = Debug|Any CPU + {8B4F93E0-86D7-474C-8DD5-14E0AAD8BF91}.Debug|x64.Build.0 = Debug|Any CPU {8B4F93E0-86D7-474C-8DD5-14E0AAD8BF91}.Release|Any CPU.ActiveCfg = Release|Any CPU {8B4F93E0-86D7-474C-8DD5-14E0AAD8BF91}.Release|Any CPU.Build.0 = Release|Any CPU + {8B4F93E0-86D7-474C-8DD5-14E0AAD8BF91}.Release|x64.ActiveCfg = Release|Any CPU + {8B4F93E0-86D7-474C-8DD5-14E0AAD8BF91}.Release|x64.Build.0 = Release|Any CPU {A0677BEA-ADB1-4950-89E6-89483D621A52}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {A0677BEA-ADB1-4950-89E6-89483D621A52}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A0677BEA-ADB1-4950-89E6-89483D621A52}.Debug|x64.ActiveCfg = Debug|Any CPU + {A0677BEA-ADB1-4950-89E6-89483D621A52}.Debug|x64.Build.0 = Debug|Any CPU {A0677BEA-ADB1-4950-89E6-89483D621A52}.Release|Any CPU.ActiveCfg = Release|Any CPU {A0677BEA-ADB1-4950-89E6-89483D621A52}.Release|Any CPU.Build.0 = Release|Any CPU + {A0677BEA-ADB1-4950-89E6-89483D621A52}.Release|x64.ActiveCfg = Release|Any CPU + {A0677BEA-ADB1-4950-89E6-89483D621A52}.Release|x64.Build.0 = Release|Any CPU {E0621435-AF35-4CFA-BE9E-3781AF6E161F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {E0621435-AF35-4CFA-BE9E-3781AF6E161F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E0621435-AF35-4CFA-BE9E-3781AF6E161F}.Debug|x64.ActiveCfg = Debug|Any CPU + {E0621435-AF35-4CFA-BE9E-3781AF6E161F}.Debug|x64.Build.0 = Debug|Any CPU {E0621435-AF35-4CFA-BE9E-3781AF6E161F}.Release|Any CPU.ActiveCfg = Release|Any CPU {E0621435-AF35-4CFA-BE9E-3781AF6E161F}.Release|Any CPU.Build.0 = Release|Any CPU + {E0621435-AF35-4CFA-BE9E-3781AF6E161F}.Release|x64.ActiveCfg = Release|Any CPU + {E0621435-AF35-4CFA-BE9E-3781AF6E161F}.Release|x64.Build.0 = Release|Any CPU {C3114338-AD22-4EBC-85C3-EE06045CDD78}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {C3114338-AD22-4EBC-85C3-EE06045CDD78}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C3114338-AD22-4EBC-85C3-EE06045CDD78}.Debug|x64.ActiveCfg = Debug|Any CPU + {C3114338-AD22-4EBC-85C3-EE06045CDD78}.Debug|x64.Build.0 = Debug|Any CPU {C3114338-AD22-4EBC-85C3-EE06045CDD78}.Release|Any CPU.ActiveCfg = Release|Any CPU {C3114338-AD22-4EBC-85C3-EE06045CDD78}.Release|Any CPU.Build.0 = Release|Any CPU + {C3114338-AD22-4EBC-85C3-EE06045CDD78}.Release|x64.ActiveCfg = Release|Any CPU + {C3114338-AD22-4EBC-85C3-EE06045CDD78}.Release|x64.Build.0 = Release|Any CPU {A1429F96-C7F8-49D8-ADB8-73A1A4DAA70F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {A1429F96-C7F8-49D8-ADB8-73A1A4DAA70F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A1429F96-C7F8-49D8-ADB8-73A1A4DAA70F}.Debug|x64.ActiveCfg = Debug|Any CPU + {A1429F96-C7F8-49D8-ADB8-73A1A4DAA70F}.Debug|x64.Build.0 = Debug|Any CPU {A1429F96-C7F8-49D8-ADB8-73A1A4DAA70F}.Release|Any CPU.ActiveCfg = Release|Any CPU {A1429F96-C7F8-49D8-ADB8-73A1A4DAA70F}.Release|Any CPU.Build.0 = Release|Any CPU + {A1429F96-C7F8-49D8-ADB8-73A1A4DAA70F}.Release|x64.ActiveCfg = Release|Any CPU + {A1429F96-C7F8-49D8-ADB8-73A1A4DAA70F}.Release|x64.Build.0 = Release|Any CPU {F50194C0-9561-40C7-B9CB-B977E3B3D76D}.Debug|Any CPU.ActiveCfg = Debug|ARM {F50194C0-9561-40C7-B9CB-B977E3B3D76D}.Debug|Any CPU.Build.0 = Debug|ARM + {F50194C0-9561-40C7-B9CB-B977E3B3D76D}.Debug|x64.ActiveCfg = Debug|x64 + {F50194C0-9561-40C7-B9CB-B977E3B3D76D}.Debug|x64.Build.0 = Debug|x64 {F50194C0-9561-40C7-B9CB-B977E3B3D76D}.Release|Any CPU.ActiveCfg = Release|ARM {F50194C0-9561-40C7-B9CB-B977E3B3D76D}.Release|Any CPU.Build.0 = Release|ARM + {F50194C0-9561-40C7-B9CB-B977E3B3D76D}.Release|x64.ActiveCfg = Release|x64 + {F50194C0-9561-40C7-B9CB-B977E3B3D76D}.Release|x64.Build.0 = Release|x64 {3434D5B2-B06F-4356-9E9B-90171CEF482B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {3434D5B2-B06F-4356-9E9B-90171CEF482B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3434D5B2-B06F-4356-9E9B-90171CEF482B}.Debug|x64.ActiveCfg = Debug|Any CPU + {3434D5B2-B06F-4356-9E9B-90171CEF482B}.Debug|x64.Build.0 = Debug|Any CPU {3434D5B2-B06F-4356-9E9B-90171CEF482B}.Release|Any CPU.ActiveCfg = Release|Any CPU {3434D5B2-B06F-4356-9E9B-90171CEF482B}.Release|Any CPU.Build.0 = Release|Any CPU + {3434D5B2-B06F-4356-9E9B-90171CEF482B}.Release|x64.ActiveCfg = Release|Any CPU + {3434D5B2-B06F-4356-9E9B-90171CEF482B}.Release|x64.Build.0 = Release|Any CPU {ECD9E150-8104-4DA3-B807-A6A4392A67C6}.Debug|Any CPU.ActiveCfg = Debug|ARM {ECD9E150-8104-4DA3-B807-A6A4392A67C6}.Debug|Any CPU.Build.0 = Debug|ARM + {ECD9E150-8104-4DA3-B807-A6A4392A67C6}.Debug|x64.ActiveCfg = Debug|Win32 + {ECD9E150-8104-4DA3-B807-A6A4392A67C6}.Debug|x64.Build.0 = Debug|Win32 {ECD9E150-8104-4DA3-B807-A6A4392A67C6}.Release|Any CPU.ActiveCfg = Release|ARM {ECD9E150-8104-4DA3-B807-A6A4392A67C6}.Release|Any CPU.Build.0 = Release|ARM + {ECD9E150-8104-4DA3-B807-A6A4392A67C6}.Release|x64.ActiveCfg = Release|Win32 + {ECD9E150-8104-4DA3-B807-A6A4392A67C6}.Release|x64.Build.0 = Release|Win32 {BE95524A-F9C2-4D0D-8F7E-1C7019B5A114}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {BE95524A-F9C2-4D0D-8F7E-1C7019B5A114}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BE95524A-F9C2-4D0D-8F7E-1C7019B5A114}.Debug|x64.ActiveCfg = Debug|Any CPU + {BE95524A-F9C2-4D0D-8F7E-1C7019B5A114}.Debug|x64.Build.0 = Debug|Any CPU {BE95524A-F9C2-4D0D-8F7E-1C7019B5A114}.Release|Any CPU.ActiveCfg = Release|Any CPU {BE95524A-F9C2-4D0D-8F7E-1C7019B5A114}.Release|Any CPU.Build.0 = Release|Any CPU + {BE95524A-F9C2-4D0D-8F7E-1C7019B5A114}.Release|x64.ActiveCfg = Release|Any CPU + {BE95524A-F9C2-4D0D-8F7E-1C7019B5A114}.Release|x64.Build.0 = Release|Any CPU {74504D41-B716-4B0B-B265-ED2A91A2A5C2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {74504D41-B716-4B0B-B265-ED2A91A2A5C2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {74504D41-B716-4B0B-B265-ED2A91A2A5C2}.Debug|x64.ActiveCfg = Debug|Any CPU + {74504D41-B716-4B0B-B265-ED2A91A2A5C2}.Debug|x64.Build.0 = Debug|Any CPU {74504D41-B716-4B0B-B265-ED2A91A2A5C2}.Release|Any CPU.ActiveCfg = Release|Any CPU {74504D41-B716-4B0B-B265-ED2A91A2A5C2}.Release|Any CPU.Build.0 = Release|Any CPU + {74504D41-B716-4B0B-B265-ED2A91A2A5C2}.Release|x64.ActiveCfg = Release|Any CPU + {74504D41-B716-4B0B-B265-ED2A91A2A5C2}.Release|x64.Build.0 = Release|Any CPU {D318834B-5A27-4EAC-B17D-A9BD7A2DCA0D}.Debug|Any CPU.ActiveCfg = Debug|ARM {D318834B-5A27-4EAC-B17D-A9BD7A2DCA0D}.Debug|Any CPU.Build.0 = Debug|ARM {D318834B-5A27-4EAC-B17D-A9BD7A2DCA0D}.Debug|Any CPU.Deploy.0 = Debug|ARM + {D318834B-5A27-4EAC-B17D-A9BD7A2DCA0D}.Debug|x64.ActiveCfg = Debug|ARM + {D318834B-5A27-4EAC-B17D-A9BD7A2DCA0D}.Debug|x64.Build.0 = Debug|ARM + {D318834B-5A27-4EAC-B17D-A9BD7A2DCA0D}.Debug|x64.Deploy.0 = Debug|ARM {D318834B-5A27-4EAC-B17D-A9BD7A2DCA0D}.Release|Any CPU.ActiveCfg = Release|ARM {D318834B-5A27-4EAC-B17D-A9BD7A2DCA0D}.Release|Any CPU.Build.0 = Release|ARM {D318834B-5A27-4EAC-B17D-A9BD7A2DCA0D}.Release|Any CPU.Deploy.0 = Release|ARM + {D318834B-5A27-4EAC-B17D-A9BD7A2DCA0D}.Release|x64.ActiveCfg = Release|ARM + {D318834B-5A27-4EAC-B17D-A9BD7A2DCA0D}.Release|x64.Build.0 = Release|ARM + {D318834B-5A27-4EAC-B17D-A9BD7A2DCA0D}.Release|x64.Deploy.0 = Release|ARM {272CEB19-2B5A-49BC-B8EA-CBC79AA87C37}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {272CEB19-2B5A-49BC-B8EA-CBC79AA87C37}.Debug|Any CPU.Build.0 = Debug|Any CPU + {272CEB19-2B5A-49BC-B8EA-CBC79AA87C37}.Debug|x64.ActiveCfg = Debug|Any CPU + {272CEB19-2B5A-49BC-B8EA-CBC79AA87C37}.Debug|x64.Build.0 = Debug|Any CPU {272CEB19-2B5A-49BC-B8EA-CBC79AA87C37}.Release|Any CPU.ActiveCfg = Release|Any CPU {272CEB19-2B5A-49BC-B8EA-CBC79AA87C37}.Release|Any CPU.Build.0 = Release|Any CPU + {272CEB19-2B5A-49BC-B8EA-CBC79AA87C37}.Release|x64.ActiveCfg = Release|Any CPU + {272CEB19-2B5A-49BC-B8EA-CBC79AA87C37}.Release|x64.Build.0 = Release|Any CPU {1C844B9E-A51C-483C-A045-7AB8F2012581}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {1C844B9E-A51C-483C-A045-7AB8F2012581}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1C844B9E-A51C-483C-A045-7AB8F2012581}.Debug|x64.ActiveCfg = Debug|Any CPU + {1C844B9E-A51C-483C-A045-7AB8F2012581}.Debug|x64.Build.0 = Debug|Any CPU {1C844B9E-A51C-483C-A045-7AB8F2012581}.Release|Any CPU.ActiveCfg = Release|Any CPU {1C844B9E-A51C-483C-A045-7AB8F2012581}.Release|Any CPU.Build.0 = Release|Any CPU + {1C844B9E-A51C-483C-A045-7AB8F2012581}.Release|x64.ActiveCfg = Release|Any CPU + {1C844B9E-A51C-483C-A045-7AB8F2012581}.Release|x64.Build.0 = Release|Any CPU {76E38559-0AF2-4AE3-BCE8-8653277A5B07}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {76E38559-0AF2-4AE3-BCE8-8653277A5B07}.Debug|Any CPU.Build.0 = Debug|Any CPU + {76E38559-0AF2-4AE3-BCE8-8653277A5B07}.Debug|x64.ActiveCfg = Debug|Any CPU + {76E38559-0AF2-4AE3-BCE8-8653277A5B07}.Debug|x64.Build.0 = Debug|Any CPU {76E38559-0AF2-4AE3-BCE8-8653277A5B07}.Release|Any CPU.ActiveCfg = Release|Any CPU {76E38559-0AF2-4AE3-BCE8-8653277A5B07}.Release|Any CPU.Build.0 = Release|Any CPU + {76E38559-0AF2-4AE3-BCE8-8653277A5B07}.Release|x64.ActiveCfg = Release|Any CPU + {76E38559-0AF2-4AE3-BCE8-8653277A5B07}.Release|x64.Build.0 = Release|Any CPU {41FBDC53-3167-4DEA-8B87-CFCCAFBCE7DD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {41FBDC53-3167-4DEA-8B87-CFCCAFBCE7DD}.Debug|Any CPU.Build.0 = Debug|Any CPU + {41FBDC53-3167-4DEA-8B87-CFCCAFBCE7DD}.Debug|x64.ActiveCfg = Debug|Any CPU + {41FBDC53-3167-4DEA-8B87-CFCCAFBCE7DD}.Debug|x64.Build.0 = Debug|Any CPU {41FBDC53-3167-4DEA-8B87-CFCCAFBCE7DD}.Release|Any CPU.ActiveCfg = Release|Any CPU {41FBDC53-3167-4DEA-8B87-CFCCAFBCE7DD}.Release|Any CPU.Build.0 = Release|Any CPU + {41FBDC53-3167-4DEA-8B87-CFCCAFBCE7DD}.Release|x64.ActiveCfg = Release|Any CPU + {41FBDC53-3167-4DEA-8B87-CFCCAFBCE7DD}.Release|x64.Build.0 = Release|Any CPU {860DC343-4022-4A6B-9053-8729DFBD5C96}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {860DC343-4022-4A6B-9053-8729DFBD5C96}.Debug|Any CPU.Build.0 = Debug|Any CPU + {860DC343-4022-4A6B-9053-8729DFBD5C96}.Debug|x64.ActiveCfg = Debug|Any CPU + {860DC343-4022-4A6B-9053-8729DFBD5C96}.Debug|x64.Build.0 = Debug|Any CPU {860DC343-4022-4A6B-9053-8729DFBD5C96}.Release|Any CPU.ActiveCfg = Release|Any CPU {860DC343-4022-4A6B-9053-8729DFBD5C96}.Release|Any CPU.Build.0 = Release|Any CPU + {860DC343-4022-4A6B-9053-8729DFBD5C96}.Release|x64.ActiveCfg = Release|Any CPU + {860DC343-4022-4A6B-9053-8729DFBD5C96}.Release|x64.Build.0 = Release|Any CPU {4C87BA2C-4EF4-447A-9409-4FD8CE9BC54D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4C87BA2C-4EF4-447A-9409-4FD8CE9BC54D}.Debug|x64.ActiveCfg = Debug|Any CPU {4C87BA2C-4EF4-447A-9409-4FD8CE9BC54D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4C87BA2C-4EF4-447A-9409-4FD8CE9BC54D}.Release|x64.ActiveCfg = Release|Any CPU {F2A56B74-FA18-4CD8-B686-61235A2DDB8C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F2A56B74-FA18-4CD8-B686-61235A2DDB8C}.Debug|x64.ActiveCfg = Debug|Any CPU {F2A56B74-FA18-4CD8-B686-61235A2DDB8C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F2A56B74-FA18-4CD8-B686-61235A2DDB8C}.Release|x64.ActiveCfg = Release|Any CPU {108D5CA8-8C44-4F7E-8C9F-02D6C1C6215F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {108D5CA8-8C44-4F7E-8C9F-02D6C1C6215F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {108D5CA8-8C44-4F7E-8C9F-02D6C1C6215F}.Debug|x64.ActiveCfg = Debug|Any CPU + {108D5CA8-8C44-4F7E-8C9F-02D6C1C6215F}.Debug|x64.Build.0 = Debug|Any CPU {108D5CA8-8C44-4F7E-8C9F-02D6C1C6215F}.Release|Any CPU.ActiveCfg = Release|Any CPU {108D5CA8-8C44-4F7E-8C9F-02D6C1C6215F}.Release|Any CPU.Build.0 = Release|Any CPU + {108D5CA8-8C44-4F7E-8C9F-02D6C1C6215F}.Release|x64.ActiveCfg = Release|Any CPU + {108D5CA8-8C44-4F7E-8C9F-02D6C1C6215F}.Release|x64.Build.0 = Release|Any CPU {1AFBBD50-CE3A-4792-BE84-15E897D281DD}.Debug|Any CPU.ActiveCfg = Debug|ARM {1AFBBD50-CE3A-4792-BE84-15E897D281DD}.Debug|Any CPU.Build.0 = Debug|ARM + {1AFBBD50-CE3A-4792-BE84-15E897D281DD}.Debug|x64.ActiveCfg = Debug|Win32 + {1AFBBD50-CE3A-4792-BE84-15E897D281DD}.Debug|x64.Build.0 = Debug|Win32 {1AFBBD50-CE3A-4792-BE84-15E897D281DD}.Release|Any CPU.ActiveCfg = Release|ARM {1AFBBD50-CE3A-4792-BE84-15E897D281DD}.Release|Any CPU.Build.0 = Release|ARM + {1AFBBD50-CE3A-4792-BE84-15E897D281DD}.Release|x64.ActiveCfg = Release|Win32 + {1AFBBD50-CE3A-4792-BE84-15E897D281DD}.Release|x64.Build.0 = Release|Win32 {632F209F-D0DD-4CE7-8975-C2D9F43EB964}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {632F209F-D0DD-4CE7-8975-C2D9F43EB964}.Debug|Any CPU.Build.0 = Debug|Any CPU + {632F209F-D0DD-4CE7-8975-C2D9F43EB964}.Debug|x64.ActiveCfg = Debug|Any CPU + {632F209F-D0DD-4CE7-8975-C2D9F43EB964}.Debug|x64.Build.0 = Debug|Any CPU {632F209F-D0DD-4CE7-8975-C2D9F43EB964}.Release|Any CPU.ActiveCfg = Release|Any CPU {632F209F-D0DD-4CE7-8975-C2D9F43EB964}.Release|Any CPU.Build.0 = Release|Any CPU + {632F209F-D0DD-4CE7-8975-C2D9F43EB964}.Release|x64.ActiveCfg = Release|Any CPU + {632F209F-D0DD-4CE7-8975-C2D9F43EB964}.Release|x64.Build.0 = Release|Any CPU {A94E45E7-5C81-4E7B-8500-F1C1B0DCB46E}.Debug|Any CPU.ActiveCfg = Debug|ARM {A94E45E7-5C81-4E7B-8500-F1C1B0DCB46E}.Debug|Any CPU.Build.0 = Debug|ARM + {A94E45E7-5C81-4E7B-8500-F1C1B0DCB46E}.Debug|x64.ActiveCfg = Debug|ARM + {A94E45E7-5C81-4E7B-8500-F1C1B0DCB46E}.Debug|x64.Build.0 = Debug|ARM {A94E45E7-5C81-4E7B-8500-F1C1B0DCB46E}.Release|Any CPU.ActiveCfg = Release|ARM {A94E45E7-5C81-4E7B-8500-F1C1B0DCB46E}.Release|Any CPU.Build.0 = Release|ARM + {A94E45E7-5C81-4E7B-8500-F1C1B0DCB46E}.Release|x64.ActiveCfg = Release|ARM + {A94E45E7-5C81-4E7B-8500-F1C1B0DCB46E}.Release|x64.Build.0 = Release|ARM {046DE977-8DFD-4847-A4D5-E65E3168E0CA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {046DE977-8DFD-4847-A4D5-E65E3168E0CA}.Debug|Any CPU.Build.0 = Debug|Any CPU + {046DE977-8DFD-4847-A4D5-E65E3168E0CA}.Debug|x64.ActiveCfg = Debug|Any CPU + {046DE977-8DFD-4847-A4D5-E65E3168E0CA}.Debug|x64.Build.0 = Debug|Any CPU {046DE977-8DFD-4847-A4D5-E65E3168E0CA}.Release|Any CPU.ActiveCfg = Release|Any CPU {046DE977-8DFD-4847-A4D5-E65E3168E0CA}.Release|Any CPU.Build.0 = Release|Any CPU + {046DE977-8DFD-4847-A4D5-E65E3168E0CA}.Release|x64.ActiveCfg = Release|Any CPU + {046DE977-8DFD-4847-A4D5-E65E3168E0CA}.Release|x64.Build.0 = Release|Any CPU {82378A12-3492-457D-AF76-CBF08CCE9832}.Debug|Any CPU.ActiveCfg = Debug|ARM {82378A12-3492-457D-AF76-CBF08CCE9832}.Debug|Any CPU.Build.0 = Debug|ARM {82378A12-3492-457D-AF76-CBF08CCE9832}.Debug|Any CPU.Deploy.0 = Debug|ARM + {82378A12-3492-457D-AF76-CBF08CCE9832}.Debug|x64.ActiveCfg = Debug|ARM + {82378A12-3492-457D-AF76-CBF08CCE9832}.Debug|x64.Build.0 = Debug|ARM + {82378A12-3492-457D-AF76-CBF08CCE9832}.Debug|x64.Deploy.0 = Debug|ARM {82378A12-3492-457D-AF76-CBF08CCE9832}.Release|Any CPU.ActiveCfg = Release|ARM {82378A12-3492-457D-AF76-CBF08CCE9832}.Release|Any CPU.Build.0 = Release|ARM {82378A12-3492-457D-AF76-CBF08CCE9832}.Release|Any CPU.Deploy.0 = Release|ARM + {82378A12-3492-457D-AF76-CBF08CCE9832}.Release|x64.ActiveCfg = Release|ARM + {82378A12-3492-457D-AF76-CBF08CCE9832}.Release|x64.Build.0 = Release|ARM + {82378A12-3492-457D-AF76-CBF08CCE9832}.Release|x64.Deploy.0 = Release|ARM {365D836D-AD15-428C-96D1-6C4870525FEF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {365D836D-AD15-428C-96D1-6C4870525FEF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {365D836D-AD15-428C-96D1-6C4870525FEF}.Debug|x64.ActiveCfg = Debug|Any CPU + {365D836D-AD15-428C-96D1-6C4870525FEF}.Debug|x64.Build.0 = Debug|Any CPU {365D836D-AD15-428C-96D1-6C4870525FEF}.Release|Any CPU.ActiveCfg = Release|Any CPU {365D836D-AD15-428C-96D1-6C4870525FEF}.Release|Any CPU.Build.0 = Release|Any CPU + {365D836D-AD15-428C-96D1-6C4870525FEF}.Release|x64.ActiveCfg = Release|Any CPU + {365D836D-AD15-428C-96D1-6C4870525FEF}.Release|x64.Build.0 = Release|Any CPU + {DF585F32-54AA-4553-AF91-6BBD1732E147}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {DF585F32-54AA-4553-AF91-6BBD1732E147}.Debug|Any CPU.Build.0 = Debug|Any CPU + {DF585F32-54AA-4553-AF91-6BBD1732E147}.Debug|x64.ActiveCfg = Debug|Any CPU + {DF585F32-54AA-4553-AF91-6BBD1732E147}.Debug|x64.Build.0 = Debug|Any CPU + {DF585F32-54AA-4553-AF91-6BBD1732E147}.Release|Any CPU.ActiveCfg = Release|Any CPU + {DF585F32-54AA-4553-AF91-6BBD1732E147}.Release|Any CPU.Build.0 = Release|Any CPU + {DF585F32-54AA-4553-AF91-6BBD1732E147}.Release|x64.ActiveCfg = Release|Any CPU + {DF585F32-54AA-4553-AF91-6BBD1732E147}.Release|x64.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -689,6 +1013,7 @@ Global {046DE977-8DFD-4847-A4D5-E65E3168E0CA} = {82274752-96AB-49DA-8B51-BA8356319308} {82378A12-3492-457D-AF76-CBF08CCE9832} = {82274752-96AB-49DA-8B51-BA8356319308} {365D836D-AD15-428C-96D1-6C4870525FEF} = {82274752-96AB-49DA-8B51-BA8356319308} + {DF585F32-54AA-4553-AF91-6BBD1732E147} = {3F77CC04-2E58-452B-8107-0C93E7944D4E} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {EAF15EE9-DCC5-411B-A9E5-7C2F3D132331} diff --git a/Sources/Audio/Microsoft.Psi.Audio.Linux/Microsoft.Psi.Audio.Linux.csproj b/Sources/Audio/Microsoft.Psi.Audio.Linux/Microsoft.Psi.Audio.Linux.csproj index ede6f18d0..5a9a11e01 100644 --- a/Sources/Audio/Microsoft.Psi.Audio.Linux/Microsoft.Psi.Audio.Linux.csproj +++ b/Sources/Audio/Microsoft.Psi.Audio.Linux/Microsoft.Psi.Audio.Linux.csproj @@ -4,6 +4,7 @@ netstandard2.0 true Provides Linux-specific APIs and components for audio capture and playback. + $(SolutionDir)\Build\PsiPackages diff --git a/Sources/Audio/Microsoft.Psi.Audio.Windows/Microsoft.Psi.Audio.Windows.csproj b/Sources/Audio/Microsoft.Psi.Audio.Windows/Microsoft.Psi.Audio.Windows.csproj index ddbda099f..d31ebbf9b 100644 --- a/Sources/Audio/Microsoft.Psi.Audio.Windows/Microsoft.Psi.Audio.Windows.csproj +++ b/Sources/Audio/Microsoft.Psi.Audio.Windows/Microsoft.Psi.Audio.Windows.csproj @@ -4,6 +4,7 @@ Provides Windows-specific APIs and components for audio capture, processing and playback. true Microsoft.Psi.Audio + $(SolutionDir)\Build\PsiPackages true diff --git a/Sources/Audio/Microsoft.Psi.Audio/Microsoft.Psi.Audio.csproj b/Sources/Audio/Microsoft.Psi.Audio/Microsoft.Psi.Audio.csproj index 7c3fd0d51..1254cb989 100644 --- a/Sources/Audio/Microsoft.Psi.Audio/Microsoft.Psi.Audio.csproj +++ b/Sources/Audio/Microsoft.Psi.Audio/Microsoft.Psi.Audio.csproj @@ -4,6 +4,7 @@ netstandard2.0 Provides data structures and APIs for audio processing. true + $(SolutionDir)\Build\PsiPackages diff --git a/Sources/Calibration/Microsoft.Psi.Calibration/Microsoft.Psi.Calibration.csproj b/Sources/Calibration/Microsoft.Psi.Calibration/Microsoft.Psi.Calibration.csproj index 7de76a6f3..f09429a45 100644 --- a/Sources/Calibration/Microsoft.Psi.Calibration/Microsoft.Psi.Calibration.csproj +++ b/Sources/Calibration/Microsoft.Psi.Calibration/Microsoft.Psi.Calibration.csproj @@ -5,6 +5,7 @@ true Microsoft.Psi.Calibration Microsoft.Psi.Calibration + $(SolutionDir)\Build\PsiPackages bin\Release\netstandard2.0\Microsoft.Psi.Calibration.xml diff --git a/Sources/Data/Microsoft.Psi.Data/Microsoft.Psi.Data.csproj b/Sources/Data/Microsoft.Psi.Data/Microsoft.Psi.Data.csproj index e482e6aef..ca9f06b89 100644 --- a/Sources/Data/Microsoft.Psi.Data/Microsoft.Psi.Data.csproj +++ b/Sources/Data/Microsoft.Psi.Data/Microsoft.Psi.Data.csproj @@ -1,9 +1,10 @@ - + netstandard2.0 true Provides APIs for manipulating datasets. + $(SolutionDir)\Build\PsiPackages diff --git a/Sources/Devices/Microsoft.Psi.DeviceManagement/Microsoft.Psi.DeviceManagement.csproj b/Sources/Devices/Microsoft.Psi.DeviceManagement/Microsoft.Psi.DeviceManagement.csproj index 116b00ce9..d0ab771a8 100644 --- a/Sources/Devices/Microsoft.Psi.DeviceManagement/Microsoft.Psi.DeviceManagement.csproj +++ b/Sources/Devices/Microsoft.Psi.DeviceManagement/Microsoft.Psi.DeviceManagement.csproj @@ -4,6 +4,7 @@ netstandard2.0 Provides data structures that support enumerating devices. true + $(SolutionDir)\Build\PsiPackages diff --git a/Sources/Filters/Microsoft.Psi.Filters/Microsoft.Psi.Filters.csproj b/Sources/Filters/Microsoft.Psi.Filters/Microsoft.Psi.Filters.csproj index faa88e45a..07cc6608f 100644 --- a/Sources/Filters/Microsoft.Psi.Filters/Microsoft.Psi.Filters.csproj +++ b/Sources/Filters/Microsoft.Psi.Filters/Microsoft.Psi.Filters.csproj @@ -6,6 +6,7 @@ true true ..\..\..\Build\Microsoft.Psi.ruleset + $(SolutionDir)\Build\PsiPackages diff --git a/Sources/Imaging/Microsoft.Psi.Imaging.Linux/Microsoft.Psi.Imaging.Linux.csproj b/Sources/Imaging/Microsoft.Psi.Imaging.Linux/Microsoft.Psi.Imaging.Linux.csproj index d44512e58..54fe76cb5 100644 --- a/Sources/Imaging/Microsoft.Psi.Imaging.Linux/Microsoft.Psi.Imaging.Linux.csproj +++ b/Sources/Imaging/Microsoft.Psi.Imaging.Linux/Microsoft.Psi.Imaging.Linux.csproj @@ -4,6 +4,7 @@ netstandard2.0 true Provides Linux-specific components for encoding and decoding images. + $(SolutionDir)\Build\PsiPackages diff --git a/Sources/Imaging/Microsoft.Psi.Imaging.Windows/Microsoft.Psi.Imaging.Windows.csproj b/Sources/Imaging/Microsoft.Psi.Imaging.Windows/Microsoft.Psi.Imaging.Windows.csproj index ba3f5fa23..b4b860827 100644 --- a/Sources/Imaging/Microsoft.Psi.Imaging.Windows/Microsoft.Psi.Imaging.Windows.csproj +++ b/Sources/Imaging/Microsoft.Psi.Imaging.Windows/Microsoft.Psi.Imaging.Windows.csproj @@ -4,6 +4,7 @@ Provides Windows-specific components for encoding and decoding images. true Microsoft.Psi.Imaging + $(SolutionDir)\Build\PsiPackages bin\Release\net472\Microsoft.Psi.Imaging.Windows.xml diff --git a/Sources/Imaging/Microsoft.Psi.Imaging/Microsoft.Psi.Imaging.csproj b/Sources/Imaging/Microsoft.Psi.Imaging/Microsoft.Psi.Imaging.csproj index 14f23672a..820954d7b 100644 --- a/Sources/Imaging/Microsoft.Psi.Imaging/Microsoft.Psi.Imaging.csproj +++ b/Sources/Imaging/Microsoft.Psi.Imaging/Microsoft.Psi.Imaging.csproj @@ -4,6 +4,7 @@ netstandard2.0 Provides APIs and components for representing and manipulating images. true + $(SolutionDir)\Build\PsiPackages diff --git a/Sources/Integrations/CognitiveServices/Microsoft.Psi.CognitiveServices.Face/Microsoft.Psi.CognitiveServices.Face.csproj b/Sources/Integrations/CognitiveServices/Microsoft.Psi.CognitiveServices.Face/Microsoft.Psi.CognitiveServices.Face.csproj index 7cffa8594..76dd951fa 100644 --- a/Sources/Integrations/CognitiveServices/Microsoft.Psi.CognitiveServices.Face/Microsoft.Psi.CognitiveServices.Face.csproj +++ b/Sources/Integrations/CognitiveServices/Microsoft.Psi.CognitiveServices.Face/Microsoft.Psi.CognitiveServices.Face.csproj @@ -5,6 +5,7 @@ true ../../../../Build/Microsoft.Psi.ruleset Provides components for using Microsoft's Cognitive Services Face API. + $(SolutionDir)\Build\PsiPackages bin\Debug\netstandard2.0\Microsoft.Psi.CognitiveServices.Face.xml diff --git a/Sources/Integrations/CognitiveServices/Microsoft.Psi.CognitiveServices.Language.Windows/Microsoft.Psi.CognitiveServices.Language.Windows.csproj b/Sources/Integrations/CognitiveServices/Microsoft.Psi.CognitiveServices.Language.Windows/Microsoft.Psi.CognitiveServices.Language.Windows.csproj index c575be6b0..b828d1521 100644 --- a/Sources/Integrations/CognitiveServices/Microsoft.Psi.CognitiveServices.Language.Windows/Microsoft.Psi.CognitiveServices.Language.Windows.csproj +++ b/Sources/Integrations/CognitiveServices/Microsoft.Psi.CognitiveServices.Language.Windows/Microsoft.Psi.CognitiveServices.Language.Windows.csproj @@ -4,6 +4,7 @@ true Provides components for using Microsoft's Cognitive Services Language API. Microsoft.Psi.CognitiveServices.Language + $(SolutionDir)\Build\PsiPackages bin\Release\net472\Microsoft.Psi.CognitiveServices.Language.Windows.xml diff --git a/Sources/Integrations/CognitiveServices/Microsoft.Psi.CognitiveServices.Language/Microsoft.Psi.CognitiveServices.Language.csproj b/Sources/Integrations/CognitiveServices/Microsoft.Psi.CognitiveServices.Language/Microsoft.Psi.CognitiveServices.Language.csproj index 26772ce17..0a7319098 100644 --- a/Sources/Integrations/CognitiveServices/Microsoft.Psi.CognitiveServices.Language/Microsoft.Psi.CognitiveServices.Language.csproj +++ b/Sources/Integrations/CognitiveServices/Microsoft.Psi.CognitiveServices.Language/Microsoft.Psi.CognitiveServices.Language.csproj @@ -4,6 +4,7 @@ netstandard2.0 Provides components for using Microsoft's Cognitive Services Language Understanding Service (LUIS). true + $(SolutionDir)\Build\PsiPackages diff --git a/Sources/Integrations/CognitiveServices/Microsoft.Psi.CognitiveServices.Speech/Microsoft.Psi.CognitiveServices.Speech.csproj b/Sources/Integrations/CognitiveServices/Microsoft.Psi.CognitiveServices.Speech/Microsoft.Psi.CognitiveServices.Speech.csproj index 1fb3db79a..84ef863f1 100644 --- a/Sources/Integrations/CognitiveServices/Microsoft.Psi.CognitiveServices.Speech/Microsoft.Psi.CognitiveServices.Speech.csproj +++ b/Sources/Integrations/CognitiveServices/Microsoft.Psi.CognitiveServices.Speech/Microsoft.Psi.CognitiveServices.Speech.csproj @@ -4,6 +4,7 @@ netstandard2.0 Provides components for using Microsoft's Cognitive Services Speech API. true + $(SolutionDir)\Build\PsiPackages diff --git a/Sources/Integrations/CognitiveServices/Microsoft.Psi.CognitiveServices.Vision/Microsoft.Psi.CognitiveServices.Vision.csproj b/Sources/Integrations/CognitiveServices/Microsoft.Psi.CognitiveServices.Vision/Microsoft.Psi.CognitiveServices.Vision.csproj index 1f2b8f4cc..1a7947289 100644 --- a/Sources/Integrations/CognitiveServices/Microsoft.Psi.CognitiveServices.Vision/Microsoft.Psi.CognitiveServices.Vision.csproj +++ b/Sources/Integrations/CognitiveServices/Microsoft.Psi.CognitiveServices.Vision/Microsoft.Psi.CognitiveServices.Vision.csproj @@ -7,6 +7,7 @@ Library + $(SolutionDir)\Build\PsiPackages bin\Release\netstandard2.0\Microsoft.Psi.CognitiveServices.Vision.xml diff --git a/Sources/Integrations/Onnx/Microsoft.Psi.Onnx.Cpu/Microsoft.Psi.Onnx.Cpu.csproj b/Sources/Integrations/Onnx/Microsoft.Psi.Onnx.Cpu/Microsoft.Psi.Onnx.Cpu.csproj index 238738d91..3d40230ba 100644 --- a/Sources/Integrations/Onnx/Microsoft.Psi.Onnx.Cpu/Microsoft.Psi.Onnx.Cpu.csproj +++ b/Sources/Integrations/Onnx/Microsoft.Psi.Onnx.Cpu/Microsoft.Psi.Onnx.Cpu.csproj @@ -4,6 +4,7 @@ Provides components for running ONNX models. true Microsoft.Psi.Onnx + $(SolutionDir)\Build\PsiPackages bin\Release\netstandard2.0\Microsoft.Psi.Onnx.Cpu.xml diff --git a/Sources/Integrations/Onnx/Microsoft.Psi.Onnx.Gpu/Microsoft.Psi.Onnx.Gpu.csproj b/Sources/Integrations/Onnx/Microsoft.Psi.Onnx.Gpu/Microsoft.Psi.Onnx.Gpu.csproj index 4291ddf8a..a81b64221 100644 --- a/Sources/Integrations/Onnx/Microsoft.Psi.Onnx.Gpu/Microsoft.Psi.Onnx.Gpu.csproj +++ b/Sources/Integrations/Onnx/Microsoft.Psi.Onnx.Gpu/Microsoft.Psi.Onnx.Gpu.csproj @@ -4,6 +4,7 @@ Provides components for running ONNX models. true Microsoft.Psi.Onnx + $(SolutionDir)\Build\PsiPackages bin\Release\netstandard2.0\Microsoft.Psi.Onnx.Gpu.xml diff --git a/Sources/Integrations/Onnx/Microsoft.Psi.Onnx.ModelRunners.Cpu/Microsoft.Psi.Onnx.ModelRunners.Cpu.csproj b/Sources/Integrations/Onnx/Microsoft.Psi.Onnx.ModelRunners.Cpu/Microsoft.Psi.Onnx.ModelRunners.Cpu.csproj index 753390c42..dcaf954c5 100644 --- a/Sources/Integrations/Onnx/Microsoft.Psi.Onnx.ModelRunners.Cpu/Microsoft.Psi.Onnx.ModelRunners.Cpu.csproj +++ b/Sources/Integrations/Onnx/Microsoft.Psi.Onnx.ModelRunners.Cpu/Microsoft.Psi.Onnx.ModelRunners.Cpu.csproj @@ -8,6 +8,7 @@ true true true + $(SolutionDir)\Build\PsiPackages diff --git a/Sources/Integrations/Onnx/Microsoft.Psi.Onnx.ModelRunners.Gpu/Microsoft.Psi.Onnx.ModelRunners.Gpu.csproj b/Sources/Integrations/Onnx/Microsoft.Psi.Onnx.ModelRunners.Gpu/Microsoft.Psi.Onnx.ModelRunners.Gpu.csproj index 668540868..7fc18ca71 100644 --- a/Sources/Integrations/Onnx/Microsoft.Psi.Onnx.ModelRunners.Gpu/Microsoft.Psi.Onnx.ModelRunners.Gpu.csproj +++ b/Sources/Integrations/Onnx/Microsoft.Psi.Onnx.ModelRunners.Gpu/Microsoft.Psi.Onnx.ModelRunners.Gpu.csproj @@ -8,6 +8,7 @@ true true true + $(SolutionDir)\Build\PsiPackages diff --git a/Sources/Integrations/Onnx/Microsoft.Psi.Onnx.Visualization.Windows/Microsoft.Psi.Onnx.Visualization.Windows.csproj b/Sources/Integrations/Onnx/Microsoft.Psi.Onnx.Visualization.Windows/Microsoft.Psi.Onnx.Visualization.Windows.csproj index 552df04df..91d0be72b 100644 --- a/Sources/Integrations/Onnx/Microsoft.Psi.Onnx.Visualization.Windows/Microsoft.Psi.Onnx.Visualization.Windows.csproj +++ b/Sources/Integrations/Onnx/Microsoft.Psi.Onnx.Visualization.Windows/Microsoft.Psi.Onnx.Visualization.Windows.csproj @@ -4,6 +4,7 @@ net472 Microsoft.Psi.Onnx.Visualization Provides visualizers for ONNX model runner output types defined in Microsoft.Psi.Onnx.ModelRunners. + $(SolutionDir)\Build\PsiPackages diff --git a/Sources/Kinect/Microsoft.Psi.AzureKinect.Visualization/Microsoft.Psi.AzureKinect.Visualization.Windows.x64.csproj b/Sources/Kinect/Microsoft.Psi.AzureKinect.Visualization/Microsoft.Psi.AzureKinect.Visualization.Windows.x64.csproj index c78b0b2f4..529b7cc86 100644 --- a/Sources/Kinect/Microsoft.Psi.AzureKinect.Visualization/Microsoft.Psi.AzureKinect.Visualization.Windows.x64.csproj +++ b/Sources/Kinect/Microsoft.Psi.AzureKinect.Visualization/Microsoft.Psi.AzureKinect.Visualization.Windows.x64.csproj @@ -5,7 +5,8 @@ Microsoft.Psi.AzureKinect.Visualization x64 ../../../Build/Microsoft.Psi.ruleset - + $(SolutionDir)\Build\PsiPackages + DEBUG;TRACE bin\Debug\net472\Microsoft.Psi.AzureKinect.Visualization.Windows.x64.xml diff --git a/Sources/Kinect/Microsoft.Psi.AzureKinect.x64/Microsoft.Psi.AzureKinect.x64.csproj b/Sources/Kinect/Microsoft.Psi.AzureKinect.x64/Microsoft.Psi.AzureKinect.x64.csproj index cbf3743c2..44c5cd8e5 100644 --- a/Sources/Kinect/Microsoft.Psi.AzureKinect.x64/Microsoft.Psi.AzureKinect.x64.csproj +++ b/Sources/Kinect/Microsoft.Psi.AzureKinect.x64/Microsoft.Psi.AzureKinect.x64.csproj @@ -8,6 +8,7 @@ Microsoft.Psi.AzureKinect.x64.nuspec configuration=$(Configuration);version=$(Version) ../../../Build/Microsoft.Psi.ruleset + $(SolutionDir)\Build\PsiPackages x64 diff --git a/Sources/Kinect/Microsoft.Psi.Kinect.Face.Windows.x64/Microsoft.Psi.Kinect.Face.Windows.x64.csproj b/Sources/Kinect/Microsoft.Psi.Kinect.Face.Windows.x64/Microsoft.Psi.Kinect.Face.Windows.x64.csproj index 3645d4726..034f64de8 100644 --- a/Sources/Kinect/Microsoft.Psi.Kinect.Face.Windows.x64/Microsoft.Psi.Kinect.Face.Windows.x64.csproj +++ b/Sources/Kinect/Microsoft.Psi.Kinect.Face.Windows.x64/Microsoft.Psi.Kinect.Face.Windows.x64.csproj @@ -4,6 +4,7 @@ Provides APIs and components for using Microsoft Kinect Face APIs. true Microsoft.Psi.Kinect.Face + $(SolutionDir)\Build\PsiPackages true diff --git a/Sources/Kinect/Microsoft.Psi.Kinect.Visualization.Windows/Microsoft.Psi.Kinect.Visualization.Windows.csproj b/Sources/Kinect/Microsoft.Psi.Kinect.Visualization.Windows/Microsoft.Psi.Kinect.Visualization.Windows.csproj index a8e4000b3..037f9d8b0 100644 --- a/Sources/Kinect/Microsoft.Psi.Kinect.Visualization.Windows/Microsoft.Psi.Kinect.Visualization.Windows.csproj +++ b/Sources/Kinect/Microsoft.Psi.Kinect.Visualization.Windows/Microsoft.Psi.Kinect.Visualization.Windows.csproj @@ -6,7 +6,8 @@ AnyCPU ../../../Build/Microsoft.Psi.ruleset Provides visualizers for Kinect v2. - + $(SolutionDir)\Build\PsiPackages + DEBUG;TRACE bin\Debug\net472\Microsoft.Psi.Kinect.Visualization.Windows.xml diff --git a/Sources/Kinect/Microsoft.Psi.Kinect.Windows/Microsoft.Psi.Kinect.Windows.csproj b/Sources/Kinect/Microsoft.Psi.Kinect.Windows/Microsoft.Psi.Kinect.Windows.csproj index 80bc592ae..1044c7ec8 100644 --- a/Sources/Kinect/Microsoft.Psi.Kinect.Windows/Microsoft.Psi.Kinect.Windows.csproj +++ b/Sources/Kinect/Microsoft.Psi.Kinect.Windows/Microsoft.Psi.Kinect.Windows.csproj @@ -4,6 +4,7 @@ Provides APIs and components for using Microsoft Kinect sensor. true Microsoft.Psi.Kinect + $(SolutionDir)\Build\PsiPackages true diff --git a/Sources/Language/Microsoft.Psi.Language/Microsoft.Psi.Language.csproj b/Sources/Language/Microsoft.Psi.Language/Microsoft.Psi.Language.csproj index ee8444a80..fcb93d474 100644 --- a/Sources/Language/Microsoft.Psi.Language/Microsoft.Psi.Language.csproj +++ b/Sources/Language/Microsoft.Psi.Language/Microsoft.Psi.Language.csproj @@ -4,6 +4,7 @@ netstandard2.0 Provides data structures for natural language processing. true + $(SolutionDir)\Build\PsiPackages diff --git a/Sources/Media/Microsoft.Psi.Media.Linux/Microsoft.Psi.Media.Linux.csproj b/Sources/Media/Microsoft.Psi.Media.Linux/Microsoft.Psi.Media.Linux.csproj index 842ba428f..fae3d51be 100644 --- a/Sources/Media/Microsoft.Psi.Media.Linux/Microsoft.Psi.Media.Linux.csproj +++ b/Sources/Media/Microsoft.Psi.Media.Linux/Microsoft.Psi.Media.Linux.csproj @@ -9,6 +9,7 @@ true Microsoft.Psi.Media Provides Linux-specific APIs and components for audio-visual capture. + $(SolutionDir)\Build\PsiPackages diff --git a/Sources/Media/Microsoft.Psi.Media.Windows.x64/Microsoft.Psi.Media.Windows.x64.csproj b/Sources/Media/Microsoft.Psi.Media.Windows.x64/Microsoft.Psi.Media.Windows.x64.csproj index 45ecc6892..786f9e13d 100644 --- a/Sources/Media/Microsoft.Psi.Media.Windows.x64/Microsoft.Psi.Media.Windows.x64.csproj +++ b/Sources/Media/Microsoft.Psi.Media.Windows.x64/Microsoft.Psi.Media.Windows.x64.csproj @@ -4,6 +4,7 @@ true Microsoft.Psi.Media Provides Windows-specific APIs and components for audio-visual capture and processing. + $(SolutionDir)\Build\PsiPackages bin\Release\net472\Microsoft.Psi.Media.Windows.x64.xml diff --git a/Sources/Media/Microsoft.Psi.Media/Microsoft.Psi.Media.csproj b/Sources/Media/Microsoft.Psi.Media/Microsoft.Psi.Media.csproj index 99a41bb01..150448bd9 100644 --- a/Sources/Media/Microsoft.Psi.Media/Microsoft.Psi.Media.csproj +++ b/Sources/Media/Microsoft.Psi.Media/Microsoft.Psi.Media.csproj @@ -5,6 +5,7 @@ Provides data structures and APIs for audio-visual media processing. true Microsoft.Psi.Media + $(SolutionDir)\Build\PsiPackages diff --git a/Sources/MixedReality/HoloLens2ResearchMode/HoloLens2ResearchMode.vcxproj b/Sources/MixedReality/HoloLens2ResearchMode/HoloLens2ResearchMode.vcxproj index bf4cc56d8..b8f71a52b 100644 --- a/Sources/MixedReality/HoloLens2ResearchMode/HoloLens2ResearchMode.vcxproj +++ b/Sources/MixedReality/HoloLens2ResearchMode/HoloLens2ResearchMode.vcxproj @@ -1,6 +1,6 @@ - + true true @@ -161,13 +161,13 @@ - + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - - + + \ No newline at end of file diff --git a/Sources/MixedReality/HoloLens2ResearchMode/packages.config b/Sources/MixedReality/HoloLens2ResearchMode/packages.config index 70bf1926d..1b8cfbd96 100644 --- a/Sources/MixedReality/HoloLens2ResearchMode/packages.config +++ b/Sources/MixedReality/HoloLens2ResearchMode/packages.config @@ -1,4 +1,4 @@  - + \ No newline at end of file diff --git a/Sources/MixedReality/HoloLensCapture/HoloLensCaptureApp/Package.appxmanifest b/Sources/MixedReality/HoloLensCapture/HoloLensCaptureApp/Package.appxmanifest index 3779d06d2..d51836e87 100644 --- a/Sources/MixedReality/HoloLensCapture/HoloLensCaptureApp/Package.appxmanifest +++ b/Sources/MixedReality/HoloLensCapture/HoloLensCaptureApp/Package.appxmanifest @@ -9,8 +9,8 @@ + Publisher="CN=AurélienM" + Version="1.0.2.0" /> diff --git a/Sources/MixedReality/Microsoft.Psi.MixedReality/Microsoft.Psi.MixedReality.csproj b/Sources/MixedReality/Microsoft.Psi.MixedReality/Microsoft.Psi.MixedReality.csproj index 7e72da776..3d63f907f 100644 --- a/Sources/MixedReality/Microsoft.Psi.MixedReality/Microsoft.Psi.MixedReality.csproj +++ b/Sources/MixedReality/Microsoft.Psi.MixedReality/Microsoft.Psi.MixedReality.csproj @@ -5,6 +5,7 @@ netstandard2.0 ../../../Build/Microsoft.Psi.ruleset true + $(SolutionDir)\Build\PsiPackages diff --git a/Sources/RealSense/Microsoft.Psi.RealSense_Interop.Windows.x64/packages.config b/Sources/RealSense/Microsoft.Psi.RealSense_Interop.Windows.x64/packages.config index 0f77b525b..750d2262e 100644 --- a/Sources/RealSense/Microsoft.Psi.RealSense_Interop.Windows.x64/packages.config +++ b/Sources/RealSense/Microsoft.Psi.RealSense_Interop.Windows.x64/packages.config @@ -1,4 +1,5 @@  - + + \ No newline at end of file diff --git a/Sources/Runtime/Microsoft.Psi.Interop/Microsoft.Psi.Interop.csproj b/Sources/Runtime/Microsoft.Psi.Interop/Microsoft.Psi.Interop.csproj index 04cc299fe..b1b43a6fd 100644 --- a/Sources/Runtime/Microsoft.Psi.Interop/Microsoft.Psi.Interop.csproj +++ b/Sources/Runtime/Microsoft.Psi.Interop/Microsoft.Psi.Interop.csproj @@ -4,6 +4,8 @@ netstandard2.0 true Provides APIs and components for interoperation with other data formats. + $(SolutionDir)\Build\PsiPackages + diff --git a/Sources/Runtime/Microsoft.Psi.Interop/Rendezvous/Operators.cs b/Sources/Runtime/Microsoft.Psi.Interop/Rendezvous/Operators.cs index 7a1608399..e8052c463 100644 --- a/Sources/Runtime/Microsoft.Psi.Interop/Rendezvous/Operators.cs +++ b/Sources/Runtime/Microsoft.Psi.Interop/Rendezvous/Operators.cs @@ -7,6 +7,7 @@ namespace Microsoft.Psi.Interop.Rendezvous using Microsoft.Psi.Interop.Serialization; using Microsoft.Psi.Interop.Transport; using Microsoft.Psi.Remoting; + using Microsoft.Psi.Serialization; /// /// Rendezvous related operators. @@ -70,6 +71,15 @@ public static Rendezvous.Endpoint ToRendezvousEndpoint(this NetMQWriter writer) public static Rendezvous.Endpoint ToRendezvousEndpoint(this RemoteClockExporter exporter, string host) => new Rendezvous.RemoteClockExporterEndpoint(host, exporter.Port); + /// + /// Create a rendezvous endpoint from a . + /// + /// from which to create endpoint. + /// Host address with which to create endpoint. + /// Rendezvous endpoint. + public static Rendezvous.Endpoint ToRendezvousEndpoint(this RemotePipelineClockExporter exporter, string host) + => new Rendezvous.RemotePipelineClockExporterEndpoint(host, exporter.Port); + /// /// Create a from a . /// @@ -106,9 +116,11 @@ public static Rendezvous.Endpoint ToRendezvousEndpoint(this RemoteExporter expor /// /// from which to create . /// The pipeline to add the component to. + /// Path for PsiStore. + /// Custom known serializers. /// . - public static RemoteImporter ToRemoteImporter(this Rendezvous.RemoteExporterEndpoint endpoint, Pipeline pipeline) - => new (pipeline, endpoint.Host, endpoint.Port); + public static RemoteImporter ToRemoteImporter(this Rendezvous.RemoteExporterEndpoint endpoint, Pipeline pipeline, string storePath, KnownSerializers knownSerializers) + => new (pipeline, storePath, endpoint.Host, endpoint.Port, knownSerializers); /// /// Create a from a . @@ -119,6 +131,15 @@ public static RemoteImporter ToRemoteImporter(this Rendezvous.RemoteExporterEndp public static RemoteClockImporter ToRemoteClockImporter(this Rendezvous.RemoteClockExporterEndpoint endpoint, Pipeline pipeline) => new (pipeline, endpoint.Host, endpoint.Port); + /// + /// Create a from a . + /// + /// from which to create . + /// The pipeline to add the component to. + /// . + public static RemotePipelineClockImporter ToRemotePipelineClockImporter(this Rendezvous.RemotePipelineClockExporterEndpoint endpoint, Pipeline pipeline) + => new (pipeline, endpoint.Host, endpoint.Port); + /// /// Writes a stream to a specified rendezvous process. /// diff --git a/Sources/Runtime/Microsoft.Psi.Interop/Rendezvous/Rendezvous.cs b/Sources/Runtime/Microsoft.Psi.Interop/Rendezvous/Rendezvous.cs index a910e257c..0e423250b 100644 --- a/Sources/Runtime/Microsoft.Psi.Interop/Rendezvous/Rendezvous.cs +++ b/Sources/Runtime/Microsoft.Psi.Interop/Rendezvous/Rendezvous.cs @@ -361,6 +361,42 @@ public override void AddStream(Stream stream) } } + /// + /// Represents a remote clock exporter endpoint providing clock information. + /// + /// + /// Endpoint does not provide any streams. Clock information is exchanged directly. + /// + public class RemotePipelineClockExporterEndpoint : Endpoint + { + /// + /// Initializes a new instance of the class. + /// + /// Host name used by the endpoint. + /// Port used by the endpoint. + public RemotePipelineClockExporterEndpoint(string host, int port) + { + this.Host = host; + this.Port = port; + } + + /// + /// Gets the endpoint host name. + /// + public string Host { get; private set; } + + /// + /// Gets the endpoint port. + /// + public int Port { get; private set; } + + /// + public override void AddStream(Stream stream) + { + throw new InvalidOperationException($"Cannot add streams to a {nameof(RemotePipelineClockExporterEndpoint)}"); + } + } + /// /// Represents an application process hosting endpoints. /// diff --git a/Sources/Runtime/Microsoft.Psi.Interop/Rendezvous/RendezvousRelay.cs b/Sources/Runtime/Microsoft.Psi.Interop/Rendezvous/RendezvousRelay.cs index 04ffc8197..32ab98fa4 100644 --- a/Sources/Runtime/Microsoft.Psi.Interop/Rendezvous/RendezvousRelay.cs +++ b/Sources/Runtime/Microsoft.Psi.Interop/Rendezvous/RendezvousRelay.cs @@ -69,6 +69,12 @@ protected static void WriteAddProcess(Rendezvous.Process process, BinaryWriter w writer.Write(remoteClockExporterEndpoint.Host); writer.Write(remoteClockExporterEndpoint.Port); } + else if (endpoint is Rendezvous.RemotePipelineClockExporterEndpoint remotePipelineClockExporterEndpoint) + { + writer.Write((byte)4); // RemotePipelineClockExporterEndpoint + writer.Write(remotePipelineClockExporterEndpoint.Host); + writer.Write(remotePipelineClockExporterEndpoint.Port); + } else { throw new ArgumentException($"Unknown type of Endpoint ({endpoint.GetType().Name})."); @@ -191,6 +197,11 @@ private static Rendezvous.Process ReadProcess(BinaryReader reader) port = reader.ReadInt32(); endpoint = new Rendezvous.RemoteClockExporterEndpoint(host, port); break; + case 4: // RemotePipelineClockExporerEndpoint + host = reader.ReadString(); + port = reader.ReadInt32(); + endpoint = new Rendezvous.RemotePipelineClockExporterEndpoint(host, port); + break; default: throw new Exception("Unknown type of Endpoint."); } diff --git a/Sources/Runtime/Microsoft.Psi.InteropAndroid/Microsoft.Psi.Interop.Android.csproj b/Sources/Runtime/Microsoft.Psi.InteropAndroid/Microsoft.Psi.Interop.Android.csproj new file mode 100644 index 000000000..9032dd930 --- /dev/null +++ b/Sources/Runtime/Microsoft.Psi.InteropAndroid/Microsoft.Psi.Interop.Android.csproj @@ -0,0 +1,48 @@ + + + + netstandard2.0 + False + Provides APIs and components for interoperation with other data formats. + + + + bin\Debug\netstandard2.0\Microsoft.Psi.Interop.xml + ../../../Build/Microsoft.Psi.ruleset + true + + + + + bin\Release\netstandard2.0\Microsoft.Psi.Interop.xml + ../../../Build/Microsoft.Psi.ruleset + true + + + + + + + + + + + + + + all + runtime; build; native; contentfiles; analyzers + + + + all + runtime; build; native; contentfiles; analyzers + + + + + + + + + diff --git a/Sources/Runtime/Microsoft.Psi.InteropAndroid/Readme.md b/Sources/Runtime/Microsoft.Psi.InteropAndroid/Readme.md new file mode 100644 index 000000000..197a6a9c2 --- /dev/null +++ b/Sources/Runtime/Microsoft.Psi.InteropAndroid/Readme.md @@ -0,0 +1,4 @@ +# Psi Interop Android + +Reduced version of `Microsoft.Psi.Interop` for using RendezVous system and TCPSource/Writer in Unity for Quest headset. +Should be used with Microsoft.Psi dll from this branch to be compatible. \ No newline at end of file diff --git a/Sources/Runtime/Microsoft.Psi.InteropAndroid/Rendezvous/Operators.cs b/Sources/Runtime/Microsoft.Psi.InteropAndroid/Rendezvous/Operators.cs new file mode 100644 index 000000000..31ba7bb93 --- /dev/null +++ b/Sources/Runtime/Microsoft.Psi.InteropAndroid/Rendezvous/Operators.cs @@ -0,0 +1,147 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +namespace Microsoft.Psi.Interop.Rendezvous +{ + using System; + using Microsoft.Psi.Interop.Serialization; + using Microsoft.Psi.Interop.Transport; + using Microsoft.Psi.Remoting; + using Microsoft.Psi.Serialization; + + /// + /// Rendezvous related operators. + /// + public static class Operators + { + /// + /// Create a rendezvous endpoint from a . + /// + /// Type of data stream. + /// from which to create endpoint. + /// Address with which to create endpoint. + /// The name of the rendezvous stream. + /// Rendezvous endpoint. + public static Rendezvous.Endpoint ToRendezvousEndpoint(this TcpWriter writer, string address, string streamName) + => new Rendezvous.TcpSourceEndpoint(address, writer.Port, new Rendezvous.Stream(streamName, typeof(T))); + + /// + /// Create a from a . + /// + /// Type of data stream. + /// from which to create . + /// The pipeline to add the component to. + /// The deserializer to use to deserialize messages. + /// An optional deallocator for the data. + /// An optional parameter indicating whether to use originating times received from the source over the network or to re-timestamp with the current pipeline time upon receiving. + /// An optional name for the TCP source component. + /// . + public static TcpSource ToTcpSource( + this Rendezvous.TcpSourceEndpoint endpoint, + Pipeline pipeline, + IFormatDeserializer deserializer, + Action deallocator = null, + bool useSourceOriginatingTimes = true, + string name = nameof(TcpSource)) + => new (pipeline, endpoint.Host, endpoint.Port, deserializer, deallocator, useSourceOriginatingTimes, name); + + /// + /// Create a rendezvous endpoint from a . + /// + /// from which to create endpoint. + /// Host address with which to create endpoint. + /// Rendezvous endpoint. + public static Rendezvous.Endpoint ToRendezvousEndpoint(this RemoteClockExporter exporter, string host) + => new Rendezvous.RemoteClockExporterEndpoint(host, exporter.Port); + + /// + /// Create a rendezvous endpoint from a . + /// + /// from which to create endpoint. + /// Host address with which to create endpoint. + /// Rendezvous endpoint. + public static Rendezvous.Endpoint ToRendezvousEndpoint(this RemotePipelineClockExporter exporter, string host) + => new Rendezvous.RemotePipelineClockExporterEndpoint(host, exporter.Port); + + /// + /// Create a rendezvous endpoint from a . + /// + /// from which to create endpoint. + /// Host name with which to create endpoint. + /// Rendezvous endpoint. + public static Rendezvous.Endpoint ToRendezvousEndpoint(this RemoteExporter exporter, string host) + { + // Each RemoteExporter is an endpoint emitting one or more streams. + var endpoint = new Rendezvous.RemoteExporterEndpoint(host, exporter.Port, exporter.TransportKind); + foreach (var m in exporter.Exporter.Metadata) + { + endpoint.AddStream(new Rendezvous.Stream(m.Name, m.TypeName)); + } + + return endpoint; + } + + /// + /// Create a from a . + /// + /// from which to create . + /// The pipeline to add the component to. + /// Path for PsiStore. + /// Custom known serializers. + /// . + public static RemoteImporter ToRemoteImporter(this Rendezvous.RemoteExporterEndpoint endpoint, Pipeline pipeline, string storePath, KnownSerializers knownSerializers) + => new (pipeline, storePath, endpoint.Host, endpoint.Port, knownSerializers); + + /// + /// Create a from a . + /// + /// from which to create . + /// The pipeline to add the component to. + /// . + public static RemoteImporter ToRemoteImporter(this Rendezvous.RemoteExporterEndpoint endpoint, Pipeline pipeline) + => new (pipeline, endpoint.Host, endpoint.Port); + + /// + /// Create a from a . + /// + /// from which to create . + /// The pipeline to add the component to. + /// . + public static RemoteClockImporter ToRemoteClockImporter(this Rendezvous.RemoteClockExporterEndpoint endpoint, Pipeline pipeline) + => new (pipeline, endpoint.Host, endpoint.Port); + + /// + /// Create a from a . + /// + /// from which to create . + /// The pipeline to add the component to. + /// . + public static RemotePipelineClockImporter ToRemotePipelineClockImporter(this Rendezvous.RemotePipelineClockExporterEndpoint endpoint, Pipeline pipeline) + => new (pipeline, endpoint.Host, endpoint.Port); + + /// + /// Writes a stream to a specified rendezvous process. + /// + /// The type of data in the stream. + /// The source stream to write. + /// The name under which to write the stream to the rendezvous process. + /// The rendezvous process. + /// The address to write the stream to. + /// The port to write the stream to. + /// The serializer to use when writing the stream. + /// An optional delivery policy. + public static void WriteToRendezvousProcess( + this IProducer source, + string streamName, + Rendezvous.Process rendezvousProcess, + string address, + int port, + IFormatSerializer serializer, + DeliveryPolicy deliveryPolicy = null) + { + var tcpWriter = new TcpWriter(source.Out.Pipeline, port, serializer); + source.PipeTo(tcpWriter, deliveryPolicy); + rendezvousProcess.AddEndpoint(tcpWriter.ToRendezvousEndpoint(address, streamName)); + } + } +} diff --git a/Sources/Runtime/Microsoft.Psi.InteropAndroid/Rendezvous/Readme.md b/Sources/Runtime/Microsoft.Psi.InteropAndroid/Rendezvous/Readme.md new file mode 100644 index 000000000..a2c561304 --- /dev/null +++ b/Sources/Runtime/Microsoft.Psi.InteropAndroid/Rendezvous/Readme.md @@ -0,0 +1,5 @@ +# Interop Rendezvous + +A distributed \psi system may have many separate pipelines running in separate processes, on separate machines, publishing and subscribing to streams being conveyed using various protocols. The rendezvous system allows each pipeline process to advertise its available streams and to discover those of other pipelines. This is accomplished by a centralized "rendezvous point" which maintains and relays endpoint connection and stream information. + +For more information, see [the Rendezvous System wiki page](https://github.com/microsoft/psi/wiki/Rendezvous-System). diff --git a/Sources/Runtime/Microsoft.Psi.InteropAndroid/Rendezvous/Rendezvous.cs b/Sources/Runtime/Microsoft.Psi.InteropAndroid/Rendezvous/Rendezvous.cs new file mode 100644 index 000000000..0e423250b --- /dev/null +++ b/Sources/Runtime/Microsoft.Psi.InteropAndroid/Rendezvous/Rendezvous.cs @@ -0,0 +1,458 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +namespace Microsoft.Psi.Interop.Rendezvous +{ + using System; + using System.Collections.Concurrent; + using System.Collections.Generic; + using System.Linq; + using Microsoft.Psi.Remoting; + + /// + /// Component that maintains rendezvous information. + /// + public class Rendezvous + { + /// + /// A rendezvous may know about many processes, each with many endpoints, each with many streams. + /// + private readonly ConcurrentDictionary processes = new (); + + private EventHandler processAdded; + + /// + /// Event raised when processes are added. + /// + /// Includes processes added before subscription. + public event EventHandler ProcessAdded + { + add + { + // inform late-joining handler of currently added processes + foreach (var p in this.Processes) + { + value.Invoke(this, p); + } + + this.processAdded += value; + } + + remove + { + this.processAdded -= value; + } + } + + /// + /// Event raised when processes are removed. + /// + public event EventHandler ProcessRemoved; + + /// + /// Gets the currently known processes. + /// + public IEnumerable Processes + { + get + { + return this.processes.Values; + } + } + + /// + /// Try to add a new process, if not already present. + /// + /// Process to add. + /// A value indicating whether the process was added. + public bool TryAddProcess(Process process) + { + if (this.processes.TryAdd(process.Name, process)) + { + this.processAdded?.Invoke(this, process); + return true; + } + + return false; + } + + /// + /// Try to remove a process if present. + /// + /// Process to remove. + /// A value indicating whether the process was removed. + public bool TryRemoveProcess(Process process) + { + if (this.processes.TryRemove(process.Name, out _)) + { + this.ProcessRemoved?.Invoke(this, process); + return true; + } + + return false; + } + + /// + /// Try to remove a process if present. + /// + /// Name of process to remove. + /// A value indicating whether the process was removed. + public bool TryRemoveProcess(string processName) + { + if (this.TryGetProcess(processName, out Process process)) + { + return this.TryRemoveProcess(process); + } + + return false; + } + + /// + /// Try to get process by name. + /// + /// Process name. + /// Process or null if not found. + /// A value indicating whether named process found. + public bool TryGetProcess(string processName, out Process process) + { + return this.processes.TryGetValue(processName, out process); + } + + /// + /// Represents a remoted stream of data. + /// + public class Stream + { + /// + /// Initializes a new instance of the class. + /// + /// Stream name. + /// Type name of stream data. + public Stream(string streamName, string typeName) + { + this.StreamName = streamName; + this.TypeName = typeName; + } + + /// + /// Initializes a new instance of the class. + /// + /// Stream name. + /// Type of stream data. + public Stream(string streamName, Type type) + : this(streamName, type.AssemblyQualifiedName) + { + } + + /// + /// Gets the stream name. + /// + public string StreamName { get; private set; } + + /// + /// Gets the type name of the stream data. + /// + public string TypeName { get; private set; } + } + + /// + /// Represents an endpoint providing remoted data streams. + /// + public abstract class Endpoint + { + private readonly ConcurrentDictionary streams; + + /// + /// Initializes a new instance of the class. + /// + /// Endpoint streams. + public Endpoint(IEnumerable streams) + { + this.streams = new ConcurrentDictionary(streams.Select(s => new KeyValuePair(s.StreamName, s))); + } + + /// + /// Initializes a new instance of the class. + /// + public Endpoint() + : this(Enumerable.Empty()) + { + } + + /// + /// Gets the streams. + /// + public IEnumerable Streams + { + get { return this.streams.Values; } + } + + /// + /// Add new stream. + /// + /// Endpoint stream to add. + public virtual void AddStream(Stream stream) + { + this.streams.TryAdd(stream.StreamName, stream); + } + } + + /// + /// Represents a simple TCP source endpoint providing a single remoted data stream. + /// + public class TcpSourceEndpoint : Endpoint + { + /// + /// Initializes a new instance of the class. + /// + /// Host name used by the endpoint. + /// Port number used by the endpoint. + /// Endpoint stream. + public TcpSourceEndpoint(string host, int port, Stream stream = null) + : base(stream is null ? Enumerable.Empty() : new[] { stream }) + { + if (string.IsNullOrEmpty(host)) + { + throw new ArgumentException("Host must be not null or empty."); + } + + this.Host = host; + this.Port = port; + } + + /// + /// Gets the endpoint address. + /// + public string Host { get; private set; } + + /// + /// Gets the endpoint port number. + /// + public int Port { get; private set; } + + /// + /// Gets the stream (Tcp endpoints have only one). + /// + public Stream Stream => this.Streams.FirstOrDefault(); + + /// + public override void AddStream(Stream stream) + { + if (this.Streams.Count() > 0) + { + throw new InvalidOperationException($"Cannot add more than one stream to a single {nameof(TcpSourceEndpoint)}"); + } + + base.AddStream(stream); + } + } + + /// + /// Represents a NetMQ source endpoint providing remoted data streams. + /// + public class NetMQSourceEndpoint : Endpoint + { + /// + /// Initializes a new instance of the class. + /// + /// Address used by the endpoint. + /// Endpoint streams. + public NetMQSourceEndpoint(string address, IEnumerable streams) + : base(streams) + { + this.Address = address; + } + + /// + /// Initializes a new instance of the class. + /// + /// Address used by the endpoint. + public NetMQSourceEndpoint(string address) + : this(address, Enumerable.Empty()) + { + } + + /// + /// Gets the endpoint address. + /// + public string Address { get; private set; } + } + + /// + /// Represents a remote exporter endpoint providing remoted data streams. + /// + public class RemoteExporterEndpoint : Endpoint + { + /// + /// Initializes a new instance of the class. + /// + /// Host name used by the endpoint. + /// Port used by the endpoint. + /// Tranport kind used by the endpoint. + /// Endpoint streams. + public RemoteExporterEndpoint(string host, int port, TransportKind transport, IEnumerable streams) + : base(streams) + { + this.Host = host; + this.Port = port; + this.Transport = transport; + } + + /// + /// Initializes a new instance of the class. + /// + /// Host name used by the endpoint. + /// Port used by the endpoint. + /// Tranport kind used by the endpoint. + public RemoteExporterEndpoint(string host, int port, TransportKind transport) + : this(host, port, transport, Enumerable.Empty()) + { + } + + /// + /// Gets the endpoint host name. + /// + public string Host { get; private set; } + + /// + /// Gets the endpoint port. + /// + public int Port { get; private set; } + + /// + /// Gets the endpoint transport kind. + /// + public TransportKind Transport { get; private set; } + } + + /// + /// Represents a remote clock exporter endpoint providing clock information. + /// + /// + /// Endpoint does not provide any streams. Clock information is exchanged directly. + /// + public class RemoteClockExporterEndpoint : Endpoint + { + /// + /// Initializes a new instance of the class. + /// + /// Host name used by the endpoint. + /// Port used by the endpoint. + public RemoteClockExporterEndpoint(string host, int port) + { + this.Host = host; + this.Port = port; + } + + /// + /// Gets the endpoint host name. + /// + public string Host { get; private set; } + + /// + /// Gets the endpoint port. + /// + public int Port { get; private set; } + + /// + public override void AddStream(Stream stream) + { + throw new InvalidOperationException($"Cannot add streams to a {nameof(RemoteClockExporterEndpoint)}"); + } + } + + /// + /// Represents a remote clock exporter endpoint providing clock information. + /// + /// + /// Endpoint does not provide any streams. Clock information is exchanged directly. + /// + public class RemotePipelineClockExporterEndpoint : Endpoint + { + /// + /// Initializes a new instance of the class. + /// + /// Host name used by the endpoint. + /// Port used by the endpoint. + public RemotePipelineClockExporterEndpoint(string host, int port) + { + this.Host = host; + this.Port = port; + } + + /// + /// Gets the endpoint host name. + /// + public string Host { get; private set; } + + /// + /// Gets the endpoint port. + /// + public int Port { get; private set; } + + /// + public override void AddStream(Stream stream) + { + throw new InvalidOperationException($"Cannot add streams to a {nameof(RemotePipelineClockExporterEndpoint)}"); + } + } + + /// + /// Represents an application process hosting endpoints. + /// + public class Process + { + private readonly List endpoints; + + /// + /// Initializes a new instance of the class. + /// + /// Unique name by which to refer to the process. + /// Process endpoints. + /// Optional process version (allowing negotiation of client compatibility). + public Process(string name, IEnumerable endpoints, string version = null) + { + this.Name = name; + this.Version = version ?? string.Empty; + this.endpoints = endpoints.ToList(); + } + + /// + /// Initializes a new instance of the class. + /// + /// Unique name by which to refer to the process. + /// Optional process version (allowing negotiation of client compatibility). + public Process(string name, string version = null) + : this(name, Enumerable.Empty(), version) + { + } + + /// + /// Gets the process name. + /// + public string Name { get; private set; } + + /// + /// Gets the process version. + /// + public string Version { get; private set; } + + /// + /// Gets the endpoints. + /// + public IEnumerable Endpoints + { + get { return this.endpoints; } + } + + /// + /// Add new endpoint. + /// + /// Process endpoint to add. + public void AddEndpoint(Endpoint endpoint) + { + this.endpoints.Add(endpoint); + } + } + } +} diff --git a/Sources/Runtime/Microsoft.Psi.InteropAndroid/Rendezvous/RendezvousClient.cs b/Sources/Runtime/Microsoft.Psi.InteropAndroid/Rendezvous/RendezvousClient.cs new file mode 100644 index 000000000..58cb028cb --- /dev/null +++ b/Sources/Runtime/Microsoft.Psi.InteropAndroid/Rendezvous/RendezvousClient.cs @@ -0,0 +1,191 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +namespace Microsoft.Psi.Interop.Rendezvous +{ + using System; + using System.Diagnostics; + using System.IO; + using System.Net.Sockets; + using System.Threading; + + /// + /// Client which connects to a and relays information. + /// + public class RendezvousClient : RendezvousRelay, IDisposable + { + private readonly string serverAddress; + private readonly int port; + private readonly EventWaitHandle connected = new (false, EventResetMode.ManualReset); + + private TcpClient client; + private BinaryReader reader; + private BinaryWriter writer; + private bool active = false; + private string clientAddress = null; + + /// + /// Initializes a new instance of the class. + /// + /// TCP address to which to connect. + /// Optional TCP port to which to connect. + /// Optional rendezvous instance to relay. + public RendezvousClient(string serverAddress, int port = RendezvousServer.DefaultPort, Rendezvous rendezvous = null) + : base(rendezvous) + { + this.serverAddress = serverAddress; + this.port = port; + this.connected.Reset(); + } + + /// + /// Gets wait handle for server connection being established. + /// + /// This should be waited on prior to trusting the processes list. + public EventWaitHandle Connected => this.connected; + + /// + /// Gets a value indicating whether the client is active. + /// + public bool IsActive => this.active; + + /// + /// Gets the client address (available after connection established). + /// + public string ClientAddress => this.clientAddress; + + /// + /// Start rendezvous client (blocking until connection is established). + /// + public void Start() + { + if (this.active) + { + throw new Exception($"{nameof(RendezvousClient)} already started."); + } + + this.Rendezvous.ProcessAdded += this.ProcessAdded; + this.Rendezvous.ProcessRemoved += this.ProcessRemoved; + while (!this.active) + { + try + { + (this.client = new TcpClient()).Connect(this.serverAddress, this.port); + var stream = this.client.GetStream(); + this.reader = new BinaryReader(stream); + this.writer = new BinaryWriter(stream); + this.writer.Write(RendezvousServer.ProtocolVersion); + this.writer.Flush(); + this.active = true; + new Thread(new ThreadStart(this.ReadFromServer)) { IsBackground = true }.Start(); + } + catch (SocketException ex) + { + Trace.WriteLine($"Failed to connect to {nameof(RendezvousServer)} (retrying): {ex.Message}"); + } + } + } + + /// + /// Stop rendezvous client. + /// + public void Stop() + { + TryWriteDisconnect(this.writer); + this.Rendezvous.ProcessAdded -= this.ProcessAdded; + this.Rendezvous.ProcessRemoved -= this.ProcessRemoved; + this.active = false; + this.client?.Close(); + this.client?.Dispose(); + this.client = null; + this.reader?.Dispose(); + this.reader = null; + this.writer?.Dispose(); + this.writer = null; + } + + /// + public void Dispose() + { + this.Stop(); + this.connected.Dispose(); + } + + private void ReadFromServer() + { + try + { + var version = this.reader.ReadInt16(); + if (version != RendezvousServer.ProtocolVersion) + { + var ex = new IOException($"{nameof(RendezvousServer)} protocol mismatch ({version})"); + this.ServerError(ex); + throw ex; + } + + this.clientAddress = this.reader.ReadString(); + + // initialize processes before signaling connected + var count = this.reader.ReadInt32(); + for (var i = 0; i < count; i++) + { + if (!this.ReadProcessUpdate(this.reader)) + { + this.ServerError(new IOException($"{nameof(RendezvousServer)} disconnected.")); + } + } + + this.connected.Set(); + + do + { + if (!this.ReadProcessUpdate(this.reader)) + { + this.ServerError(new IOException($"{nameof(RendezvousServer)} disconnected.")); + } + } + while (this.active && this.client.Connected); + } + catch (Exception ex) + { + this.ServerError(ex); + this.connected.Reset(); + } + } + + private void NotifyServer(Rendezvous.Process process, Action action) + { + try + { + action(process, this.writer); + } + catch (Exception ex) + { + this.ServerError(ex); + } + } + + private void ProcessAdded(object sender, Rendezvous.Process process) + { + if (this.writer != null) + { + this.NotifyServer(process, WriteAddProcess); + } + } + + private void ProcessRemoved(object sender, Rendezvous.Process process) + { + if (this.writer != null) + { + this.NotifyServer(process, WriteRemoveProcess); + } + } + + private void ServerError(Exception ex) + { + Trace.WriteLine($"{nameof(RendezvousServer)} error: {ex.Message}"); + this.Stop(); + this.OnError(ex); + } + } +} diff --git a/Sources/Runtime/Microsoft.Psi.InteropAndroid/Rendezvous/RendezvousClient.py b/Sources/Runtime/Microsoft.Psi.InteropAndroid/Rendezvous/RendezvousClient.py new file mode 100644 index 000000000..0b20b67ad --- /dev/null +++ b/Sources/Runtime/Microsoft.Psi.InteropAndroid/Rendezvous/RendezvousClient.py @@ -0,0 +1,212 @@ +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT license. + +import socket, struct, threading +from enum import IntEnum + +# Client which connects to a RendezvousServer and relays rendezvous information. +class RendezvousClient: + PROTOCOL_VERSION = 2 + + def __init__(self, host, port = 13331): + self.serverAddress = (host, port) + self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + + def __sendByte(self, b): + self.socket.send(struct.pack('b', b)) + + def __readByte(self): + b, = struct.unpack('b', self.socket.recv(1)) + return b + + def __sendInt(self, c): + self.socket.send(struct.pack(' + /// Base class for and . + /// + public abstract class RendezvousRelay + { + /// + /// Initializes a new instance of the class. + /// + /// Optional rendezvous instance to relay. + public RendezvousRelay(Rendezvous rendezvous = null) + { + this.Rendezvous = rendezvous ?? new Rendezvous(); + } + + /// + /// Event raised when errors occur. + /// + public event EventHandler Error; + + /// + /// Gets the underlying rendezvous. + /// + public Rendezvous Rendezvous { get; private set; } + + /// + /// Write update to add process. + /// + /// Process to add. + /// Writer to which to write update. + protected static void WriteAddProcess(Rendezvous.Process process, BinaryWriter writer) + { + writer.Write((byte)1); // add + writer.Write(process.Name); + writer.Write(process.Version); + writer.Write(process.Endpoints.Count()); + foreach (var endpoint in process.Endpoints) + { + if (endpoint is Rendezvous.TcpSourceEndpoint tcpEndpoint) + { + writer.Write((byte)0); // TcpEndpoint + writer.Write(tcpEndpoint.Host); + writer.Write(tcpEndpoint.Port); + } + else if (endpoint is Rendezvous.NetMQSourceEndpoint netMQEndpoint) + { + writer.Write((byte)1); // NetMQEndpoint + writer.Write(netMQEndpoint.Address); + } + else if (endpoint is Rendezvous.RemoteExporterEndpoint remoteExporterEndpoint) + { + writer.Write((byte)2); // RemoteExporterEndpoint + writer.Write(remoteExporterEndpoint.Host); + writer.Write(remoteExporterEndpoint.Port); + writer.Write((int)remoteExporterEndpoint.Transport); + } + else if (endpoint is Rendezvous.RemoteClockExporterEndpoint remoteClockExporterEndpoint) + { + writer.Write((byte)3); // RemoteClockExporterEndpoint + writer.Write(remoteClockExporterEndpoint.Host); + writer.Write(remoteClockExporterEndpoint.Port); + } + else if (endpoint is Rendezvous.RemotePipelineClockExporterEndpoint remotePipelineClockExporterEndpoint) + { + writer.Write((byte)4); // RemotePipelineClockExporterEndpoint + writer.Write(remotePipelineClockExporterEndpoint.Host); + writer.Write(remotePipelineClockExporterEndpoint.Port); + } + else + { + throw new ArgumentException($"Unknown type of Endpoint ({endpoint.GetType().Name})."); + } + + writer.Write(endpoint.Streams.Count()); + foreach (var stream in endpoint.Streams) + { + writer.Write(stream.StreamName); + writer.Write(stream.TypeName); + } + } + + writer.Flush(); + } + + /// + /// Write update to remove process. + /// + /// Process to remove. + /// Writer to which to write update. + protected static void WriteRemoveProcess(Rendezvous.Process process, BinaryWriter writer) + { + writer.Write((byte)2); // remove + writer.Write(process.Name); + writer.Flush(); + } + + /// + /// Write disconnection signal.. + /// + /// Writer to which to write disconnection signal. + protected static void TryWriteDisconnect(BinaryWriter writer) + { + try + { + writer?.Write((byte)0); // disconnect + writer?.Flush(); + } + catch + { + } + } + + /// + /// Raise error event. + /// + /// Underlying exception. + protected void OnError(Exception ex) + { + this.Error?.Invoke(this, ex); + } + + /// + /// Read process update record. + /// + /// Reader from which to read. + /// A value indicating whether an update was read, otherwise false indicated disconnection. + protected bool ReadProcessUpdate(BinaryReader reader) + { + try + { + switch (reader.ReadByte()) + { + case 0: // disconnect + return false; + case 1: // add process + var process = ReadProcess(reader); + this.Rendezvous.TryAddProcess(process); + return true; + case 2: // remove process + var name = reader.ReadString(); + this.Rendezvous.TryRemoveProcess(name); + return true; + default: + throw new Exception("Unexpected rendezvous action."); + } + } + catch (Exception ex) + { + this.OnError(ex); + return false; + } + } + + /// + /// Read process. + /// + /// Reader from which to deserialize. + /// Process. + private static Rendezvous.Process ReadProcess(BinaryReader reader) + { + var processName = reader.ReadString(); + var processVersion = reader.ReadString(); + var process = new Rendezvous.Process(processName, processVersion); + + // read endpoint info + var endpointCount = reader.ReadInt32(); + for (var i = 0; i < endpointCount; i++) + { + Rendezvous.Endpoint endpoint; + switch (reader.ReadByte()) + { + case 0: // TcpEndpoint + var address = reader.ReadString(); + var port = reader.ReadInt32(); + endpoint = new Rendezvous.TcpSourceEndpoint(address, port); + break; + case 1: // NetMQEndpoint + endpoint = new Rendezvous.NetMQSourceEndpoint(reader.ReadString()); + break; + case 2: // RemoteExporterEndpoint + var host = reader.ReadString(); + port = reader.ReadInt32(); + var transport = (TransportKind)reader.ReadInt32(); + endpoint = new Rendezvous.RemoteExporterEndpoint(host, port, transport); + break; + case 3: // RemoteClockExporerEndpoint + host = reader.ReadString(); + port = reader.ReadInt32(); + endpoint = new Rendezvous.RemoteClockExporterEndpoint(host, port); + break; + case 4: // RemotePipelineClockExporerEndpoint + host = reader.ReadString(); + port = reader.ReadInt32(); + endpoint = new Rendezvous.RemotePipelineClockExporterEndpoint(host, port); + break; + default: + throw new Exception("Unknown type of Endpoint."); + } + + // read stream info + var streamCount = reader.ReadInt32(); + for (var j = 0; j < streamCount; j++) + { + var name = reader.ReadString(); + var typeName = reader.ReadString(); + endpoint.AddStream(new Rendezvous.Stream(name, typeName)); + } + + process.AddEndpoint(endpoint); + } + + return process; + } + } +} diff --git a/Sources/Runtime/Microsoft.Psi.InteropAndroid/Rendezvous/RendezvousServer.cs b/Sources/Runtime/Microsoft.Psi.InteropAndroid/Rendezvous/RendezvousServer.cs new file mode 100644 index 000000000..38112f496 --- /dev/null +++ b/Sources/Runtime/Microsoft.Psi.InteropAndroid/Rendezvous/RendezvousServer.cs @@ -0,0 +1,208 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +namespace Microsoft.Psi.Interop.Rendezvous +{ + using System; + using System.Collections.Concurrent; + using System.Diagnostics; + using System.IO; + using System.Linq; + using System.Net; + using System.Net.Sockets; + using System.Threading; + + /// + /// Server which accepts one or more connections and relays information. + /// + public class RendezvousServer : RendezvousRelay, IDisposable + { + /// + /// Default TCP port on which to listen for clients. + /// + public const int DefaultPort = 13331; + + /// + /// Protocol version. + /// + internal const short ProtocolVersion = 2; + + private readonly int port; + private readonly ConcurrentDictionary writers = new (); + + private TcpListener listener; + private bool active = false; + private string serverAddress; + + /// + /// Initializes a new instance of the class. + /// + /// Optional TCP port on which to listen for clients. + /// Optional rendezvous instance to relay. + public RendezvousServer(int port = DefaultPort, Rendezvous rendezvous = null) + : base(rendezvous) + { + this.port = port; + } + + /// + /// Gets a value indicating whether the server is active. + /// + public bool IsActive => this.active; + + /// + /// Gets the server address (on which the most recent client connection was received). + /// + public string ServerAddress => this.serverAddress; + + /// + /// Start rendezvous client (blocking until connection is established). + /// + public void Start() + { + if (this.active) + { + throw new Exception($"{nameof(RendezvousServer)} already started."); + } + + this.Rendezvous.ProcessAdded += (_, process) => this.NotifyClients(process, WriteAddProcess); + this.Rendezvous.ProcessRemoved += (_, process) => this.NotifyClients(process, WriteRemoveProcess); + this.listener = new TcpListener(IPAddress.Any, this.port); + this.active = true; + new Thread(new ThreadStart(this.ListenForClients)) { IsBackground = true }.Start(); + } + + /// + /// Stop rendezvous client. + /// + public void Stop() + { + this.active = false; + + foreach (var writer in this.writers.Values) + { + TryWriteDisconnect(writer); + writer.Dispose(); + } + + this.listener?.Stop(); + this.listener = null; + } + + /// + public void Dispose() + { + this.Stop(); + } + + private void ListenForClients() + { + this.listener.Start(); + do + { + try + { + var client = this.listener.AcceptTcpClient(); + var remoteAddress = client.Client.RemoteEndPoint.ToString().Split(':')[0]; + var localAddress = client.Client.LocalEndPoint.ToString().Split(':')[0]; + var stream = client.GetStream(); + var reader = new BinaryReader(stream); + var version = reader.ReadInt16(); + if (version != ProtocolVersion) + { + var ex = new IOException($"{nameof(RendezvousClient)} protocol mismatch ({version})"); + this.ClientError(ex); + continue; + } + + var writer = new BinaryWriter(stream); + var guid = Guid.NewGuid(); + this.writers.TryAdd(guid, writer); + + writer.Write(ProtocolVersion); + writer.Write(remoteAddress); + writer.Write(this.Rendezvous.Processes.Count()); + writer.Flush(); + + // notify client of curent process info + foreach (var process in this.Rendezvous.Processes) + { + WriteAddProcess(process, writer); + } + + // set the server address to the address of the local endpoint from which the client was received + this.serverAddress = localAddress; + + new Thread(new ParameterizedThreadStart(this.ReadFromClient)) { IsBackground = true } + .Start(Tuple.Create(reader, guid)); + } + catch (Exception ex) + { + this.ClientError(ex); + } + } + while (this.active && this.listener != null); + } + + private void ReadFromClient(object param) + { + var tuple = param as Tuple; + var reader = tuple.Item1; + var guid = tuple.Item2; + try + { + do + { + if (!this.ReadProcessUpdate(reader)) + { + Trace.WriteLine($"{nameof(RendezvousClient)} disconnected."); + break; + } + } + while (this.active && this.listener != null); + } + catch (Exception ex) + { + this.ClientError(ex); + } + + reader.Dispose(); + if (this.writers.TryRemove(guid, out var writer)) + { + writer.Dispose(); + } + } + + private void NotifyClients(Rendezvous.Process process, Action action) + { + foreach (var kv in this.writers) + { + var writer = kv.Value; + try + { + if (writer.BaseStream.CanWrite) + { + action(process, writer); + } + } + catch (Exception ex) + { + this.ClientError(ex); + if (this.writers.TryRemove(kv.Key, out _)) + { + writer.Dispose(); + } + } + } + } + + private void ClientError(Exception ex) + { + Trace.WriteLine($"{nameof(RendezvousClient)} failed to connect: {ex.Message}"); + if (this.active) + { + this.OnError(ex); // note: only invoked on first error + } + } + } +} diff --git a/Sources/Runtime/Microsoft.Psi.InteropAndroid/Serialization/Format{T}.cs b/Sources/Runtime/Microsoft.Psi.InteropAndroid/Serialization/Format{T}.cs new file mode 100644 index 000000000..ae6b27e4d --- /dev/null +++ b/Sources/Runtime/Microsoft.Psi.InteropAndroid/Serialization/Format{T}.cs @@ -0,0 +1,88 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +namespace Microsoft.Psi.Interop.Serialization +{ + using System; + using System.IO; + using System.Text; + + /// + /// Helper class for making new formats (implementations of /. + /// + /// Type which is serialized/deserialized. + public class Format : IFormatSerializer, IFormatDeserializer, IDisposable + { + private readonly Func serialize; + private readonly Func deserialize; + private readonly MemoryStream memoryStream = null; + + /// + /// Initializes a new instance of the class. + /// + /// Serialization function. + /// Deserialization function. + public Format( + Func serializeFunc, + Func deserializeFunc) + { + this.serialize = serializeFunc; + this.deserialize = deserializeFunc; + } + + /// + /// Initializes a new instance of the class. + /// + /// Action to serialize using . + /// Function to deserialize using (also given raw payload, offset, length). + /// Serialization format. + public Format( + Action serializeAction, + Func deserializeFunc) + { + this.memoryStream = new MemoryStream(); + + this.serialize = (val, originatingTime) => + { + this.memoryStream.Position = 0; + using var writer = new BinaryWriter(this.memoryStream, Encoding.UTF8, true); + writer.Write(originatingTime.ToBinary()); + serializeAction(val, writer); + return (this.memoryStream.GetBuffer(), 0, (int)this.memoryStream.Position); + }; + + this.deserialize = (payload, offset, length) => + { + using var reader = new BinaryReader(new MemoryStream(payload, offset, length), Encoding.UTF8); + var originatingTime = DateTime.FromBinary(reader.ReadInt64()); + var val = deserializeFunc(reader, payload, offset, length); + return (val, originatingTime); + }; + } + + /// + /// Initializes a new instance of the class. + /// + /// Action to serialize using . + /// Function to deserialize using (also given raw payload, offset, length). + /// Serialization format. + public Format( + Action serializeAction, + Func deserializeFunc) + : this(serializeAction, (reader, _, _, _) => deserializeFunc(reader)) + { + } + + /// + public (byte[] Bytes, int Index, int Count) SerializeMessage(T message, DateTime originatingTime) + => this.serialize(message, originatingTime); + + /// + public (T Message, DateTime OriginatingTime) DeserializeMessage(byte[] payload, int index, int count) + => this.deserialize(payload, index, count); + + /// + public void Dispose() + => this.memoryStream?.Dispose(); + } +} diff --git a/Sources/Runtime/Microsoft.Psi.InteropAndroid/Serialization/IFormatDeserializer.cs b/Sources/Runtime/Microsoft.Psi.InteropAndroid/Serialization/IFormatDeserializer.cs new file mode 100644 index 000000000..2fc9cb0a9 --- /dev/null +++ b/Sources/Runtime/Microsoft.Psi.InteropAndroid/Serialization/IFormatDeserializer.cs @@ -0,0 +1,23 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +namespace Microsoft.Psi.Interop.Serialization +{ + using System; + + /// + /// Format deserializer interface. + /// + /// Type which is deserialized. + public interface IFormatDeserializer + { + /// + /// Deserialize single message and originating time stamp payload. + /// + /// Payload bytes. + /// Starting index of message data. + /// Number of bytes constituting message data. + /// Type of primitive or IEnumerable/ExpandoObject of primitive as well as originating time stamp. + (T Message, DateTime OriginatingTime) DeserializeMessage(byte[] payload, int index, int count); + } +} \ No newline at end of file diff --git a/Sources/Runtime/Microsoft.Psi.InteropAndroid/Serialization/IFormatSerializer.cs b/Sources/Runtime/Microsoft.Psi.InteropAndroid/Serialization/IFormatSerializer.cs new file mode 100644 index 000000000..0634d8db3 --- /dev/null +++ b/Sources/Runtime/Microsoft.Psi.InteropAndroid/Serialization/IFormatSerializer.cs @@ -0,0 +1,22 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +namespace Microsoft.Psi.Interop.Serialization +{ + using System; + + /// + /// Format serializer interface. + /// + /// Type which is deserialized. + public interface IFormatSerializer + { + /// + /// Serialize single message with originating time stamp. + /// + /// Message of type. + /// Originating time of message. + /// Serialized bytes, index and count. + (byte[] Bytes, int Index, int Count) SerializeMessage(T message, DateTime originatingTime); + } +} diff --git a/Sources/Runtime/Microsoft.Psi.InteropAndroid/Serialization/Readme.md b/Sources/Runtime/Microsoft.Psi.InteropAndroid/Serialization/Readme.md new file mode 100644 index 000000000..49c5be8d4 --- /dev/null +++ b/Sources/Runtime/Microsoft.Psi.InteropAndroid/Serialization/Readme.md @@ -0,0 +1,45 @@ +# Interop Serialization Interfaces + +Each [concrete format](../Format/Readme.md) is an implementation of several serialization interfaces. These interfaces are similar to `Microsoft.Psi.Serialization.ISerializer` but are specific to dynamic types and don't include cloning. An `IFormatSerializer` converts a message of any type, along with its originating time, to a simple `byte[]`, while an `IFormatDeserializer` reverses the process; taking a `byte[]` and returning a message and originating time. + +```csharp +public interface IFormatSerializer +{ + (byte[], int, int) SerializeMessage(dynamic message, DateTime originatingTime); +} + +public interface IFormatDeserializer +{ + (dynamic, DateTime) DeserializeMessage(byte[] payload, int index, int count); +} +``` + +Versions of these intended for persistent storage, where a sequence of messages are persisted together, are provided as well. + +```csharp +public interface IPersistentFormatSerializer +{ + dynamic PersistHeader(dynamic message, Stream stream); + + void PersistRecord(dynamic message, DateTime originatingTime, bool first, Stream stream, dynamic state); + + void PersistFooter(Stream stream, dynamic state); +} + +public interface IPersistentFormatDeserializer +{ + IEnumerable<(dynamic, DateTime)> DeserializeRecords(Stream stream); +} +``` + +`IPersistentFormatSerializer` writes a set of messages to a `Stream`. The header may be field names in the case of CSV, a simple array container in the case of JSON, etc. Similarly, the footer may close such constructs. The `PersistRecord` method is very similar to `SerializeMessage` above, but may include message framing or delimiting, such as `Environment.NewLine` delimiting records in CSV or a comma for JSON, or maybe a length-prefix for binary MessagePack. The `IPersistentFormatDeserializer` reverses the process; producing messages and timestamps from a previously serialized stream. + +Notice that messages lose their types at this point; generally becoming `dynamic` over `ExpandoObject` and primitives. This means that it may no longer be possible to reify as the original .NET types after serialization in this way (unlike with Psi Stores). + +Note also that, while `dynamic` may be any type, it is recommended that deserialization returns primitives or composites in the form of `ExpandoObject`. For example, the JSON implementation uses `Newtonsoft.Json.JsonConvert` under the covers but, rather than returning `JObject`, is careful to not expose dependencies on this library. + +## Note About `dynamic` and `ExpandoObject` + +A `dynamic` type may be *anything* in .NET and an `ExpandoObject` may have properties of *any* type. We are using these types to represent untyped values flowing through the system in various places above. + +Composite/structured values should be restricted to collections (arrays or `IEnumerable<_>`) of primitives or other composites or `ExpandoObject` of named properties of primitives or other composites. Any "shape" of data is representable this way. \ No newline at end of file diff --git a/Sources/Runtime/Microsoft.Psi.InteropAndroid/Transport/Readme.md b/Sources/Runtime/Microsoft.Psi.InteropAndroid/Transport/Readme.md new file mode 100644 index 000000000..ae0a50b4f --- /dev/null +++ b/Sources/Runtime/Microsoft.Psi.InteropAndroid/Transport/Readme.md @@ -0,0 +1,167 @@ +# Interop Transports + +Here we describe several ways of getting data in and out of Psi in order to interop with other languages and platforms. + +## Generic File Source/Writer Component + +The simplest transport is via the file system. This is most appropriate for offline/batch processing. A generic `FileWriter` component is provided that, when given an `IPersistentFormatSerializer`, will persist a message stream to disk. Similarly, a generic `FileSource` component is provided, taking an `IPersistentFormatDeserializer`, to reconstitute such a persisted file as a Psi stream. + +For example, a stream of messages of any type may be written using the `FileWriter`: + +```csharp +using (var p = Pipeline.Create()) +{ + var gen = Generators.Range(p, 0, 1000); + var sin = gen.Select(x => Math.Sin(x / 100.0)); + var writer = new FileWriter(p, "TestFile.json", JsonFormat.Instance); + sin.PipeTo(writer); + p.Run(); +} +``` + +This produces a JSON file containing something like: + +```json +[ + { + "originatingTime": "2018-11-12T22:48:58.3770983Z", + "message": 0.0 + }, + { + "originatingTime": "2018-11-12T22:48:58.3770984Z", + "message": 0.0099998333341666645 + }, + { + "originatingTime": "2018-11-12T22:48:58.3770985Z", + "message": 0.01999866669333308 + }, + ... +] +``` + +This may then be read back into a proper \\psi stream using a `FileSource`: + +```csharp +using (var p = Pipeline.Create()) +{ + var reader = new FileSource(p, "TestFile.json", JsonFormat.Instance); + reader.Do(Console.WriteLine); + p.Run(); +} +``` + +## Message Queue Components + +Message queues are most appropriate for live interop. Currently, only ZeroMQ is supported. In the future, Azure Storage Queue and/or Service Bus as well as Amazon SMQ may be supported. While the Psi `RemoteExporter`/`Importer` is an excellent, high performance means of remoting, it assumes .NET on both ends. To facilitate remoting to Python and others, we provide message queuing components. These components are `IConsumer` and simply push to a message queue or are `IProducer` and take messages from a queue. Given an `IFormatSerializer`/`Deserializer`, they handle packing/unpacking messages. + +### ZeroMQ/NetMQ + +An implementation for ZeroMQ (calleg NetMQ for .NET) is provided. These components are configured with a URI, a topic name and an `IFormatSerializer`. + +#### `NetMQWriter` + +A stream of messages of any type may be piped to a `NetMQWriter`. These will be serialized and sent to the queue for consumption outside of Psi. + +```csharp +using (var p = Pipeline.Create()) +{ + var gen = Generators.Range(p, 0, 1000); + var sin = gen.Select(x => Math.Sin(x / 100.0)); + var mq = new NetMQWriter(p, "sin-topic", "tcp://localhost:12345", JsonFormat.Instance); + sin.PipeTo(mq); + p.Run(); +} +``` + +This component uses the [NetMQ Pub/Sub](https://netmq.readthedocs.io/en/latest/pub-sub/) pattern, in which messages convey _topic_ information (much like ROS). Subscribers may then subscribe by topic name. + +The generic `NetMQWriter` component has a single `In` receiver which sends to the topic specified at construction. This is the most common case. To facilitate multiple topics over one channel, the `AddTopic(...)` method may be called; returning an additional `IReceiver` to which to pipe. + +```csharp + var cos = gen.Select(x => Math.Cos(x / 100.0)); + var topic = mq.AddTopic("cos-topic"); + cos.PipeTo(topic); +``` + +Alternatively, the non-generic `NetMQWriter` may be used, which has _no_ receivers. Each must be created with `AddTopic(...)`. + +```csharp +using (var p = Pipeline.Create()) +{ + var gen = Generators.Range(p, 0, 1000); + var sin = gen.Select(x => Math.Sin(x / 100.0)); + var cos = gen.Select(x => Math.Cos(x / 100.0)); + + var mq = new NetMQWriter(p, "sin-topic", "tcp://localhost:12345", JsonFormat.Instance); + sin.PipeTo(mq.AddTopic("sin-topic"); + cos.PipeTo(mq.AddTopic("cos-topic"); + + p.Run(); +} +``` + +Then from Python, for example, messages may be consumed using `pyzmq` with something like: + +```python +import zmq, json + +socket = zmq.Context().socket(zmq.SUB) +socket.connect("tcp://localhost:12345") +socket.setsockopt(zmq.SUBSCRIBE, '') # '' means all topics, otherwise 'sin-topic'/'cos-topic' + +while True: + [topic, message] = socket.recv_multipart() + j = json.loads(message) + print "Message: ", repr(j['message']) + print "Originating Time: ", repr(j['originatingTime']) +``` + +#### `NetMQSource` + +Psi may also consume message that have been produced from "outside." For example, the below Python code produces an infinite stream of random doubles: + +```python +import zmq, random, datetime, json + +context = zmq.Context() +socket = context.socket(zmq.PUB) +socket.bind('tcp://127.0.0.1:45678') + +while True: + payload = {} + payload['message'] = random.uniform(0, 1) + payload['originatingTime'] = datetime.datetime.utcnow().isoformat() + socket.send_multipart(['test-topic'.encode(), json.dumps(payload).encode('utf-8')]) +``` + +Notice that the [Pub/Sub](https://netmq.readthedocs.io/en/latest/pub-sub/) pattern is expected here as well; using `socket.send_multipart` with a topic name and the JSON-encoded data. Also notice that the schema must match that expected by the `JsonFormat` `IFormatDeserializer` described above: + +```json +{ "message": , "originatingTime": bin\Release\net472\Microsoft.Psi.Windows.xml diff --git a/Sources/Runtime/Microsoft.Psi/Common/PsiStreamMetadata.cs b/Sources/Runtime/Microsoft.Psi/Common/PsiStreamMetadata.cs index ad981123d..9b2153e84 100644 --- a/Sources/Runtime/Microsoft.Psi/Common/PsiStreamMetadata.cs +++ b/Sources/Runtime/Microsoft.Psi/Common/PsiStreamMetadata.cs @@ -235,7 +235,7 @@ public T GetSupplementalMetadata(KnownSerializers serializers) /// public T GetSupplementalMetadata() { - return this.GetSupplementalMetadata(KnownSerializers.Default); + return this.GetSupplementalMetadata(KnownSerializers.GetKnownSerializers()); } /// diff --git a/Sources/Runtime/Microsoft.Psi/Common/TypeResolutionHelper.cs b/Sources/Runtime/Microsoft.Psi/Common/TypeResolutionHelper.cs index 9ef09538d..f241bb117 100644 --- a/Sources/Runtime/Microsoft.Psi/Common/TypeResolutionHelper.cs +++ b/Sources/Runtime/Microsoft.Psi/Common/TypeResolutionHelper.cs @@ -32,8 +32,8 @@ public static Type GetVerifiedType(string typeName, IReadOnlyDictionary this.writer.InitializeStreamOpenedTimes(e.StartOriginatingTime); diff --git a/Sources/Runtime/Microsoft.Psi/Data/Importer.cs b/Sources/Runtime/Microsoft.Psi/Data/Importer.cs index 5227ab767..5fa223604 100644 --- a/Sources/Runtime/Microsoft.Psi/Data/Importer.cs +++ b/Sources/Runtime/Microsoft.Psi/Data/Importer.cs @@ -69,7 +69,7 @@ public KnownSerializers Serializers return storeStreamReader.GetSerializers(); } - return KnownSerializers.Default; + return KnownSerializers.GetKnownSerializers(); } } diff --git a/Sources/Runtime/Microsoft.Psi/Data/PsiImporter.cs b/Sources/Runtime/Microsoft.Psi/Data/PsiImporter.cs index 33677089c..bd01d996f 100644 --- a/Sources/Runtime/Microsoft.Psi/Data/PsiImporter.cs +++ b/Sources/Runtime/Microsoft.Psi/Data/PsiImporter.cs @@ -4,7 +4,8 @@ namespace Microsoft.Psi.Data { using Microsoft.Psi; - + using Microsoft.Psi.Serialization; +#pragma warning disable CS0419 // Ambiguous reference in cref attribute /// /// Component that reads messages from a \psi store and publishes them on streams. /// @@ -14,6 +15,7 @@ namespace Microsoft.Psi.Data /// method. The store metadata is available immediately after open /// (before the pipeline is running) via the property. /// +#pragma warning restore CS0419 // Ambiguous reference in cref attribute public sealed class PsiImporter : Importer { /// @@ -27,5 +29,18 @@ public PsiImporter(Pipeline pipeline, string name, string path, bool usePerStrea : base(pipeline, new PsiStoreStreamReader(name, path), usePerStreamReaders) { } + + /// + /// Initializes a new instance of the class. + /// + /// The pipeline to add the component to. + /// Custom known serializers. + /// The name of the application that generated the persisted files, or the root name of the files. + /// The directory in which the main persisted file resides or will reside, or null to open a volatile data store. + /// Flag indicating whether to use per-stream readers. + public PsiImporter(Pipeline pipeline, KnownSerializers serializers, string name, string path, bool usePerStreamReaders) + : base(pipeline, new PsiStoreStreamReader(name, path, serializers), usePerStreamReaders) + { + } } } diff --git a/Sources/Runtime/Microsoft.Psi/Data/PsiStore.cs b/Sources/Runtime/Microsoft.Psi/Data/PsiStore.cs index 7f66af770..ea6146ac8 100644 --- a/Sources/Runtime/Microsoft.Psi/Data/PsiStore.cs +++ b/Sources/Runtime/Microsoft.Psi/Data/PsiStore.cs @@ -91,6 +91,29 @@ public static PsiImporter Open(Pipeline pipeline, string name, string rootPath, return new PsiImporter(pipeline, name, rootPath, usePerStreamReaders); } + /// + /// Opens a \psi store for read and returns a instance + /// which can be used to inspect the store and open the streams. + /// The store metadata is available immediately after this call (before the pipeline is running) via the property. + /// + /// The pipeline to add the component to. + /// Custom known serializers. + /// The name of the store to open (the same as the catalog file name). + /// + /// The path to the store. + /// This can be one of: + /// - a full path to a directory containing the store + /// - a root path containing one or more versions of the store, each in its own subdirectory, + /// in which case the latest store is opened. + /// - a null string, in which case an in-memory store is opened. + /// + /// Optional flag indicating whether to use per-stream readers (see remarks). + /// A instance that can be used to open streams and read messages. + public static PsiImporter Open(Pipeline pipeline, KnownSerializers serializers, string name, string rootPath, bool usePerStreamReaders = true) + { + return new PsiImporter(pipeline, serializers, name, rootPath, usePerStreamReaders); + } + /// /// Writes the specified stream to a multi-stream \psi store. /// diff --git a/Sources/Runtime/Microsoft.Psi/Data/PsiStoreStreamReader.cs b/Sources/Runtime/Microsoft.Psi/Data/PsiStoreStreamReader.cs index 882c909fb..27558a538 100644 --- a/Sources/Runtime/Microsoft.Psi/Data/PsiStoreStreamReader.cs +++ b/Sources/Runtime/Microsoft.Psi/Data/PsiStoreStreamReader.cs @@ -35,6 +35,18 @@ public PsiStoreStreamReader(string name, string path) this.PsiStoreReader = new PsiStoreReader(name, path, this.LoadMetadata); } + /// + /// Initializes a new instance of the class. + /// + /// The name of the application that generated the persisted files, or the root name of the files. + /// The directory in which the main persisted file resides or will reside, or null to create a volatile data store. + /// Custom known serializers. + public PsiStoreStreamReader(string name, string path, KnownSerializers serializers) + { + this.PsiStoreReader = new PsiStoreReader(name, path, this.LoadMetadata); + this.context = new SerializationContext(serializers); + } + /// /// Initializes a new instance of the class. /// diff --git a/Sources/Runtime/Microsoft.Psi/Executive/Pipeline.cs b/Sources/Runtime/Microsoft.Psi/Executive/Pipeline.cs index 5c75e1cb1..8f7abf8a3 100644 --- a/Sources/Runtime/Microsoft.Psi/Executive/Pipeline.cs +++ b/Sources/Runtime/Microsoft.Psi/Executive/Pipeline.cs @@ -4,6 +4,7 @@ namespace Microsoft.Psi { using System; + using System.Collections; using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics; @@ -62,8 +63,6 @@ public class Pipeline : IDisposable /// private ReplayDescriptor replayDescriptor; - private TimeInterval proposedOriginatingTimeInterval; - private State state; private bool enableExceptionHandling; @@ -252,6 +251,8 @@ private set internal Scheduler Scheduler => this.scheduler; + internal TimeInterval ProposedOriginatingTimeInterval { get; private set; } + internal SchedulerContext ActivationContext => this.activationContext; internal SchedulerContext SchedulerContext => this.schedulerContext; @@ -317,7 +318,7 @@ public virtual void ProposeReplayTime(TimeInterval originatingTimeInterval) throw new ArgumentException(nameof(originatingTimeInterval), "Replay time intervals must have a valid start time."); } - this.proposedOriginatingTimeInterval = (this.proposedOriginatingTimeInterval == null) ? originatingTimeInterval : TimeInterval.Coverage(new[] { this.proposedOriginatingTimeInterval, originatingTimeInterval }); + this.ProposedOriginatingTimeInterval = (this.ProposedOriginatingTimeInterval == null) ? originatingTimeInterval : TimeInterval.Coverage(new[] { this.ProposedOriginatingTimeInterval, originatingTimeInterval }); } /// @@ -900,6 +901,33 @@ internal virtual void Stop(DateTime finalOriginatingTime, bool abandonPendingWor this.completed.Set(); } + internal bool RemoveSubpipline(Subpipeline subpipeline) + { + PipelineElement node = this.components.FirstOrDefault(c => c.StateObject == subpipeline); + if (node == null) + { + return false; + } + + if (!subpipeline.IsCompleted) + { + throw new InvalidOperationException($"Subpipeline is still running, it can't be removed from parent pipeline."); + } + + List list = subpipeline.Components.ToList(); + list.Add(node); + SynchronizationLock locker = new SynchronizationLock(this, true); + this.scheduler.Freeze(locker); + this.components = new ConcurrentQueue(this.components.Where(x => !list.Contains(x))); + foreach (PipelineElement child in list) + { + this.DiagnosticsCollector?.PipelineElementDisposed(this, child); + } + + locker.Release(); + return true; + } + /// /// Run pipeline (asynchronously). /// @@ -911,7 +939,7 @@ protected virtual IDisposable RunAsync(ReplayDescriptor descriptor, Clock clock, { this.state = State.Starting; descriptor ??= ReplayDescriptor.ReplayAllRealTime; - this.replayDescriptor = descriptor.Intersect(this.proposedOriginatingTimeInterval); + this.replayDescriptor = descriptor.Intersect(this.ProposedOriginatingTimeInterval); this.completed.Reset(); if (clock == null) @@ -976,6 +1004,18 @@ protected virtual IDisposable RunAsync(ReplayDescriptor descriptor, Clock clock, return this; } + /// + /// Dispose components within pipeline and recursively within subpipelines. + /// + protected void DisposeComponents() + { + foreach (var component in this.components) + { + this.DiagnosticsCollector?.PipelineElementDisposed(this, component); + component.Dispose(); + } + } + /// /// Gather all nodes within this pipeline and recursively within subpipelines. /// @@ -1401,15 +1441,6 @@ private void NotifyPipelineFinalizing(DateTime finalOriginatingTime) } } - private void DisposeComponents() - { - foreach (var component in this.components) - { - this.DiagnosticsCollector?.PipelineElementDisposed(this, component); - component.Dispose(); - } - } - private void ThrowIfError() { if (this.PipelineExceptionNotHandled != null) diff --git a/Sources/Runtime/Microsoft.Psi/Executive/RebootableExtensions.cs b/Sources/Runtime/Microsoft.Psi/Executive/RebootableExtensions.cs new file mode 100644 index 000000000..b3b939e93 --- /dev/null +++ b/Sources/Runtime/Microsoft.Psi/Executive/RebootableExtensions.cs @@ -0,0 +1,35 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +// Extensions for activating the possibility of rebooting and modifing pipeline structure. +namespace Microsoft.Psi +{ + using System.Collections.Generic; + using System.Linq; + + /// + /// Class containing rebootable extensions for the executive subsystem. + /// + public static class RebootableExtensions + { + /// + /// Retrieve the specified components from the given type. + /// + /// The type of component to retrieve. + /// The pipeline to get elements from. + /// The list of the components found inside the pipeline. + public static List GetElementsOfType(this Pipeline pipeline) + { + List components = new List(); + foreach (var component in pipeline.Components) + { + if (component.StateObject is T element) + { + components.Add(element); + } + } + + return components; + } + } +} diff --git a/Sources/Runtime/Microsoft.Psi/Executive/Subpipeline.cs b/Sources/Runtime/Microsoft.Psi/Executive/Subpipeline.cs index aaa5c0a92..c78c863d7 100644 --- a/Sources/Runtime/Microsoft.Psi/Executive/Subpipeline.cs +++ b/Sources/Runtime/Microsoft.Psi/Executive/Subpipeline.cs @@ -4,6 +4,7 @@ namespace Microsoft.Psi { using System; + using System.Collections.Concurrent; using System.Linq; using Microsoft.Psi.Components; using Microsoft.Psi.Executive; @@ -102,6 +103,17 @@ public void Stop(DateTime finalOriginatingTime, Action notifyCompleted) } } + /// + /// Remove subpipline from parent. + /// + public override void Dispose() + { + this.Stop(this.parentPipeline.GetCurrentTime(), true); + this.DisposeComponents(); + this.parentPipeline.RemoveSubpipline(this); + this.SchedulerContext.Dispose(); + } + /// public override void ProposeReplayTime(TimeInterval originatingTimeInterval) { diff --git a/Sources/Runtime/Microsoft.Psi/Microsoft.Psi.csproj b/Sources/Runtime/Microsoft.Psi/Microsoft.Psi.csproj index de3a346ed..cf57890a5 100644 --- a/Sources/Runtime/Microsoft.Psi/Microsoft.Psi.csproj +++ b/Sources/Runtime/Microsoft.Psi/Microsoft.Psi.csproj @@ -5,6 +5,7 @@ true Microsoft.Psi.Runtime Provides the core APIs and components for Platform for Situated Intelligence. + $(SolutionDir)\Build\PsiPackages diff --git a/Sources/Runtime/Microsoft.Psi/Operators/Connectors.cs b/Sources/Runtime/Microsoft.Psi/Operators/Connectors.cs index 18cd44273..cb34190cf 100644 --- a/Sources/Runtime/Microsoft.Psi/Operators/Connectors.cs +++ b/Sources/Runtime/Microsoft.Psi/Operators/Connectors.cs @@ -52,7 +52,7 @@ public static IProducer BridgeTo(this IProducer input, Pipeline targetP else { var connector = new Connector(input.Out.Pipeline, targetPipeline, name ?? nameof(BridgeTo)); - return input.PipeTo(connector, deliveryPolicy); + return input.PipeTo(connector, true, deliveryPolicy); } } diff --git a/Sources/Runtime/Microsoft.Psi/Persistence/InfiniteFileReader.cs b/Sources/Runtime/Microsoft.Psi/Persistence/InfiniteFileReader.cs index c8d5ee7da..a2a58f6bc 100644 --- a/Sources/Runtime/Microsoft.Psi/Persistence/InfiniteFileReader.cs +++ b/Sources/Runtime/Microsoft.Psi/Persistence/InfiniteFileReader.cs @@ -28,9 +28,12 @@ public InfiniteFileReader(string path, string fileName, int fileId = 0) this.path = path; this.fileName = fileName; this.fileId = fileId; - Mutex pulse; - Mutex.TryOpenExisting(InfiniteFileWriter.PulseEventName(path, fileName), out pulse); - this.writePulse = pulse ?? new Mutex(false); + + // Mutex pulse; + // Mutex.TryOpenExisting(InfiniteFileWriter.PulseEventName(path, fileName), out pulse); + // this.writePulse = pulse ?? new Mutex(false); + InfiniteFileWriter.PulseEventName(path, fileName); + this.writePulse = new Mutex(false); } public InfiniteFileReader(string name, int fileId = 0) diff --git a/Sources/Runtime/Microsoft.Psi/Persistence/InfiniteFileWriter.cs b/Sources/Runtime/Microsoft.Psi/Persistence/InfiniteFileWriter.cs index 3d6d35668..ec3e03635 100644 --- a/Sources/Runtime/Microsoft.Psi/Persistence/InfiniteFileWriter.cs +++ b/Sources/Runtime/Microsoft.Psi/Persistence/InfiniteFileWriter.cs @@ -52,7 +52,8 @@ public InfiniteFileWriter(string path, string fileName, int extentSize) { try { - this.globalWritePulse = new Mutex(true, PulseEventName(path, fileName)); + // this.globalWritePulse = new Mutex(true, PulseEventName(path, fileName)); + this.globalWritePulse = new Mutex(true); } catch (UnauthorizedAccessException) { @@ -64,9 +65,20 @@ public InfiniteFileWriter(string path, string fileName, int extentSize) { while (!this.disposed) { - this.localWritePulse?.WaitOne(); - this.globalWritePulse?.ReleaseMutex(); - this.globalWritePulse?.WaitOne(); + if (this.localWritePulse != null) + { + this.localWritePulse.WaitOne(); + } + + if (this.globalWritePulse != null) + { + this.globalWritePulse.ReleaseMutex(); + } + + if (this.globalWritePulse != null) + { + this.globalWritePulse.WaitOne(); + } } } catch (ObjectDisposedException) @@ -109,7 +121,7 @@ public void Dispose() this.localWritePulse.Set(); this.localWritePulse.Dispose(); this.localWritePulse = null; - this.globalWritePulse?.Dispose(); + this.globalWritePulse.Dispose(); this.globalWritePulse = null; // may have already been disposed in CloseCurrent @@ -218,11 +230,16 @@ internal static string PulseEventName(string path, string fileName) private static string MakeHandleName(string format, string path, string fileName) { - var name = string.Format(format, path?.ToLower().GetDeterministicHashCode(), fileName.ToLower()); + if (path == null) + { + return default; + } + + var name = string.Format(format, path.ToLower().GetDeterministicHashCode(), fileName.ToLower()); if (name.Length > 260) { // exceeded the name length limit - return string.Format(format, path?.ToLower().GetDeterministicHashCode(), fileName.ToLower().GetDeterministicHashCode()); + return string.Format(format, path.ToLower().GetDeterministicHashCode(), fileName.ToLower().GetDeterministicHashCode()); } return name; @@ -239,14 +256,16 @@ private void CreateNewExtent() if (!this.IsVolatile) { this.extentName = System.IO.Path.Combine(this.Path, this.extentName); - var file = File.Open(this.extentName, FileMode.Create, FileAccess.ReadWrite, FileShare.Read); + + // var file = File.Open(this.extentName, FileMode.Create, FileAccess.ReadWrite, FileShare.Read); try { - newMMF = MemoryMappedFile.CreateFromFile(file, null, this.extentSize, MemoryMappedFileAccess.ReadWrite, HandleInheritability.Inheritable, false); + // newMMF = MemoryMappedFile.CreateFromFile(file, null, this.extentSize, MemoryMappedFileAccess.ReadWrite, HandleInheritability.Inheritable, false); + newMMF = MemoryMappedFile.CreateFromFile(this.extentName, FileMode.Create, null, this.extentSize); } catch (IOException) { - file.Dispose(); + // file.Dispose(); throw; } } diff --git a/Sources/Runtime/Microsoft.Psi/Remoting/RemoteExporter.cs b/Sources/Runtime/Microsoft.Psi/Remoting/RemoteExporter.cs index 12b1772f1..8742d5a7d 100644 --- a/Sources/Runtime/Microsoft.Psi/Remoting/RemoteExporter.cs +++ b/Sources/Runtime/Microsoft.Psi/Remoting/RemoteExporter.cs @@ -8,8 +8,10 @@ namespace Microsoft.Psi.Remoting using System.Collections.Generic; using System.Diagnostics; using System.IO; + using System.Linq.Expressions; using System.Net; using System.Net.Sockets; + using System.Numerics; using System.Threading; using Microsoft.Psi.Common; using Microsoft.Psi.Data; @@ -217,9 +219,9 @@ private void AcceptDataClientsBackground() { try { - this.dataTransport.StartListening(); while (!this.disposed) { + this.dataTransport.StartListening(); var client = this.dataTransport.AcceptClient(); var guid = Guid.Empty; try @@ -229,7 +231,7 @@ private void AcceptDataClientsBackground() if (this.connections.TryGetValue(guid, out Connection connection)) { - connection.JoinBackground(client); + connection.JoinBackgroundThread(client); } else { @@ -260,11 +262,13 @@ private sealed class Connection : IDisposable private readonly string storeName; private readonly string storePath; + private bool threadRunning; private TcpClient client; private Stream stream; private PsiStoreReader storeReader; private TimeInterval interval; + private Thread connectionThread; public Connection(TcpClient client, ITransport dataTransport, string name, string path, Action onDisconnect, Exporter exporter, long maxBytesPerSecond, double bytesPerSecondSmoothingWindowSeconds) { @@ -278,6 +282,7 @@ public Connection(TcpClient client, ITransport dataTransport, string name, strin this.exporter = exporter; this.maxBytesPerSecond = maxBytesPerSecond; this.bytesPerSecondSmoothingWindowSeconds = bytesPerSecondSmoothingWindowSeconds; + this.connectionThread = null; } public Guid Id => this.id; @@ -332,6 +337,13 @@ public void Connect() } } + public void JoinBackgroundThread(ITransportClient client) + { + this.threadRunning = true; + this.connectionThread = new Thread(() => this.JoinBackground(client)); + this.connectionThread.Start(); + } + public void JoinBackground(ITransportClient client) { double avgBytesPerSec = 0; @@ -345,59 +357,70 @@ public void JoinBackground(ITransportClient client) this.storeReader.Seek(this.interval); - while (true) + while (this.threadRunning) { - if (this.storeReader.MoveNext(out Envelope envelope)) + try { - var length = this.storeReader.Read(ref buffer); - this.exporter.Throttle.Reset(); - try + if (this.storeReader.MoveNext(out Envelope envelope)) { - client.WriteMessage(envelope, buffer); - if (lastTime > DateTime.MinValue /* at least second message */) + var length = this.storeReader.Read(ref buffer); + this.exporter.Throttle.Reset(); + try { - if (this.maxBytesPerSecond < long.MaxValue) + client.WriteMessage(envelope, buffer); + if (lastTime > DateTime.MinValue /* at least second message */) { - // throttle to arbitrary max BPS - var elapsed = (envelope.OriginatingTime - lastTime).TotalSeconds; - var bytesPerSec = (envelopeSize + length) / elapsed; - double smoothingFactor = 1.0 / (this.bytesPerSecondSmoothingWindowSeconds / elapsed); - avgBytesPerSec = (bytesPerSec * smoothingFactor) + (avgBytesPerSec * (1.0 - smoothingFactor)); - if (bytesPerSec > this.maxBytesPerSecond) + if (this.maxBytesPerSecond < long.MaxValue) { - var wait = (int)(((avgBytesPerSec / this.maxBytesPerSecond) - elapsed) * 1000.0); - if (wait > 0) + // throttle to arbitrary max BPS + var elapsed = (envelope.OriginatingTime - lastTime).TotalSeconds; + var bytesPerSec = (envelopeSize + length) / elapsed; + double smoothingFactor = 1.0 / (this.bytesPerSecondSmoothingWindowSeconds / elapsed); + avgBytesPerSec = (bytesPerSec * smoothingFactor) + (avgBytesPerSec * (1.0 - smoothingFactor)); + if (bytesPerSec > this.maxBytesPerSecond) { - Thread.Sleep(wait); + var wait = (int)(((avgBytesPerSec / this.maxBytesPerSecond) - elapsed) * 1000.0); + if (wait > 0) + { + Thread.Sleep(wait); + } } } } - } - lastTime = envelope.OriginatingTime; - } - finally - { - // writers continue upon failure - meanwhile, remote client may reconnect and resume based on replay interval - this.exporter.Throttle.Set(); + lastTime = envelope.OriginatingTime; + } + finally + { + // writers continue upon failure - meanwhile, remote client may reconnect and resume based on replay interval + this.exporter.Throttle.Set(); + } } } + catch (Exception) + { + this.threadRunning = false; + } } } public void Dispose() { + this.threadRunning = false; this.storeReader.Dispose(); this.storeReader = null; this.client.Dispose(); this.client = null; this.stream.Dispose(); this.stream = null; + this.connectionThread.Abort(); + this.connectionThread = null; } private void Disconnect() { this.onDisconnect(this.id); + this.connectionThread.Abort(); this.Dispose(); } diff --git a/Sources/Runtime/Microsoft.Psi/Remoting/RemoteImporter.cs b/Sources/Runtime/Microsoft.Psi/Remoting/RemoteImporter.cs index b452d693f..6d406a31e 100644 --- a/Sources/Runtime/Microsoft.Psi/Remoting/RemoteImporter.cs +++ b/Sources/Runtime/Microsoft.Psi/Remoting/RemoteImporter.cs @@ -72,6 +72,21 @@ public RemoteImporter(Pipeline pipeline, string host, int port = RemoteExporter. { } + /// + /// Initializes a new instance of the class. + /// + /// The pipeline to add the component to. + /// Path for PsiStore. + /// Remote host name. + /// TCP port on which to connect (default 11411). + /// Custom known serializers. + /// Whether to allow sequence ID restarts upon connection loss/reacquire. + /// In this case the start is a special behavior that is `DateTime.UtcNow` _at the sending `RemoteExporter`_. + public RemoteImporter(Pipeline pipeline, string storePath, string host, int port = RemoteExporter.DefaultPort, KnownSerializers serializers = null, bool allowSequenceRestart = true) + : this(name => PsiStore.Open(pipeline, serializers, name, storePath), new TimeInterval(DateTime.MinValue, DateTime.MaxValue), true, host, port, $"RemoteImporter_{Guid.NewGuid()}", storePath, allowSequenceRestart) + { + } + /// /// Initializes a new instance of the class. /// diff --git a/Sources/Runtime/Microsoft.Psi/Remoting/RemotePipelineClockExporter.cs b/Sources/Runtime/Microsoft.Psi/Remoting/RemotePipelineClockExporter.cs new file mode 100644 index 000000000..87e6a47df --- /dev/null +++ b/Sources/Runtime/Microsoft.Psi/Remoting/RemotePipelineClockExporter.cs @@ -0,0 +1,127 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +namespace Microsoft.Psi.Remoting +{ + using System; + using System.Diagnostics; + using System.IO; + using System.Net; + using System.Net.Sockets; + using System.Threading; + + /// + /// Component that exports pipeline clock information over TCP to enable synchronization. + /// + public class RemotePipelineClockExporter : IDisposable + { + /// + /// Default TCP port used to communicate with . + /// + public const int DefaultPort = 11511; + + internal const short ProtocolVersion = 0; + + private TcpListener listener; + private bool isDisposing; + private Pipeline pipeline; + private TimeInterval interval; + + /// + /// Initializes a new instance of the class. + /// + /// The pipeline sync master. + /// The connection port. + /// Optional for specific time interval. + public RemotePipelineClockExporter(Pipeline pipeline, int port = DefaultPort, TimeInterval interval = null) + { + this.pipeline = pipeline; + this.Port = port; + this.interval = interval ?? TimeInterval.Infinite; + this.listener = new TcpListener(IPAddress.Any, port); + this.Start(); + } + + /// + /// Gets the connection port. + /// + public int Port { get; } + + /// + public void Dispose() + { + this.isDisposing = true; + this.listener.Stop(); + this.listener = null; + } + + private void Start() + { + new Thread(new ThreadStart(this.Listen)) { IsBackground = true }.Start(); + } + + private void Listen() + { + if (this.listener != null) + { + NetworkStream networkStream = null; + try + { + this.listener.Start(); + networkStream = this.listener.AcceptTcpClient().GetStream(); + + // check protocol version + using var reader = new BinaryReader(networkStream); + var version = reader.ReadInt16(); + if (version != ProtocolVersion) + { + throw new IOException($"Unsupported remote pipeline clock protocol version: {version}"); + } + + using var writer = new BinaryWriter(networkStream); + + // current pipeline time, used by client to sync clocks + if (this.pipeline.IsRunning || this.pipeline.ReplayDescriptor.Start != DateTime.MinValue) + { + writer.Write(0); + if (this.pipeline.IsRunning) + { + writer.Write(this.pipeline.GetCurrentTime().Ticks); + } + else + { + writer.Write(this.pipeline.ReplayDescriptor.Start.Ticks); + } + } + else if (this.pipeline.IsInitial || this.pipeline.IsStarting) + { + writer.Write(1); + writer.Write(Math.Max(this.pipeline.ProposedOriginatingTimeInterval.Left.Ticks, this.interval.Left.Ticks)); + } + else + { + writer.Write(2); + writer.Write(DateTime.UtcNow.Ticks); + } + + // name of the pipeline + writer.Write(this.pipeline.Name); + writer.Flush(); + } + catch (Exception ex) + { + Trace.WriteLine($"{nameof(RemotePipelineClockExporter)} Exception: {ex.Message}"); + } + finally + { + networkStream?.Dispose(); + if (!this.isDisposing) + { + this.listener.Stop(); + this.Start(); + } + } + } + } + } +} diff --git a/Sources/Runtime/Microsoft.Psi/Remoting/RemotePipelineClockImporter.cs b/Sources/Runtime/Microsoft.Psi/Remoting/RemotePipelineClockImporter.cs new file mode 100644 index 000000000..dd459a2ee --- /dev/null +++ b/Sources/Runtime/Microsoft.Psi/Remoting/RemotePipelineClockImporter.cs @@ -0,0 +1,137 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +namespace Microsoft.Psi.Remoting +{ + using System; + using System.Diagnostics; + using System.IO; + using System.Net.Sockets; + using System.Threading; + using Microsoft.Psi; + + /// + /// Component that reads remote clock information over TCP and synchronizes the local pipeline clock. + /// + public class RemotePipelineClockImporter : IDisposable + { + private readonly Pipeline pipeline; + private readonly string name; + private readonly string host; + private readonly int port; + private readonly TcpClient client; + private readonly EventWaitHandle connected = new (false, EventResetMode.ManualReset); + + /// + /// Initializes a new instance of the class. + /// + /// The pipeline to add the component to. + /// The host name of the remote clock exporter/server. + /// The port on which to connect. + /// An optional name for the component. + public RemotePipelineClockImporter(Pipeline pipeline, string host, int port = RemotePipelineClockExporter.DefaultPort, string name = nameof(RemotePipelineClockImporter)) + { + this.pipeline = pipeline; + this.name = name; + this.client = new TcpClient(); + this.host = host; + this.port = port; + this.connected.Reset(); + new Thread(new ThreadStart(this.SynchronizeLocalPipelineClock)) { IsBackground = true }.Start(); + } + + /// + /// Gets wait handle for remote connection being established. + /// + /// This should be waited on prior to running the pipeline. + public EventWaitHandle Connected + { + get { return this.connected; } + } + + /// + /// Gets or sets machine with which to synchronize pipeline clock. + /// + internal static string PrimaryClockSourceMachineName { get; set; } = string.Empty; + + /// + public void Dispose() + { + this.client.Close(); + this.connected.Dispose(); + } + + /// + public override string ToString() => this.name; + + private void SynchronizeLocalPipelineClock() + { + var completed = false; + while (!completed) + { + NetworkStream networkStream = null; + try + { + Trace.WriteLine($"Attempting to connect to {this.host} on port {this.port} ..."); + this.client.Connect(this.host, this.port); + networkStream = this.client.GetStream(); + Trace.WriteLine($"Connected to {this.host} on port {this.port}."); + + // send protocol version + using var writer = new BinaryWriter(networkStream); + var stopwatch = new Stopwatch(); + stopwatch.Start(); + writer.Write(RemotePipelineClockExporter.ProtocolVersion); + + using var reader = new BinaryReader(networkStream); + int state = reader.ReadInt32(); + var timeAtExporter = reader.ReadInt64(); + stopwatch.Stop(); + var timeAtImporter = DateTime.UtcNow.Ticks; + var elapsedTime = stopwatch.ElapsedTicks; + var pipelineName = reader.ReadString(); + var timeOffset = TimeSpan.Zero; + + if (pipelineName != this.pipeline.Name) + { + switch (state) + { + case 2: + case 0: + // Elapsed time includes the complete round trip latency between writing the header and receiving the + // remote (exporter) machine's time. We assume that half of the time was from here to the exporter, meaning + // that subtracting elapsed / 2 from our current time gives the time as it was on our clock when the exporter + // sent it's time. The difference becomes an offset to apply to our pipeline clock to synchronize. + timeOffset = TimeSpan.FromTicks(timeAtExporter - (timeAtImporter - (elapsedTime / 2))); + Trace.WriteLine($"{nameof(RemotePipelineClockImporter)} clock sync: Local={timeAtImporter} Remote pipeline[{pipelineName}]={timeAtExporter} Latency={elapsedTime} Offset={timeOffset.Ticks}."); + break; + case 1: + this.pipeline.ProposeReplayTime(new TimeInterval(new DateTime(timeAtExporter), DateTime.MaxValue)); + timeOffset = TimeSpan.FromTicks(elapsedTime / 2); + break; + } + } + else + { + // The "remote" pipeline is actually *this* pipeline. In this case, assume exactly zero offset. + Trace.WriteLine($"{nameof(RemotePipelineClockImporter)} clock sync with self ignored ({pipelineName}). Pipeline clock will remain unchanged."); + } + + // synchronize pipeline clock + this.pipeline.VirtualTimeOffset = timeOffset; + this.connected.Set(); + completed = true; + } + catch (Exception ex) + { + Trace.WriteLine($"{nameof(RemotePipelineClockImporter)} Exception: {ex.Message}"); + } + finally + { + networkStream?.Dispose(); + this.client.Close(); + } + } + } + } +} diff --git a/Sources/Runtime/Microsoft.Psi/Serialization/KnownSerializers.cs b/Sources/Runtime/Microsoft.Psi/Serialization/KnownSerializers.cs index a0cc6ea5e..86a9148b6 100644 --- a/Sources/Runtime/Microsoft.Psi/Serialization/KnownSerializers.cs +++ b/Sources/Runtime/Microsoft.Psi/Serialization/KnownSerializers.cs @@ -15,7 +15,7 @@ namespace Microsoft.Psi.Serialization /// /// Represents the registry of all serializers. - /// The contains system-wide serializers for the current version of the type system. + /// The contains system-wide serializers for the current version of the type system. /// Serializers explicitly registered with this instance are used by all other instances unless an override is specified. /// When deserializing from a persisted file, the instance returned by /// the will create its own KnownSerializer instance, and register serializers @@ -65,14 +65,14 @@ public class KnownSerializers // - there can be at most one schema for a type and at most one type for a schema, // and they must have matching IDs (implicitly or because of a user-specified name mapping) + // the set of types we don't know how to serialize + private static readonly HashSet UnserializableTypes = new (); + /// /// The default set of types and serializer creation rules globally known to the serialization subsystem. /// Custom serializers can be added directly to this set. /// - public static readonly KnownSerializers Default; - - // the set of types we don't know how to serialize - private static readonly HashSet UnserializableTypes = new (); + private static KnownSerializers defaultKnownSerializers; // mapping from fully-qualified .NET type names to synonyms private readonly Dictionary typeNameSynonyms = new (); @@ -131,7 +131,7 @@ static KnownSerializers() UnserializableTypes.Add(typeof(UIntPtr)); UnserializableTypes.Add(typeof(MemberInfo)); UnserializableTypes.Add(typeof(System.Diagnostics.StackTrace)); - Default = new KnownSerializers(true, RuntimeInfo.Latest); + defaultKnownSerializers = new KnownSerializers(true, RuntimeInfo.Latest); } /// @@ -165,22 +165,36 @@ private KnownSerializers(bool isDefault, RuntimeInfo runtimeInfo) this.handlersByType = new Dictionary(); this.index = new Dictionary(); - this.templates = new Dictionary(); - this.serializers = new Dictionary(); - this.knownTypes = new ConcurrentDictionary(); - this.knownNames = new ConcurrentDictionary(); - this.schemas = new ConcurrentDictionary(); - this.schemasById = new ConcurrentDictionary(); - this.cloningFlags = new ConcurrentDictionary(); - - // register non-generic, custom serializers - this.Register(); - this.Register(); - this.Register(); - this.Register(); - this.Register(); - this.RegisterGenericSerializer(typeof(EnumerableSerializer<>)); - this.RegisterGenericSerializer(typeof(DictionarySerializer<,>)); + if (isDefault) + { + this.templates = new Dictionary(); + this.serializers = new Dictionary(); + this.knownTypes = new ConcurrentDictionary(); + this.knownNames = new ConcurrentDictionary(); + this.schemas = new ConcurrentDictionary(); + this.schemasById = new ConcurrentDictionary(); + this.cloningFlags = new ConcurrentDictionary(); + + // register non-generic, custom serializers + this.Register(); + this.Register(); + this.Register(); + this.Register(); + this.Register(); + this.RegisterGenericSerializer(typeof(EnumerableSerializer<>)); + this.RegisterGenericSerializer(typeof(DictionarySerializer<,>)); + } + else + { + // all other instances start off with the Default rules + this.templates = new Dictionary(defaultKnownSerializers.templates); + this.serializers = new Dictionary(defaultKnownSerializers.serializers); + this.knownTypes = new ConcurrentDictionary(defaultKnownSerializers.knownTypes); + this.knownNames = new ConcurrentDictionary(defaultKnownSerializers.knownNames); + this.schemas = new ConcurrentDictionary(defaultKnownSerializers.schemas); + this.schemasById = new ConcurrentDictionary(defaultKnownSerializers.schemasById); + this.cloningFlags = new ConcurrentDictionary(defaultKnownSerializers.cloningFlags); + } } /// @@ -203,6 +217,15 @@ private KnownSerializers(bool isDefault, RuntimeInfo runtimeInfo) /// public IDictionary TypeNameSynonyms => this.typeNameSynonyms; + /// + /// Initializes a new instance of the class. + /// + /// static class. + public static KnownSerializers GetKnownSerializers() + { + return defaultKnownSerializers; + } + /// /// Registers type T with the specified contract name. /// Use this overload to deserialize data persisted before a type name change. diff --git a/Sources/Runtime/Microsoft.Psi/Serialization/SerializationContext.cs b/Sources/Runtime/Microsoft.Psi/Serialization/SerializationContext.cs index becb73803..e9b92fc47 100644 --- a/Sources/Runtime/Microsoft.Psi/Serialization/SerializationContext.cs +++ b/Sources/Runtime/Microsoft.Psi/Serialization/SerializationContext.cs @@ -27,7 +27,7 @@ public class SerializationContext /// This will become internal. Use Serializer.Schema instead. /// public SerializationContext() - : this(KnownSerializers.Default) + : this(KnownSerializers.GetKnownSerializers()) { } @@ -37,7 +37,7 @@ public SerializationContext() /// The set of custom serializers to use instead of the default ones. public SerializationContext(KnownSerializers serializers) { - this.serializers = serializers ?? KnownSerializers.Default; + this.serializers = serializers ?? KnownSerializers.GetKnownSerializers(); } internal KnownSerializers Serializers => this.serializers; diff --git a/Sources/Runtime/Microsoft.Psi/Serialization/Serializer.cs b/Sources/Runtime/Microsoft.Psi/Serialization/Serializer.cs index faf81af7d..29b6df56b 100644 --- a/Sources/Runtime/Microsoft.Psi/Serialization/Serializer.cs +++ b/Sources/Runtime/Microsoft.Psi/Serialization/Serializer.cs @@ -142,7 +142,7 @@ public static void Clear(ref T target, SerializationContext context) /// True if the type is immutable. public static bool IsImmutableType() { - return KnownSerializers.Default.GetHandler().IsImmutableType; + return KnownSerializers.GetKnownSerializers().GetHandler().IsImmutableType; } [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/Sources/Runtime/Microsoft.Psi/Serialization/StructHandler.cs b/Sources/Runtime/Microsoft.Psi/Serialization/StructHandler.cs index b262eca8b..cd8674a43 100644 --- a/Sources/Runtime/Microsoft.Psi/Serialization/StructHandler.cs +++ b/Sources/Runtime/Microsoft.Psi/Serialization/StructHandler.cs @@ -15,7 +15,7 @@ namespace Microsoft.Psi.Serialization /// The type of objects the handler understands. internal sealed class StructHandler : SerializationHandler { - private static readonly CloneDelegate CopyToBox = Generator.GenerateCloneMethod(il => Generator.EmitCopyToBox(typeof(T), il)); + // private static readonly CloneDelegate CopyToBox = Generator.GenerateCloneMethod(il => Generator.EmitCopyToBox(typeof(T), il)); // the inner serializer and serializerEx private readonly ISerializer innerSerializer; @@ -71,7 +71,9 @@ internal override void UntypedDeserialize(BufferReader reader, ref object target context.AddDeserializedObject(target); this.innerSerializer.Deserialize(reader, ref typedTarget, context); - CopyToBox(typedTarget, ref target, context); + + // CopyToBox(typedTarget, ref target, context); + target = (object)typedTarget; } internal override void UntypedClone(object instance, ref object target, SerializationContext context) @@ -88,7 +90,9 @@ internal override void UntypedClone(object instance, ref object target, Serializ context.AddDeserializedObject(target); this.innerSerializer.Clone((T)instance, ref typedTarget, context); - CopyToBox(typedTarget, ref target, context); + + // CopyToBox(typedTarget, ref target, context); + target = (object)typedTarget; } internal override void UntypedClear(ref object target, SerializationContext context) @@ -105,7 +109,9 @@ internal override void UntypedClear(ref object target, SerializationContext cont context.AddDeserializedObject(target); this.innerSerializer.Clear(ref typedTarget, context); - CopyToBox(typedTarget, ref target, context); + + // CopyToBox(typedTarget, ref target, context); + target = (object)typedTarget; } } } \ No newline at end of file diff --git a/Sources/Runtime/Microsoft.Psi/Streams/Receiver{T}.cs b/Sources/Runtime/Microsoft.Psi/Streams/Receiver{T}.cs index 2ae7922c1..6990acc5f 100644 --- a/Sources/Runtime/Microsoft.Psi/Streams/Receiver{T}.cs +++ b/Sources/Runtime/Microsoft.Psi/Streams/Receiver{T}.cs @@ -422,7 +422,7 @@ private int ComputeDataSize(T data) { if (this.computeDataSize == null) { - var serializers = KnownSerializers.Default; + var serializers = KnownSerializers.GetKnownSerializers(); var context = new SerializationContext(serializers); var handler = serializers.GetHandler(); var writer = new BufferWriter(16); diff --git a/Sources/Spatial/Microsoft.Psi.Spatial.Euclidean.Visualization.Windows/Microsoft.Psi.Spatial.Euclidean.Visualization.Windows.csproj b/Sources/Spatial/Microsoft.Psi.Spatial.Euclidean.Visualization.Windows/Microsoft.Psi.Spatial.Euclidean.Visualization.Windows.csproj index 215bf3465..c75e92668 100644 --- a/Sources/Spatial/Microsoft.Psi.Spatial.Euclidean.Visualization.Windows/Microsoft.Psi.Spatial.Euclidean.Visualization.Windows.csproj +++ b/Sources/Spatial/Microsoft.Psi.Spatial.Euclidean.Visualization.Windows/Microsoft.Psi.Spatial.Euclidean.Visualization.Windows.csproj @@ -5,6 +5,7 @@ Microsoft.Psi.Spatial.Euclidean.Visualization Provides visualization objects and adapters for various types defined in Microsoft.Psi.Spatial.Euclidean. True + $(SolutionDir)\Build\PsiPackages diff --git a/Sources/Spatial/Microsoft.Psi.Spatial.Euclidean/Microsoft.Psi.Spatial.Euclidean.csproj b/Sources/Spatial/Microsoft.Psi.Spatial.Euclidean/Microsoft.Psi.Spatial.Euclidean.csproj index 3a3a95de0..a228a0dfc 100644 --- a/Sources/Spatial/Microsoft.Psi.Spatial.Euclidean/Microsoft.Psi.Spatial.Euclidean.csproj +++ b/Sources/Spatial/Microsoft.Psi.Spatial.Euclidean/Microsoft.Psi.Spatial.Euclidean.csproj @@ -4,6 +4,7 @@ netstandard2.0 Provides types and methods for representing various 3D shapes and spatial operations. This project extends what is already provided in MathNet.Spatial.Euclidean. true + $(SolutionDir)\Build\PsiPackages diff --git a/Sources/Speech/Microsoft.Psi.Speech.Windows/Microsoft.Psi.Speech.Windows.csproj b/Sources/Speech/Microsoft.Psi.Speech.Windows/Microsoft.Psi.Speech.Windows.csproj index b9dcac8d3..6a1879be6 100644 --- a/Sources/Speech/Microsoft.Psi.Speech.Windows/Microsoft.Psi.Speech.Windows.csproj +++ b/Sources/Speech/Microsoft.Psi.Speech.Windows/Microsoft.Psi.Speech.Windows.csproj @@ -4,6 +4,7 @@ true Microsoft.Psi.Speech Provides Windows-specific components for speech processing. + $(SolutionDir)\Build\PsiPackages bin\Release\net472\Microsoft.Psi.Speech.Windows.xml diff --git a/Sources/Speech/Microsoft.Psi.Speech/Microsoft.Psi.Speech.csproj b/Sources/Speech/Microsoft.Psi.Speech/Microsoft.Psi.Speech.csproj index a8cefc179..f70538dbe 100644 --- a/Sources/Speech/Microsoft.Psi.Speech/Microsoft.Psi.Speech.csproj +++ b/Sources/Speech/Microsoft.Psi.Speech/Microsoft.Psi.Speech.csproj @@ -1,9 +1,10 @@ - + netstandard2.0 Provides data structures, APIs and components for speech processing. true + $(SolutionDir)\Build\PsiPackages diff --git a/Sources/Visualization/Microsoft.Msagl.WpfGraphControl/Microsoft.Msagl.WpfGraphControl.csproj b/Sources/Visualization/Microsoft.Msagl.WpfGraphControl/Microsoft.Msagl.WpfGraphControl.csproj index aa72588d8..0bbcdb1a9 100644 --- a/Sources/Visualization/Microsoft.Msagl.WpfGraphControl/Microsoft.Msagl.WpfGraphControl.csproj +++ b/Sources/Visualization/Microsoft.Msagl.WpfGraphControl/Microsoft.Msagl.WpfGraphControl.csproj @@ -1,9 +1,10 @@  net472 - false + True Microsoft.Msagl.WpfGraphControl WPF graph control for MSAGL. + $(SolutionDir)\Build\PsiPackages bin\Release\Microsoft.Msagl.WpfGraphControl.xml diff --git a/Sources/Visualization/Microsoft.Psi.LiveCharts.Visualization.Windows/Microsoft.Psi.LiveCharts.Visualization.Windows.csproj b/Sources/Visualization/Microsoft.Psi.LiveCharts.Visualization.Windows/Microsoft.Psi.LiveCharts.Visualization.Windows.csproj index cab6ff50e..54cbc446c 100644 --- a/Sources/Visualization/Microsoft.Psi.LiveCharts.Visualization.Windows/Microsoft.Psi.LiveCharts.Visualization.Windows.csproj +++ b/Sources/Visualization/Microsoft.Psi.LiveCharts.Visualization.Windows/Microsoft.Psi.LiveCharts.Visualization.Windows.csproj @@ -1,9 +1,10 @@  net472 - false + True Microsoft.Psi.LiveCharts.Visualization Provides visualization objects and APIs for using LiveCharts with Platform for Situated Intelligence Studio. + $(SolutionDir)\Build\PsiPackages bin\Release\net472\Microsoft.Psi.LiveCharts.Visualization.Windows.xml diff --git a/Sources/Visualization/Microsoft.Psi.Visualization.Windows/Microsoft.Psi.Visualization.Windows.csproj b/Sources/Visualization/Microsoft.Psi.Visualization.Windows/Microsoft.Psi.Visualization.Windows.csproj index 3f283de20..0d7490307 100644 --- a/Sources/Visualization/Microsoft.Psi.Visualization.Windows/Microsoft.Psi.Visualization.Windows.csproj +++ b/Sources/Visualization/Microsoft.Psi.Visualization.Windows/Microsoft.Psi.Visualization.Windows.csproj @@ -1,9 +1,10 @@  net472 - false + True Microsoft.Psi.Visualization Provides base data structures for visualization APIs for Platform for Situated Intelligence. + $(SolutionDir)\Build\PsiPackages bin\Release\net472\Microsoft.Psi.Visualization.Windows.xml