Compare commits

..

152 Commits

Author SHA1 Message Date
Andrew Welker
6ed7c96ec7 fix: centralize debug printing into extension methods
Stream debugging now uses CrestronConsole instead of debug methods, so that the debug statements will be printed regardless of console debug level. This also means that comm debug statements will NOT be in the Crestron Error log or in the files created by the logging system
2025-11-03 10:53:21 -06:00
Andrew Welker
666be5ae59 Merge pull request #1345 from PepperDash/mc-touchpanel-key
mc touchpanel key
2025-10-30 17:11:15 -04:00
Andrew Welker
bfc9b7e7fa fix: logging & ternary changes 2025-10-30 16:07:26 -05:00
Andrew Welker
b02e952765 Merge remote-tracking branch 'origin/com102-comspec' into mc-touchpanel-key 2025-10-30 15:58:36 -05:00
Andrew Welker
72861a5097 Merge remote-tracking branch 'origin/feature/fusion-help-request' into mc-touchpanel-key 2025-10-30 15:58:26 -05:00
Andrew Welker
44ed067f4d chore: update local build version 2025-10-30 15:57:37 -05:00
Andrew Welker
faa2169baf fix: send correct URL to panel 2025-10-30 15:57:28 -05:00
Andrew Welker
fd40b0c6d1 fix: send all status messages with ClientId 2025-10-30 15:57:15 -05:00
Andrew Welker
afcddad1cc fix: remove async on task, as it's unnecessary 2025-10-30 15:56:50 -05:00
Andrew Welker
c4cf8f13e9 fix: register panel in post phase rather than activation cycle 2025-10-30 15:56:28 -05:00
Neil Dorin
c852f87a27 refactor: Comment out logging statements in help request handling 2025-10-30 14:33:58 -06:00
Neil Dorin
071174fa7d feat: Implement help request status tracking in Fusion system 2025-10-30 14:13:02 -06:00
Neil Dorin
da0f28a10c feat: Enhance IEssentialsRoomFusionController with additional properties and logging 2025-10-29 16:47:18 -06:00
Andrew Welker
0538a304ed Merge branch 'main' into mc-touchpanel-key 2025-10-29 15:13:53 -05:00
Andrew Welker
705c5db237 Merge pull request #1347 from PepperDash/json-console
fix: print json response to console using \r\n instead of \n
2025-10-29 16:12:49 -04:00
Neil Dorin
2e95f5337e feat: Add IEssentialsRoomFusionController and related configurations
- Introduced IEssentialsRoomFusionControllerFactory for creating Fusion Room Controller devices.
- Added IEssentialsRoomFusionControllerPropertiesConfig to define configuration properties for the Fusion Room Controller.
- Updated IFusionHelpRequest interface to include methods for cancelling and toggling help requests.
- Refactored RoomOnToDefaultSourceWhenOccupied to use IEssentialsRoomFusionController instead of EssentialsHuddleSpaceFusionSystemControllerBase.
- Modified EssentialsRoomBase to check for IEssentialsRoomFusionController in occupancy status provider.
2025-10-28 16:49:29 -06:00
jkdevito
ba576180a7 refactor: remove unused using directives in ComPortController 2025-10-28 09:05:56 -05:00
jkdevito
92c9db8237 refactor: improve logging messages in ComPort registration and configuration 2025-10-28 09:04:15 -05:00
jkdevito
c4d064965f refactor: enhance ComPortController with additional event documentation and logging improvements 2025-10-28 08:40:29 -05:00
Neil Dorin
f27965ac29 feat: Add help request functionality to Fusion system controller
Introduce `IFusionHelpRequest` interface for managing help requests, including `HelpRequestResponseFeedback` property and `SendHelpRequest` method. Enhance `EssentialsHuddleSpaceFusionSystemControllerBase` with new properties and methods to support help request tracking and management. Improve code organization and documentation throughout the affected files.
2025-10-27 17:35:38 -06:00
jkdevito
3ce282e2db refactor: comment out debug logging for ComPort registration 2025-10-27 17:33:06 -05:00
jkdevito
32a332a6e2 refactor: update TODO comment for Port registration verification 2025-10-27 17:32:40 -05:00
jkdevito
151a90c9be refactor: streamline ComPort registration and configuration logic with enhanced logging 2025-10-27 17:13:43 -05:00
jkdevito
89e893b3b7 fix: improve formatting and logging in ComPortController 2025-10-27 16:46:09 -05:00
jkdevito
d01eb03890 feat: enhance ComPortController with detailed logging on configuration changes 2025-10-27 14:06:20 -05:00
Andrew Welker
16c92afabb fix: print json response to console using \r\n instead of \n 2025-10-27 09:21:51 -05:00
Andrew Welker
dff5d2d32e Merge pull request #1346 from PepperDash/feature/add-infinetEx-comm-method 2025-10-23 18:50:06 -04:00
Neil Dorin
317bde3814 fix: add InfinetEx to eControlMethods enum 2025-10-23 15:22:46 -06:00
Andrew Welker
8bab3dc966 fix: send touchpanelKey message with all room combiner checks 2025-10-23 11:21:17 -05:00
Andrew Welker
514ac850ca fix: send touchpanel key correctly 2025-10-23 10:01:05 -05:00
Andrew Welker
44432f7a41 fix: send touchpanel key to client when client joins direct server 2025-10-23 09:54:03 -05:00
Andrew Welker
99253b30c2 fix: send touchpanel key to client when client joins 2025-10-23 09:49:45 -05:00
Andrew Welker
bf248fe33e Merge pull request #1343 from PepperDash/device-interface-system
device interface system
2025-10-23 10:40:56 -04:00
cdenig
2f44040e4f Merge pull request #1344 from PepperDash/feature/allow-config-tool-v2-structure
Feature/allow config tool v2 structure
2025-10-23 10:15:03 -04:00
Neil Dorin
10399a1be8 Update src/PepperDash.Core/Config/PortalConfigReader.cs
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-10-22 13:54:08 -06:00
Neil Dorin
5409db193c Update src/PepperDash.Essentials.Core/Config/Essentials/EssentialsConfig.cs
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-10-22 13:53:23 -06:00
Neil Dorin
f1ce54a524 Merge branch 'main' into feature/allow-config-tool-v2-structure 2025-10-22 13:48:22 -06:00
Andrew Welker
7c72a0d905 fix: send device interface list on client join
In order to avoid updating the MC Edge API to send device interfaces, one of the responses to the clientJoined message will now be a message with the type `/system/deviceInterfaces`. This has a corresponding handler in the core library to put the interfaces in the correct location.
2025-10-21 18:19:09 -05:00
Andrew Welker
5d5e78629e chore: update local build version to 2.18.2-local 2025-10-21 18:17:25 -05:00
Neil Dorin
dab5484d6e Merge pull request #1342 from PepperDash/wsdebug-persistence
Unique Client IDs
2025-10-15 15:16:46 -04:00
Andrew Welker
5c35a3be45 fix: catch exceptions in handlers directly
Previously, any exceptions that were occuring in a hander's action were being swalled due to being off on another thread. Now, those exceptions are caught and printed out.
2025-10-15 14:03:17 -05:00
Andrew Welker
6cb98e12fa fix: use correct collection for program stop 2025-10-15 14:02:06 -05:00
Andrew Welker
608601990b docs: fix Copilot comments 2025-10-15 12:54:14 -05:00
Andrew Welker
3e0f318f7f fix: update log methods to all be consistent 2025-10-15 12:36:42 -05:00
Andrew Welker
98d0cc8fdc docs: add missing XML comments for Mobile Control Project 2025-10-15 12:26:57 -05:00
Andrew Welker
c557c6cdd6 fix: mobileinfo & CWS info call report the correct data 2025-10-15 11:49:06 -05:00
Andrew Welker
8525134ae7 fix: direct server clients now have unique client IDs
Using the generated security token as an ID was presenting problems with duplicate connections using the same ID and trying to figure out where to send the messages. Now, the clients have a unique ID that's an increasing integer that restarts at 1 when the program restarts.
2025-10-15 09:50:12 -05:00
Andrew Welker
1197b15a33 Merge pull request #1340 from PepperDash/display-bridging
Display bridging, Docs, Appdebug
2025-10-10 10:21:35 -04:00
Andrew Welker
ea6a7568fc Merge remote-tracking branch 'origin/feature/add-IKeyed-to-interfaces' into display-bridging 2025-10-09 16:08:17 -05:00
Andrew Welker
3a2a059ce1 fix: appdebug now has option for setting all sink levels 2025-10-09 16:07:25 -05:00
Andrew Welker
f9d9df9d5c docs: XML comments for Devices.Common 2025-10-09 13:18:36 -05:00
Neil Dorin
c284c4275f feat: Enhance UDPServer initialization in Connect method
Updated the `Connect` method in `GenericUdpServer.cs` to include error handling for hostname parsing. The method now attempts to create a `UDPServer` instance with specified parameters and falls back to default initialization if an error occurs. This improves flexibility and robustness in server setup.
2025-10-09 11:31:38 -06:00
Neil Dorin
0418f8a7cc fix: Fix typos and enhance item selection handling
Corrected parameter name in GenericUdpServer constructor.
Added new action for item selection in ISelectableItemsMessenger
with error handling for missing or invalid keys.
Updated SetItems method to improve clarity and ensure proper
clearing and re-adding of item actions.
2025-10-09 09:40:46 -06:00
Andrew Welker
a5d409e93a fix: analog input check uese correct comparison
input selection coming from SIMPL is 1-based, not 0-based. Comparison to input port array length needs to be <= instead of <
2025-10-09 08:36:26 -05:00
Andrew Welker
59944c0e2f refactor: split up classes to separate files 2025-10-09 08:33:22 -05:00
Neil Dorin
419177ccd5 fix: Add item management and update handling in messenger
Introduces a new private field `_itemKeys` to store item keys. Adds the `SetItems` method to manage item actions and update events, ensuring proper registration and cleanup. The `SendFullStatus` method is now invoked from a dedicated event handler `LocalItem_ItemUpdated`, improving the handling of item updates.
2025-10-08 12:24:17 -06:00
Neil Dorin
bd01e2bacc fix: Add KeyName class and update camera messaging
This commit introduces a new `KeyName` class implementing the `IKeyName` interface, enhancing the representation of camera data. The `CameraController_CameraSelected` and `SendFullStatus` methods are updated to utilize `KeyName` instances for selected and listed cameras, improving data encapsulation and consistency in the `IHasCamerasWithControlsStateMessage`. Additionally, new using directives for logging and core functionalities are added.
2025-10-07 17:10:11 -06:00
Neil Dorin
2928c5cf94 feat: Enhance camera capabilities and messaging structure
- Introduced `ICameraCapabilities` interface and `CameraCapabilities` class for defining camera features like pan, tilt, zoom, and focus.
- Modified `IHasCameras` interface to include a list of `IHasCameraControls` objects for improved camera management.
- Refactored `CameraBaseMessenger` to be generic, enhancing flexibility and type safety.
- Updated `SendCameraFullMessageObject` to include detailed camera capabilities in status messages.
- Added `CameraStateMessage` class to encapsulate camera state, including control support and capabilities.
- Updated `IHasCamerasWithControlMessenger` to use `IKeyName` for camera list and selected camera properties, improving type consistency.
- Enhanced `MobileControlSystemController` to manage devices implementing `IHasCameraControls`, creating appropriate messengers for different device types.
2025-10-07 15:37:31 -06:00
Neil Dorin
82b5dc96c1 feat: Deprecate IHasCamerasMessenger; introduce new controls
Mark IHasCamerasMessenger as obsolete and replace it with
IHasCamerasWithControlMessenger, which adds functionality
for devices with camera controls. A new state message class,
IHasCamerasWithControlsStateMessage, has been added to
encapsulate camera state. Update MobileControlSystemController
to use the new messenger implementation.
2025-10-07 12:00:01 -06:00
Neil Dorin
37cea8a11c fix: Refactor camera control interfaces and event arguments
Significantly restructure camera control interfaces and event arguments.
Removed obsolete interfaces like `IHasCameras` and `CameraSelectedEventArgs`,
and introduced generic event argument classes for improved type safety.
Added `IHasCamerasWithControls` for better management of camera controls.
Corrected the `IHasCameraMuteWithUnmuteReqeust` interface name.
Reintroduced the `eCameraControlMode` enum to define camera control modes.
These changes enhance the organization, clarity, and functionality of the camera control system.
2025-10-07 11:52:07 -06:00
Neil Dorin
a3f0901fa0 Merge pull request #1335 from PepperDash/mc-messenger-improvements
feat: unique status requests for messengers
2025-09-30 14:33:07 -06:00
Neil Dorin
f91f435768 fix: Add null check for CurrentScenario in MobileControlSystem
This change introduces a check for `_roomCombiner.CurrentScenario` being `null`. When it is `null`, a `MobileControlMessage` is created with the type `/system/roomKey`, `clientId`, and `roomKey`, which is then sent to the client. This improves the handling of scenarios without a current scenario by ensuring relevant room key information is communicated.
2025-09-30 12:11:25 -06:00
Andrew Welker
5120b2b574 Merge branch 'main' into mc-messenger-improvements 2025-09-29 11:02:39 -05:00
Andrew Welker
7c90027578 feat: set direct server debug level via console command
The `mobilewsdebug` console command can now be used to set the internal websocket logging level for both the API client and the direct server at the same time.
2025-09-26 21:47:06 -05:00
Andrew Welker
bb694b4200 feat: enable subscription logic for messengers
In order to help control traffic over the websocket, a subscription feature has been added:
* A config option, `enableMessengerSubscriptions` has been added
* When true, the MessengerBase class will assume that any message sent using the `PostStatusMessage` that has a valid client ID wants to send any subsequent unsolicited updates to that same client
* The client's ID will be added a list of subscribed client IDs
* Any subsequent messages sent using the `PostStatusMessage` methods that have a null clientId will ONLY be sent to subscribed clients
* When a client disconnects, it will be removed from the list of subscribed clients

This should cut down drastically on the traffic to the UI, especially when combined with requesting partial status updates from a device rather than the entire state.
2025-09-26 21:31:54 -05:00
Andrew Welker
087d0a1149 chore: move classes and interfaces to individual files 2025-09-26 21:21:01 -05:00
Andrew Welker
4747c16b02 build: delete all local build clzs on build 2025-09-26 21:17:28 -05:00
Andrew Welker
aa4d241dde Merge pull request #1337 from PepperDash/Ibasicvolumecontrols-messenger
fix: modify volume messenger to start with IBasicVolumeControls
2025-09-26 10:40:44 -04:00
Andrew Welker
9fc5586531 fix: use IBasicVolumeControls to build DeviceVolumeMessenger 2025-09-25 15:29:35 -05:00
Andrew Welker
d33fd56529 feat: add method to force panel reload
Other refactorings for factory method and log statements
2025-09-25 14:47:11 -05:00
Neil Dorin
8fc4d21f02 fix: Add DeviceInterfaceSupport property to JoinResponse
This commit introduces a new property, `DeviceInterfaceSupport`,
to the `JoinResponse` class in the `PepperDash.Essentials.WebSocketServer`
namespace. This property is a dictionary that maps strings to
`DeviceInterfaceInfo` objects, enhancing support for device interfaces.
A summary comment has also been added for clarity.
2025-09-25 11:50:42 -06:00
Neil Dorin
6d93662b31 Merge pull request #1338 from PepperDash/io-updates 2025-09-25 09:59:36 -06:00
Andrew Welker
11b190e76f docs: put XML comments on correct thing 2025-09-25 10:51:30 -05:00
Andrew Welker
df03a71cbc chore: fix errors in log statements
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-09-25 09:50:55 -05:00
Andrew Welker
72e67a1c4c fix: implement IHasFeedback for all IO devices and modify logging 2025-09-25 09:47:44 -05:00
Andrew Welker
f8728b6825 docs: update XML comments for EssentialsDeviceFactory 2025-09-25 09:46:55 -05:00
Andrew Welker
d9ef8a2289 docs: add XML comments for IAnalogInput 2025-09-25 09:46:03 -05:00
Andrew Welker
278408a3bc docs: reword XML comments for IHasFeedback Interface 2025-09-25 09:45:42 -05:00
Andrew Welker
fd70377c7f fix: log errors and disconnects for UI Clients 2025-09-25 08:51:11 -05:00
Neil Dorin
06341b14f3 feat: Adds device interface support info to joinroom response in MC websocket server.
Enhance MessengerBase and WebSocketServer functionality

Updated MessengerBase with new methods for action management and message posting, along with improved documentation. Introduced DeviceMessageBase for better message representation.

Enhanced MobileControlWebsocketServer to support device interfaces, adding DeviceInterfaceSupport to JoinResponse and a new DeviceInterfaceInfo class for detailed device information.
2025-09-24 14:49:41 -06:00
Andrew Welker
d7f9c74b2f fix: modify volume messenger to start with IBasicVolumeControls 2025-09-23 13:39:03 -05:00
Andrew Welker
5921b5dbb0 Merge pull request #1336 from PepperDash/hotfix/check-for-cs-lan-on-mctp
Hotfix/check for cs lan on mctp
2025-09-23 13:58:15 -04:00
Neil Dorin
ba0de5128f Update src/PepperDash.Essentials.MobileControl/Touchpanel/MobileControlTouchpanelController.cs
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-09-23 11:56:57 -06:00
Andrew Welker
b0a090062f Merge branch 'hotfix/check-for-cs-lan-on-mctp' into mc-messenger-improvements 2025-09-23 11:25:41 -05:00
Andrew Welker
9c0cab8218 fix: use id parameter and fix some formatting 2025-09-23 11:09:44 -05:00
Andrew Welker
c0af637108 fix: use correct property names 2025-09-23 11:07:24 -05:00
Andrew Welker
9c9eaea928 feat: unique status requests for messengers
UI Applications can now request status for specific feature sets instead of full status for a device. This will hopefully cut down on the traffic and messages required to get the data for the UI.
2025-09-23 10:55:16 -05:00
Neil Dorin
9de94bd65f fix: Update v2 config detection criteria
Changed the logic for identifying v2 configuration files. The check now looks for the presence of a "versions" node instead of the absence of "system" or "template" nodes, reflecting an update in the configuration file structure.
2025-09-22 15:05:06 -06:00
Neil Dorin
ff46fb8f29 feat: Add versioning support to EssentialsConfig
Introduce `Versions` property in `EssentialsConfig` to hold version information.
Add `VersionData` class for Essentials and package versions, and `NugetVersion` class for individual package details.
Retain and document `SystemTemplateConfigs` class.
2025-09-22 14:55:58 -06:00
Neil Dorin
d9243def30 feat: Adds ability to read configs generated from v2 config tool that are pre-merged don't have system or template objects
Refactor config handling and improve documentation

- Updated `PortalConfigReader.cs` to use constants for configuration keys, enhancing maintainability and readability. Improved error logging with `Debug.LogError`.
- Modified `ConfigReader.cs` to handle v2 configuration format, streamlining the loading process and avoiding redundant parsing.
- Added XML documentation comments to properties in `EssentialsConfig.cs`, improving code documentation. Initialized `Rooms` property in the constructor.
- Enhanced `SystemTemplateConfigs` class with XML documentation for better clarity on its properties.
2025-09-22 14:22:57 -06:00
Neil Dorin
258699fbcd fix: Enhance AppUrl change logging
Updated logging to include the new AppUrl value when it changes. This provides better context in the logs, making it easier to track the specific URL that was set.
2025-09-18 18:23:12 -06:00
Neil Dorin
738504e9fc fix: Add error handling for network parameter retrieval
Introduced a try-catch block to handle exceptions when fetching the Crestron Ethernet adapter's ID, subnet mask, and IP address. Added logging for cases where the processor lacks a CS LAN. Also included a new using directive for Serilog.Events.
2025-09-18 14:48:21 -06:00
erikdred
cae1bbd6e6 Merge pull request #1332 from PepperDash/feature/add-hide-property-to-room-combine-scenario
feat: Add HideInUi property to room combiner classes
2025-09-17 18:38:21 -04:00
Neil Dorin
dea4407e3e Update src/PepperDash.Essentials.Core/Room/Combining/IEssentialsRoomCombiner.cs
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-09-17 16:36:33 -06:00
Neil Dorin
ab08a546f7 feat: Add HideInUi property to room combiner classes
- Introduced `HideInUi` property in `EssentialsRoomCombinerPropertiesConfig` to control UI visibility.
- Added `HideInUi` interface property in `IEssentialsRoomCombiner`.
- Implemented `HideInUi` in `RoomCombinationScenario`, along with new `Key` and `Name` properties for improved data representation.
2025-09-17 15:25:23 -06:00
Neil Dorin
ef98d47792 Merge pull request #1331 from PepperDash/dev-list-fix 2025-09-17 08:36:04 -06:00
Andrew Welker
c05976ee60 fix: modify formatting and sorting for devfb response 2025-09-17 09:28:04 -05:00
Andrew Welker
4ca1031bef docs: fix CS1587 errors 2025-09-17 08:52:44 -05:00
Andrew Welker
6d61c4525e fix: use ConsoleCommandResponse for device feedbacks 2025-09-17 08:52:29 -05:00
Andrew Welker
3db274ace5 fix: add line endings for devlist console command 2025-09-17 08:51:45 -05:00
Neil Dorin
f0f708294c Merge pull request #1329 from PepperDash/devlist-fix
fix: print devlist output using ConsoleCommandResponse
2025-09-10 09:58:13 -06:00
Andrew Welker
a00d186c62 fix: print devlist output using ConsoleCommandResponse 2025-09-10 10:45:55 -05:00
Neil Dorin
51da668dfd Merge pull request #1327 from PepperDash/cs-lan-mc-panel
fix: add config property for devices on CS LAN
2025-09-05 16:20:42 -06:00
Andrew Welker
d2b7400039 fix: INvxNetworkPortInformation inherits from IKeyed 2025-09-05 15:55:31 -05:00
Andrew Welker
2424838b7f chore: remove unused using
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-09-02 12:56:14 -05:00
Andrew Welker
9556edc064 fix: add config property for devices on CS LAN 2025-09-02 12:54:51 -05:00
Sumanth Rayancha
f9088691fd Merge pull request #1322 from PepperDash/release
Release
2025-08-26 10:16:18 -04:00
Andrew Welker
e40b6a8b4c Merge pull request #1320 from PepperDash/feature/extract-html-assets
feat: add html assets extraction from zip files in ControlSystem
2025-08-26 09:58:02 -04:00
Erik Meyer
c3b39a87da fix: enhance zip extraction to prevent directory traversal attacks 2025-08-22 15:15:02 -04:00
erikdred
06dc0e947e fix: check for null when getting directory
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-08-22 09:12:20 -04:00
aknous
147997f460 Merge pull request #1321 from PepperDash/IBasicVideoMuteWithFeedbackMessenger
Adds IBasicVideoMuteWithFeedbackMessenger
2025-08-21 14:26:10 -04:00
aknous
49abec5eea Update src/PepperDash.Essentials.MobileControl.Messengers/Messengers/CameraBaseMessenger.cs
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-08-21 13:23:43 -04:00
aknous
6830efe42a fix: fixes CameraBaseMessenger hold timer for PTZ controls, adds storePreset messenger 2025-08-19 18:34:16 -04:00
Neil Dorin
d013068a0c Update src/PepperDash.Essentials/ControlSystem.cs
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-08-19 14:54:11 -06:00
Neil Dorin
52916d29f4 Update src/PepperDash.Essentials/ControlSystem.cs
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-08-19 14:53:06 -06:00
Erik Meyer
19e8489166 feat: add html assets extraction from zip files in ControlSystem 2025-08-19 15:48:43 -04:00
Neil Dorin
fe33443b25 fix: Update IP handling in MobileControlTouchpanelController
Updates logic to handle setting the URL sent to the CH5 wrapper app to use the CS LAN IP based on the actual IpInformationChange event on the panel itself.

- Added `._PepperDash.Essentials.4Series.sln` to .gitignore.
- Introduced new using directives for Regex and Crestron libraries.
- Added `csIpAddress` and `csSubnetMask` fields to store device IP info.
- Modified constructor to retrieve and assign current IP and subnet mask.
- Updated `Panel.IpInformationChange` event handler for logging and URL setting.
- Created `GetUrlWithCorrectIp` method to determine the correct URL based on IP.
- Refactored `SetAppUrl` to utilize the new URL method.
- Commented out old IP determination logic in `MobileControlWebsocketServer.cs` as it was moved to the touchpanel controller.
2025-08-19 13:28:45 -06:00
aknous
8cf195b262 feat: adds IBasicVideoMuteWithFeedbackMessenger 2025-08-19 11:37:39 -04:00
Neil Dorin
40406b797d Merge pull request #1314 from PepperDash/streaming-device-properties 2025-08-15 11:52:20 -06:00
Andrew Welker
65bc408ebf fix: add StreamUrl to baseStreamingDeviceProperties 2025-08-15 12:41:21 -05:00
Neil Dorin
9d49fb8357 Merge pull request #1313 from PepperDash/temp-to-dev
Temp to dev
2025-08-15 09:34:41 -06:00
Neil Dorin
fb7797dac7 Merge pull request #1312 from PepperDash/comm-bridge-add 2025-08-15 09:02:23 -06:00
Andrew Welker
574f5f6dc9 chore: remove unused using directives in CommFactory.cs 2025-08-15 09:51:17 -05:00
Andrew Welker
e49c69a12f feat: add CommBridge class and enhance EssentialsBridgeableDevice with new constructors 2025-08-15 09:48:30 -05:00
Sumanth Rayancha
7889cba196 Merge pull request #1307 from PepperDash/feature/assets-folder
feat: add LoadAssets method to manage asset loading and configuration…
2025-08-13 12:08:14 -04:00
Nick Genovese
033aa1f3dd feat: enhance asset extraction and configuration file handling in ControlSystem 2025-08-12 12:17:58 -04:00
Neil Dorin
9b6c2d80ea Merge pull request #1308 from PepperDash/current-sources 2025-08-11 22:53:11 -06:00
Andrew Welker
a0fc731701 chore: apply copilot suggestions
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-08-11 17:55:02 -05:00
Andrew Welker
f2d0dca7b8 fix: make appdebug case-insensitive
when commands like `appdebug verbose` are used rather than `appdebug 2`, the command would fail because Verbose != verbose. The enum conversion is now case-insensitive.
2025-08-11 17:50:50 -05:00
Andrew Welker
ab4e85d081 fix: use different methods for extensions
The logging extension methods now use the appropriate methods from
the debug class. Previously some messages were not getting handled correctly, and it was causing issues with log statements.
2025-08-11 17:48:46 -05:00
Andrew Welker
47017da527 fix: use correct property names 2025-08-11 17:47:14 -05:00
Andrew Welker
4f0d464ba4 Merge pull request #1306 from PepperDash/fix/remove-unsued-method-in-cs
Fix/remove unsued method in cs
2025-08-11 15:30:04 -05:00
Nick Genovese
0107422507 refactor: remove unused assembly resolution logic from ControlSystem 2025-08-11 16:24:44 -04:00
Nick Genovese
1a366790e7 feat: add LoadAssets method to manage asset loading and configuration file cleanup 2025-08-11 16:23:31 -04:00
Andrew Welker
97448f4f0f Merge branch 'main' into current-sources 2025-08-06 09:01:40 -05:00
Andrew Welker
cf3ece4237 fix: use cr-lf for line endings 2025-08-06 09:00:45 -05:00
Andrew Welker
bb4b2f88b6 Merge pull request #1304 from PepperDash/temp-to-dev
Temp to dev
2025-08-05 16:39:38 -05:00
aknous
808e8042a7 Merge pull request #1305 from PepperDash/default-debug-levels
fix: set default debug levels if not found
2025-08-04 14:47:31 -04:00
Andrew Welker
0bc4388bfd Merge branch 'default-debug-levels' into current-sources 2025-08-04 13:31:26 -05:00
Andrew Welker
dbc132c0da fix: set default debug levels if not found 2025-08-04 13:31:17 -05:00
Andrew Welker
5bb0ab2626 fix: base config properties for use with streaming devices 2025-08-01 21:17:35 -05:00
Andrew Welker
27bf36c58c fix: modify how current sources dictionary gets updated 2025-08-01 09:22:31 -05:00
Andrew Welker
ce886aea63 chore: update local build version 2025-08-01 09:22:31 -05:00
Andrew Welker
ef920bf54c Merge branch 'main' into current-sources 2025-07-31 13:27:43 -05:00
Andrew Welker
88466818ce Merge pull request #1303 from PepperDash/debug-fixes
fix: use correct overload for logging at levels
2025-07-31 13:03:14 -05:00
Andrew Welker
0871a902e1 fix: use correct overload for logging at levels
Some overloads that had the first argument as an Exception were calling the _logger.Write method with a null where the message template should have been, causing messages logged with that overload to be swallowed and not logged.
2025-07-31 12:50:41 -05:00
Andrew Welker
a031424752 fix: add destination & source keys to routelist 2025-07-30 11:20:54 -05:00
Andrew Welker
fd1ba345aa fix: remove StringEnumConverter 2025-07-29 23:01:13 -05:00
Andrew Welker
e03874a7a9 fix: add messenger and event to ICurrentSources 2025-07-29 22:26:07 -05:00
aknous
2efab4f196 Merge pull request #1301 from PepperDash/mc-tp-ip-fix
fix: only adjust IP if processor is a CS processor
2025-07-28 12:57:24 -04:00
Andrew Welker
a41aba1904 Merge pull request #1300 from PepperDash/temp-to-dev
Temp to dev
2025-07-28 11:54:11 -05:00
Andrew Welker
d0ca6721f5 fix: only adjust IP if processor is a CS processor 2025-07-28 11:48:00 -05:00
640 changed files with 15641 additions and 11096 deletions

2
.gitignore vendored
View File

@@ -395,3 +395,5 @@ essentials-framework/Essentials Interfaces/PepperDash_Essentials_Interfaces/Pepp
_site/
api/
*.DS_Store
/._PepperDash.Essentials.4Series.sln
dotnet

View File

@@ -1,6 +1,6 @@
<Project>
<PropertyGroup>
<Version>2.4.0-local</Version>
<Version>2.19.4-local</Version>
<InformationalVersion>$(Version)</InformationalVersion>
<Authors>PepperDash Technology</Authors>
<Company>PepperDash Technology</Company>

View File

@@ -23,23 +23,32 @@
<FileName>$(TargetDir)$(TargetName).$(Version).$(TargetFramework).cpz</FileName>
</PropertyGroup>
<Target Name="DeleteCLZ" BeforeTargets="PreBuildEvent" Condition="$(ProjectType) == 'Library' And $(TargetDir) != '' And Exists($(FileName))">
<Delete Files="$(TargetDir)$(TargetName).$(Version).$(TargetFramework).clz">
<Target Name="DeleteCLZ" BeforeTargets="CoreBuild" Condition="$(ProjectType) == 'Library' And $(TargetDir) != ''">
<ItemGroup>
<OldCLZFiles Include="$(TargetDir)$(TargetName).*.$(TargetFramework).clz" />
</ItemGroup>
<Delete Files="@(OldCLZFiles)" Condition="@(OldCLZFiles) != ''">
<Output TaskParameter="DeletedFiles" ItemName="DeletedList"/>
</Delete>
<Message Text="Deleted files: '@(DeletedList)'" />
<Message Text="Deleted old CLZ files: '@(DeletedList)'" Condition="@(DeletedList) != ''" />
</Target>
<Target Name="DeleteCPZ" BeforeTargets="PreBuildEvent" Condition="$(ProjectType) == 'Program' And $(TargetDir) != '' And Exists($(FileName))">
<Delete Files="$(TargetDir)$(TargetName).$(Version).$(TargetFramework).cpz">
<Target Name="DeleteCPZ" BeforeTargets="CoreBuild" Condition="$(ProjectType) == 'Program' And $(TargetDir) != ''">
<ItemGroup>
<OldCPZFiles Include="$(TargetDir)$(TargetName).*.$(TargetFramework).cpz" />
</ItemGroup>
<Delete Files="@(OldCPZFiles)" Condition="@(OldCPZFiles) != ''">
<Output TaskParameter="DeletedFiles" ItemName="DeletedList"/>
</Delete>
<Message Text="Deleted files: '@(DeletedList)'" />
<Message Text="Deleted old CPZ files: '@(DeletedList)'" Condition="@(DeletedList) != ''" />
</Target>
<Target Name="DeleteCPLZ" BeforeTargets="PreBuildEvent" Condition="$(ProjectType) == 'ProgramLibrary' And $(TargetDir) != '' And Exists($(FileName))">
<Delete Files="$(TargetDir)$(TargetName).$(Version).$(TargetFramework).cplz">
<Target Name="DeleteCPLZ" BeforeTargets="CoreBuild" Condition="$(ProjectType) == 'ProgramLibrary' And $(TargetDir) != ''">
<ItemGroup>
<OldCPLZFiles Include="$(TargetDir)$(TargetName).*.$(TargetFramework).cplz" />
</ItemGroup>
<Delete Files="@(OldCPLZFiles)" Condition="@(OldCPLZFiles) != ''">
<Output TaskParameter="DeletedFiles" ItemName="DeletedList"/>
</Delete>
<Message Text="Deleted files: '@(DeletedList)'" />
<Message Text="Deleted old CPLZ files: '@(DeletedList)'" Condition="@(DeletedList) != ''" />
</Target>
<Target Name="CreateCPLZ" AfterTargets="Build" Condition="$(ProjectType) == 'ProgramLibrary' And $(TargetDir) != ''" DependsOnTargets="DeleteCPLZ">

View File

@@ -0,0 +1,43 @@
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
namespace PepperDash.Core
{
/// <summary>
///
/// </summary>
public class ComTextHelper
{
/// <summary>
/// Gets escaped text for a byte array
/// </summary>
/// <param name="bytes"></param>
/// <returns>string with all bytes escaped</returns>
public static string GetEscapedText(byte[] bytes)
{
return string.Concat(bytes.Select(b => string.Format(@"[{0:X2}]", (int)b)).ToArray());
}
/// <summary>
/// Gets escaped text for a string
/// </summary>
/// <param name="text"></param>
/// <returns>string with all bytes escaped</returns>
public static string GetEscapedText(string text)
{
var bytes = Encoding.GetEncoding(28591).GetBytes(text);
return string.Concat(bytes.Select(b => string.Format(@"[{0:X2}]", (int)b)).ToArray());
}
/// <summary>
/// Gets debug text for a string
/// </summary>
/// <param name="text"></param>
/// <returns>string with all non-printable characters escaped</returns>
public static string GetDebugText(string text)
{
return Regex.Replace(text, @"[^\u0020-\u007E]", a => GetEscapedText(a.Value));
}
}
}

View File

@@ -1,11 +1,14 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using Crestron.SimplSharp;
using PepperDash.Core;
namespace PepperDash.Essentials.Core.Communications
namespace PepperDash.Core
{
/// <summary>
/// Defines the string event handler for line events on the gather

View File

@@ -1,8 +1,8 @@
using System;
using System.Collections.Generic;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Crestron.SimplSharp;
using PepperDash.Core;
namespace PepperDash.Core
{
@@ -36,14 +36,14 @@ namespace PepperDash.Core
{
get
{
return _DebugTimeoutInMs/60000;
return _DebugTimeoutInMs / 60000;
}
}
/// <summary>
/// Gets or sets the RxStreamDebuggingIsEnabled
/// </summary>
public bool RxStreamDebuggingIsEnabled{ get; private set; }
public bool RxStreamDebuggingIsEnabled { get; private set; }
/// <summary>
/// Indicates that transmit stream debugging is enabled
@@ -107,7 +107,7 @@ namespace PepperDash.Core
TxStreamDebuggingIsEnabled = true;
Debug.SetDeviceDebugSettings(ParentDeviceKey, setting);
}
/// <summary>
@@ -135,51 +135,4 @@ namespace PepperDash.Core
DebugExpiryPeriod = null;
}
}
/// <summary>
/// The available settings for stream debugging
/// </summary>
[Flags]
/// <summary>
/// Enumeration of eStreamDebuggingSetting values
/// </summary>
public enum eStreamDebuggingSetting
{
/// <summary>
/// Debug off
/// </summary>
Off = 0,
/// <summary>
/// Debug received data
/// </summary>
Rx = 1,
/// <summary>
/// Debug transmitted data
/// </summary>
Tx = 2,
/// <summary>
/// Debug both received and transmitted data
/// </summary>
Both = Rx | Tx
}
/// <summary>
/// The available settings for stream debugging response types
/// </summary>
[Flags]
public enum eStreamDebuggingDataTypeSettings
{
/// <summary>
/// Debug data in byte format
/// </summary>
Bytes = 0,
/// <summary>
/// Debug data in text format
/// </summary>
Text = 1,
/// <summary>
/// Debug data in both byte and text formats
/// </summary>
Both = Bytes | Text,
}
}

View File

@@ -1,8 +1,9 @@
using System;
using Crestron.SimplSharp;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
namespace PepperDash.Essentials.Core.Communications
namespace PepperDash.Core
{
/// <summary>
/// Represents a ControlPropertiesConfig

View File

@@ -416,18 +416,14 @@ namespace PepperDash.Core
if (bytesHandler != null)
{
var bytes = Encoding.UTF8.GetBytes(response);
if (StreamDebugging.RxStreamDebuggingIsEnabled)
{
this.LogInformation("Received {1} bytes: '{0}'", ComTextHelper.GetEscapedText(bytes), bytes.Length);
}
this.PrintReceivedBytes(bytes);
bytesHandler(this, new GenericCommMethodReceiveBytesArgs(bytes));
}
var textHandler = TextReceived;
if (textHandler != null)
{
if (StreamDebugging.RxStreamDebuggingIsEnabled)
this.LogInformation("Received: '{0}'", ComTextHelper.GetDebugText(response));
this.PrintReceivedText(response);
textHandler(this, new GenericCommMethodReceiveTextArgs(response));
}
@@ -484,11 +480,7 @@ namespace PepperDash.Core
{
if (Client != null && TheStream != null && IsConnected)
{
if (StreamDebugging.TxStreamDebuggingIsEnabled)
this.LogInformation(
"Sending {length} characters of text: '{text}'",
text.Length,
ComTextHelper.GetDebugText(text));
this.PrintSentText(text);
TheStream.Write(text);
TheStream.Flush();
@@ -521,8 +513,7 @@ namespace PepperDash.Core
{
if (Client != null && TheStream != null && IsConnected)
{
if (StreamDebugging.TxStreamDebuggingIsEnabled)
this.LogInformation("Sending {0} bytes: '{1}'", bytes.Length, ComTextHelper.GetEscapedText(bytes));
this.PrintSentBytes(bytes);
TheStream.Write(bytes, 0, bytes.Length);
TheStream.Flush();

View File

@@ -19,44 +19,44 @@ namespace PepperDash.Core
/// </summary>
public CommunicationStreamDebugging StreamDebugging { get; private set; }
/// <summary>
/// Fires when data is received from the server and returns it as a Byte array
/// </summary>
public event EventHandler<GenericCommMethodReceiveBytesArgs> BytesReceived;
/// <summary>
/// Fires when data is received from the server and returns it as a Byte array
/// </summary>
public event EventHandler<GenericCommMethodReceiveBytesArgs> BytesReceived;
/// <summary>
/// Fires when data is received from the server and returns it as text
/// </summary>
public event EventHandler<GenericCommMethodReceiveTextArgs> TextReceived;
/// <summary>
/// Fires when data is received from the server and returns it as text
/// </summary>
public event EventHandler<GenericCommMethodReceiveTextArgs> TextReceived;
/// <summary>
///
/// </summary>
//public event GenericSocketStatusChangeEventDelegate SocketStatusChange;
public event EventHandler<GenericSocketStatusChageEventArgs> ConnectionChange;
/// <summary>
///
/// </summary>
//public event GenericSocketStatusChangeEventDelegate SocketStatusChange;
public event EventHandler<GenericSocketStatusChageEventArgs> ConnectionChange;
private string _hostname;
private string _hostname;
/// <summary>
/// Address of server
/// </summary>
public string Hostname
{
get
{
return _hostname;
}
get
{
return _hostname;
}
set
{
_hostname = value;
if (_client != null)
{
_client.AddressClientConnectedTo = _hostname;
}
}
}
set
{
_hostname = value;
if (_client != null)
{
_client.AddressClientConnectedTo = _hostname;
}
}
}
/// <summary>
/// Gets or sets the Port
@@ -78,19 +78,19 @@ namespace PepperDash.Core
/// </summary>
public int BufferSize { get; set; }
/// <summary>
/// The actual client class
/// </summary>
private TCPClient _client;
/// <summary>
/// The actual client class
/// </summary>
private TCPClient _client;
/// <summary>
/// Bool showing if socket is connected
/// </summary>
public bool IsConnected
{
get { return _client != null && _client.ClientStatus == SocketStatus.SOCKET_STATUS_CONNECTED; }
/// <summary>
/// Bool showing if socket is connected
/// </summary>
public bool IsConnected
{
get { return _client != null && _client.ClientStatus == SocketStatus.SOCKET_STATUS_CONNECTED; }
}
/// <summary>
/// S+ helper for IsConnected
/// </summary>
@@ -99,15 +99,15 @@ namespace PepperDash.Core
get { return (ushort)(IsConnected ? 1 : 0); }
}
/// <summary>
/// _client socket status Read only
/// </summary>
public SocketStatus ClientStatus
{
get
/// <summary>
/// _client socket status Read only
/// </summary>
public SocketStatus ClientStatus
{
get
{
return _client == null ? SocketStatus.SOCKET_STATUS_NO_CONNECT : _client.ClientStatus;
}
return _client == null ? SocketStatus.SOCKET_STATUS_NO_CONNECT : _client.ClientStatus;
}
}
/// <summary>
@@ -119,26 +119,26 @@ namespace PepperDash.Core
get { return (ushort)ClientStatus; }
}
/// <summary>
/// <summary>
/// Status text shows the message associated with socket status
/// </summary>
public string ClientStatusText { get { return ClientStatus.ToString(); } }
/// </summary>
public string ClientStatusText { get { return ClientStatus.ToString(); } }
/// <summary>
/// Ushort representation of client status
/// </summary>
/// <summary>
/// Ushort representation of client status
/// </summary>
[Obsolete]
public ushort UClientStatus { get { return (ushort)ClientStatus; } }
public ushort UClientStatus { get { return (ushort)ClientStatus; } }
/// <summary>
/// Connection failure reason
/// </summary>
public string ConnectionFailure { get { return ClientStatus.ToString(); } }
/// <summary>
/// Connection failure reason
/// </summary>
public string ConnectionFailure { get { return ClientStatus.ToString(); } }
/// <summary>
/// Gets or sets the AutoReconnect
/// </summary>
public bool AutoReconnect { get; set; }
/// <summary>
/// Gets or sets the AutoReconnect
/// </summary>
public bool AutoReconnect { get; set; }
/// <summary>
/// S+ helper for AutoReconnect
@@ -149,29 +149,29 @@ namespace PepperDash.Core
set { AutoReconnect = value == 1; }
}
/// <summary>
/// Milliseconds to wait before attempting to reconnect. Defaults to 5000
/// </summary>
public int AutoReconnectIntervalMs { get; set; }
/// <summary>
/// Milliseconds to wait before attempting to reconnect. Defaults to 5000
/// </summary>
public int AutoReconnectIntervalMs { get; set; }
/// <summary>
/// Set only when the disconnect method is called
/// </summary>
bool DisconnectCalledByUser;
/// <summary>
/// Set only when the disconnect method is called
/// </summary>
bool DisconnectCalledByUser;
/// <summary>
///
/// </summary>
public bool Connected
{
get { return _client.ClientStatus == SocketStatus.SOCKET_STATUS_CONNECTED; }
}
/// <summary>
///
/// </summary>
public bool Connected
{
get { return _client.ClientStatus == SocketStatus.SOCKET_STATUS_CONNECTED; }
}
//Lock object to prevent simulatneous connect/disconnect operations
private CCriticalSection connectLock = new CCriticalSection();
// private Timer for auto reconnect
private CTimer RetryTimer;
private CTimer RetryTimer;
/// <summary>
/// Constructor
@@ -181,8 +181,8 @@ namespace PepperDash.Core
/// <param name="port"></param>
/// <param name="bufferSize"></param>
public GenericTcpIpClient(string key, string address, int port, int bufferSize)
: base(key)
{
: base(key)
{
StreamDebugging = new CommunicationStreamDebugging(key);
CrestronEnvironment.ProgramStatusEventHandler += new ProgramStatusEventHandler(CrestronEnvironment_ProgramStatusEventHandler);
AutoReconnectIntervalMs = 5000;
@@ -218,18 +218,18 @@ namespace PepperDash.Core
/// Default constructor for S+
/// </summary>
public GenericTcpIpClient()
: base(SplusKey)
: base(SplusKey)
{
StreamDebugging = new CommunicationStreamDebugging(SplusKey);
CrestronEnvironment.ProgramStatusEventHandler += new ProgramStatusEventHandler(CrestronEnvironment_ProgramStatusEventHandler);
AutoReconnectIntervalMs = 5000;
CrestronEnvironment.ProgramStatusEventHandler += new ProgramStatusEventHandler(CrestronEnvironment_ProgramStatusEventHandler);
AutoReconnectIntervalMs = 5000;
BufferSize = 2000;
RetryTimer = new CTimer(o =>
{
Reconnect();
}, Timeout.Infinite);
}
}
/// <summary>
/// Initialize method
@@ -255,26 +255,26 @@ namespace PepperDash.Core
///
/// </summary>
/// <returns></returns>
/// <summary>
/// Deactivate method
/// </summary>
public override bool Deactivate()
{
/// <summary>
/// Deactivate method
/// </summary>
public override bool Deactivate()
{
RetryTimer.Stop();
RetryTimer.Dispose();
if (_client != null)
{
_client.SocketStatusChange -= this.Client_SocketStatusChange;
_client.SocketStatusChange -= this.Client_SocketStatusChange;
DisconnectClient();
}
return true;
}
return true;
}
/// <summary>
/// Connect method
/// </summary>
public void Connect()
{
/// <summary>
/// Connect method
/// </summary>
public void Connect()
{
if (string.IsNullOrEmpty(Hostname))
{
Debug.Console(1, Debug.ErrorLogLevel.Warning, "GenericTcpIpClient '{0}': No address set", Key);
@@ -310,7 +310,7 @@ namespace PepperDash.Core
{
connectLock.Leave();
}
}
}
private void Reconnect()
{
@@ -337,11 +337,11 @@ namespace PepperDash.Core
}
}
/// <summary>
/// Disconnect method
/// </summary>
public void Disconnect()
{
/// <summary>
/// Disconnect method
/// </summary>
public void Disconnect()
{
try
{
connectLock.Enter();
@@ -355,7 +355,7 @@ namespace PepperDash.Core
{
connectLock.Leave();
}
}
}
/// <summary>
/// DisconnectClient method
@@ -375,7 +375,7 @@ namespace PepperDash.Core
/// </summary>
/// <param name="c"></param>
void ConnectToServerCallback(TCPClient c)
{
{
if (c.ClientStatus != SocketStatus.SOCKET_STATUS_CONNECTED)
{
Debug.Console(0, this, "Server connection result: {0}", c.ClientStatus);
@@ -385,13 +385,13 @@ namespace PepperDash.Core
{
Debug.Console(1, this, "Server connection result: {0}", c.ClientStatus);
}
}
}
/// <summary>
/// Disconnects, waits and attemtps to connect again
/// </summary>
void WaitAndTryReconnect()
{
{
CrestronInvoke.BeginInvoke(o =>
{
try
@@ -409,7 +409,7 @@ namespace PepperDash.Core
connectLock.Leave();
}
});
}
}
/// <summary>
/// Recieves incoming data
@@ -417,7 +417,7 @@ namespace PepperDash.Core
/// <param name="client"></param>
/// <param name="numBytes"></param>
void Receive(TCPClient client, int numBytes)
{
{
if (client != null)
{
if (numBytes > 0)
@@ -426,10 +426,7 @@ namespace PepperDash.Core
var bytesHandler = BytesReceived;
if (bytesHandler != null)
{
if (StreamDebugging.RxStreamDebuggingIsEnabled)
{
Debug.Console(0, this, "Received {1} bytes: '{0}'", ComTextHelper.GetEscapedText(bytes), bytes.Length);
}
this.PrintReceivedBytes(bytes);
bytesHandler(this, new GenericCommMethodReceiveBytesArgs(bytes));
}
var textHandler = TextReceived;
@@ -437,58 +434,54 @@ namespace PepperDash.Core
{
var str = Encoding.GetEncoding(28591).GetString(bytes, 0, bytes.Length);
if (StreamDebugging.RxStreamDebuggingIsEnabled)
{
Debug.Console(0, this, "Received {1} characters of text: '{0}'", ComTextHelper.GetDebugText(str), str.Length);
}
this.PrintReceivedText(str);
textHandler(this, new GenericCommMethodReceiveTextArgs(str));
}
}
}
client.ReceiveDataAsync(Receive);
}
}
}
/// <summary>
/// SendText method
/// </summary>
public void SendText(string text)
{
var bytes = Encoding.GetEncoding(28591).GetBytes(text);
// Check debug level before processing byte array
if (StreamDebugging.TxStreamDebuggingIsEnabled)
Debug.Console(0, this, "Sending {0} characters of text: '{1}'", text.Length, ComTextHelper.GetDebugText(text));
/// <summary>
/// SendText method
/// </summary>
public void SendText(string text)
{
var bytes = Encoding.GetEncoding(28591).GetBytes(text);
// Check debug level before processing byte array
this.PrintSentText(text);
if (_client != null)
_client.SendData(bytes, bytes.Length);
}
_client.SendData(bytes, bytes.Length);
}
/// <summary>
/// SendEscapedText method
/// </summary>
public void SendEscapedText(string text)
{
var unescapedText = Regex.Replace(text, @"\\x([0-9a-fA-F][0-9a-fA-F])", s =>
{
var hex = s.Groups[1].Value;
return ((char)Convert.ToByte(hex, 16)).ToString();
});
SendText(unescapedText);
}
/// <summary>
/// SendEscapedText method
/// </summary>
public void SendEscapedText(string text)
{
var unescapedText = Regex.Replace(text, @"\\x([0-9a-fA-F][0-9a-fA-F])", s =>
{
var hex = s.Groups[1].Value;
return ((char)Convert.ToByte(hex, 16)).ToString();
});
SendText(unescapedText);
}
/// <summary>
/// Sends Bytes to the server
/// </summary>
/// <param name="bytes"></param>
/// <summary>
/// SendBytes method
/// </summary>
public void SendBytes(byte[] bytes)
{
/// <summary>
/// SendBytes method
/// </summary>
public void SendBytes(byte[] bytes)
{
if (StreamDebugging.TxStreamDebuggingIsEnabled)
Debug.Console(0, this, "Sending {0} bytes: '{1}'", bytes.Length, ComTextHelper.GetEscapedText(bytes));
if (_client != null)
_client.SendData(bytes, bytes.Length);
}
_client.SendData(bytes, bytes.Length);
}
/// <summary>
/// Socket Status Change Handler
@@ -496,7 +489,7 @@ namespace PepperDash.Core
/// <param name="client"></param>
/// <param name="clientSocketStatus"></param>
void Client_SocketStatusChange(TCPClient client, SocketStatus clientSocketStatus)
{
{
if (clientSocketStatus != SocketStatus.SOCKET_STATUS_CONNECTED)
{
Debug.Console(0, this, "Socket status change {0} ({1})", clientSocketStatus, ClientStatusText);
@@ -505,68 +498,68 @@ namespace PepperDash.Core
else
{
Debug.Console(1, this, "Socket status change {0} ({1})", clientSocketStatus, ClientStatusText);
_client.ReceiveDataAsync(Receive);
_client.ReceiveDataAsync(Receive);
}
var handler = ConnectionChange;
if (handler != null)
ConnectionChange(this, new GenericSocketStatusChageEventArgs(this));
}
}
var handler = ConnectionChange;
if (handler != null)
ConnectionChange(this, new GenericSocketStatusChageEventArgs(this));
}
}
/// <summary>
/// Represents a TcpSshPropertiesConfig
/// </summary>
public class TcpSshPropertiesConfig
{
/// <summary>
/// Represents a TcpSshPropertiesConfig
/// </summary>
public class TcpSshPropertiesConfig
{
/// <summary>
/// Address to connect to
/// </summary>
[JsonProperty(Required = Required.Always)]
public string Address { get; set; }
public string Address { get; set; }
/// <summary>
/// Port to connect to
/// </summary>
[JsonProperty(Required = Required.Always)]
public int Port { get; set; }
[JsonProperty(Required = Required.Always)]
public int Port { get; set; }
/// <summary>
/// Username credential
/// </summary>
public string Username { get; set; }
/// <summary>
/// Gets or sets the Password
/// </summary>
public string Password { get; set; }
public string Username { get; set; }
/// <summary>
/// Gets or sets the Password
/// </summary>
public string Password { get; set; }
/// <summary>
/// Defaults to 32768
/// </summary>
public int BufferSize { get; set; }
/// <summary>
/// Defaults to 32768
/// </summary>
public int BufferSize { get; set; }
/// <summary>
/// Gets or sets the AutoReconnect
/// </summary>
public bool AutoReconnect { get; set; }
/// <summary>
/// Gets or sets the AutoReconnect
/// </summary>
public bool AutoReconnect { get; set; }
/// <summary>
/// Gets or sets the AutoReconnectIntervalMs
/// </summary>
public int AutoReconnectIntervalMs { get; set; }
/// <summary>
/// Gets or sets the AutoReconnectIntervalMs
/// </summary>
public int AutoReconnectIntervalMs { get; set; }
/// <summary>
/// Default constructor
/// </summary>
public TcpSshPropertiesConfig()
{
BufferSize = 32768;
AutoReconnect = true;
AutoReconnectIntervalMs = 5000;
{
BufferSize = 32768;
AutoReconnect = true;
AutoReconnectIntervalMs = 5000;
Username = "";
Password = "";
}
}
}
}
}

View File

@@ -124,21 +124,21 @@ namespace PepperDash.Core
CrestronEnvironment.ProgramStatusEventHandler += new ProgramStatusEventHandler(CrestronEnvironment_ProgramStatusEventHandler);
CrestronEnvironment.EthernetEventHandler += new EthernetEventHandler(CrestronEnvironment_EthernetEventHandler);
}
/// <summary>
///
/// </summary>
/// <param name="key"></param>
/// <param name="address"></param>
/// <param name="port"></param>
/// <param name="buffefSize"></param>
public GenericUdpServer(string key, string address, int port, int buffefSize)
/// <param name="bufferSize"></param>
public GenericUdpServer(string key, string address, int port, int bufferSize)
: base(key)
{
StreamDebugging = new CommunicationStreamDebugging(key);
StreamDebugging = new CommunicationStreamDebugging(key);
Hostname = address;
Port = port;
BufferSize = buffefSize;
BufferSize = bufferSize;
CrestronEnvironment.ProgramStatusEventHandler += new ProgramStatusEventHandler(CrestronEnvironment_ProgramStatusEventHandler);
CrestronEnvironment.EthernetEventHandler += new EthernetEventHandler(CrestronEnvironment_EthernetEventHandler);
@@ -180,7 +180,7 @@ namespace PepperDash.Core
/// <param name="programEventType"></param>
void CrestronEnvironment_ProgramStatusEventHandler(eProgramStatusEventType programEventType)
{
if (programEventType != eProgramStatusEventType.Stopping)
if (programEventType != eProgramStatusEventType.Stopping)
return;
Debug.Console(1, this, "Program stopping. Disabling Server");
@@ -194,7 +194,21 @@ namespace PepperDash.Core
{
if (Server == null)
{
Server = new UDPServer();
try
{
var address = IPAddress.Parse(Hostname);
Server = new UDPServer(address, Port, BufferSize);
}
catch (Exception ex)
{
this.LogError("Error parsing IP Address '{ipAddress}': message: {message}", Hostname, ex.Message);
this.LogInformation("Creating UDPServer with default buffersize");
Server = new UDPServer();
}
}
if (string.IsNullOrEmpty(Hostname))
@@ -229,7 +243,7 @@ namespace PepperDash.Core
/// </summary>
public void Disconnect()
{
if(Server != null)
if (Server != null)
Server.DisableUDPServer();
IsConnected = false;
@@ -251,7 +265,7 @@ namespace PepperDash.Core
try
{
if (numBytes <= 0)
if (numBytes <= 0)
return;
var sourceIp = Server.IPAddressLastMessageReceivedFrom;
@@ -267,17 +281,13 @@ namespace PepperDash.Core
var bytesHandler = BytesReceived;
if (bytesHandler != null)
{
if (StreamDebugging.RxStreamDebuggingIsEnabled)
{
Debug.Console(0, this, "Received {1} bytes: '{0}'", ComTextHelper.GetEscapedText(bytes), bytes.Length);
}
this.PrintReceivedBytes(bytes);
bytesHandler(this, new GenericCommMethodReceiveBytesArgs(bytes));
}
var textHandler = TextReceived;
if (textHandler != null)
{
if (StreamDebugging.RxStreamDebuggingIsEnabled)
Debug.Console(0, this, "Received {1} characters of text: '{0}'", ComTextHelper.GetDebugText(str), str.Length);
this.PrintReceivedText(str);
textHandler(this, new GenericCommMethodReceiveTextArgs(str));
}
}
@@ -304,8 +314,7 @@ namespace PepperDash.Core
if (IsConnected && Server != null)
{
if (StreamDebugging.TxStreamDebuggingIsEnabled)
Debug.Console(0, this, "Sending {0} characters of text: '{1}'", text.Length, ComTextHelper.GetDebugText(text));
this.PrintSentText(text);
Server.SendData(bytes, bytes.Length);
}
@@ -320,8 +329,7 @@ namespace PepperDash.Core
/// </summary>
public void SendBytes(byte[] bytes)
{
if (StreamDebugging.TxStreamDebuggingIsEnabled)
Debug.Console(0, this, "Sending {0} bytes: '{1}'", bytes.Length, ComTextHelper.GetEscapedText(bytes));
this.PrintSentBytes(bytes);
if (IsConnected && Server != null)
Server.SendData(bytes, bytes.Length);
@@ -329,11 +337,11 @@ namespace PepperDash.Core
}
/// <summary>
/// Represents a GenericUdpReceiveTextExtraArgs
/// </summary>
public class GenericUdpReceiveTextExtraArgs : EventArgs
{
/// <summary>
/// Represents a GenericUdpReceiveTextExtraArgs
/// </summary>
public class GenericUdpReceiveTextExtraArgs : EventArgs
{
/// <summary>
///
/// </summary>
@@ -345,7 +353,7 @@ namespace PepperDash.Core
/// <summary>
///
/// </summary>
public int Port { get; private set; }
public int Port { get; private set; }
/// <summary>
///
/// </summary>
@@ -359,18 +367,18 @@ namespace PepperDash.Core
/// <param name="port"></param>
/// <param name="bytes"></param>
public GenericUdpReceiveTextExtraArgs(string text, string ipAddress, int port, byte[] bytes)
{
Text = text;
IpAddress = ipAddress;
Port = port;
Bytes = bytes;
}
{
Text = text;
IpAddress = ipAddress;
Port = port;
Bytes = bytes;
}
/// <summary>
/// Stupid S+ Constructor
/// </summary>
public GenericUdpReceiveTextExtraArgs() { }
}
/// <summary>
/// Stupid S+ Constructor
/// </summary>
public GenericUdpReceiveTextExtraArgs() { }
}
/// <summary>
///

View File

@@ -0,0 +1,69 @@
using System;
using Crestron.SimplSharp;
namespace PepperDash.Core
{
/// <summary>
/// Extension methods for stream debugging
/// </summary>
public static class StreamDebuggingExtensions
{
private static readonly string app = CrestronEnvironment.DevicePlatform == eDevicePlatform.Appliance ? $"App {InitialParametersClass.ApplicationNumber}" : $"{InitialParametersClass.RoomId}";
/// <summary>
/// Print the sent bytes to the console
/// </summary>
/// <param name="comms">comms device</param>
/// <param name="bytes">bytes to print</param>
public static void PrintSentBytes(this IStreamDebugging comms, byte[] bytes)
{
if (!comms.StreamDebugging.TxStreamDebuggingIsEnabled) return;
var timestamp = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff");
CrestronConsole.PrintLine($"[{timestamp}][{app}][{comms.Key}] Sending {bytes.Length} bytes: '{ComTextHelper.GetEscapedText(bytes)}'");
}
/// <summary>
/// Print the received bytes to the console
/// </summary>
/// <param name="comms">comms device</param>
/// <param name="bytes">bytes to print</param>
public static void PrintReceivedBytes(this IStreamDebugging comms, byte[] bytes)
{
if (!comms.StreamDebugging.RxStreamDebuggingIsEnabled) return;
var timestamp = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff");
CrestronConsole.PrintLine($"[{timestamp}][{app}][{comms.Key}] Received {bytes.Length} bytes: '{ComTextHelper.GetEscapedText(bytes)}'");
}
/// <summary>
/// Print the sent text to the console
/// </summary>
/// <param name="comms">comms device</param>
/// <param name="text">text to print</param>
public static void PrintSentText(this IStreamDebugging comms, string text)
{
if (!comms.StreamDebugging.TxStreamDebuggingIsEnabled) return;
var timestamp = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff");
CrestronConsole.PrintLine($"[{timestamp}][{app}][{comms.Key}] Sending Text: '{ComTextHelper.GetDebugText(text)}'");
}
/// <summary>
/// Print the received text to the console
/// </summary>
/// <param name="comms">comms device</param>
/// <param name="text">text to print</param>
public static void PrintReceivedText(this IStreamDebugging comms, string text)
{
if (!comms.StreamDebugging.RxStreamDebuggingIsEnabled) return;
var timestamp = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff");
CrestronConsole.PrintLine($"[{timestamp}][{app}][{comms.Key}] Received Text: '{ComTextHelper.GetDebugText(text)}'");
}
}
}

View File

@@ -1,4 +1,10 @@
namespace PepperDash.Essentials.Core.Communications
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Crestron.SimplSharp;
namespace PepperDash.Core
{
/// <summary>
/// Crestron Control Methods for a comm object
@@ -68,6 +74,14 @@
/// <summary>
/// Secure TCP/IP
/// </summary>
SecureTcpIp
SecureTcpIp,
/// <summary>
/// Used when comms needs to be handled in SIMPL and bridged opposite the normal direction
/// </summary>
ComBridge,
/// <summary>
/// InfinetEX control
/// </summary>
InfinetEx
}
}

View File

@@ -0,0 +1,24 @@
using System;
namespace PepperDash.Core
{
/// <summary>
/// The available settings for stream debugging response types
/// </summary>
[Flags]
public enum eStreamDebuggingDataTypeSettings
{
/// <summary>
/// Debug data in byte format
/// </summary>
Bytes = 0,
/// <summary>
/// Debug data in text format
/// </summary>
Text = 1,
/// <summary>
/// Debug data in both byte and text formats
/// </summary>
Both = Bytes | Text,
}
}

View File

@@ -0,0 +1,28 @@
using System;
namespace PepperDash.Core
{
/// <summary>
/// The available settings for stream debugging
/// </summary>
[Flags]
public enum eStreamDebuggingSetting
{
/// <summary>
/// Debug off
/// </summary>
Off = 0,
/// <summary>
/// Debug received data
/// </summary>
Rx = 1,
/// <summary>
/// Debug transmitted data
/// </summary>
Tx = 2,
/// <summary>
/// Debug both received and transmitted data
/// </summary>
Both = Rx | Tx
}
}

View File

@@ -1,10 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Crestron.SimplSharp;
using Crestron.SimplSharp.CrestronSockets;
using System.Text.RegularExpressions;
using Newtonsoft.Json;
namespace PepperDash.Core
@@ -42,7 +39,7 @@ namespace PepperDash.Core
/// Defines the contract for IBasicCommunication
/// </summary>
public interface IBasicCommunication : ICommunicationReceiver
{
{
/// <summary>
/// Send text to the device
/// </summary>
@@ -54,7 +51,7 @@ namespace PepperDash.Core
/// </summary>
/// <param name="bytes"></param>
void SendBytes(byte[] bytes);
}
}
/// <summary>
/// Represents a device that implements IBasicCommunication and IStreamDebugging
@@ -67,7 +64,7 @@ namespace PepperDash.Core
/// <summary>
/// Represents a device with stream debugging capablities
/// </summary>
public interface IStreamDebugging
public interface IStreamDebugging : IKeyed
{
/// <summary>
/// Object to enable stream debugging
@@ -76,12 +73,12 @@ namespace PepperDash.Core
CommunicationStreamDebugging StreamDebugging { get; }
}
/// <summary>
/// For IBasicCommunication classes that have SocketStatus. GenericSshClient,
/// GenericTcpIpClient
/// </summary>
public interface ISocketStatus : IBasicCommunication
{
/// <summary>
/// For IBasicCommunication classes that have SocketStatus. GenericSshClient,
/// GenericTcpIpClient
/// </summary>
public interface ISocketStatus : IBasicCommunication
{
/// <summary>
/// Notifies of socket status changes
/// </summary>
@@ -93,7 +90,7 @@ namespace PepperDash.Core
[JsonProperty("clientStatus")]
[JsonConverter(typeof(Newtonsoft.Json.Converters.StringEnumConverter))]
SocketStatus ClientStatus { get; }
}
}
/// <summary>
/// Describes a device that implements ISocketStatus and IStreamDebugging
@@ -107,24 +104,24 @@ namespace PepperDash.Core
/// Describes a device that can automatically attempt to reconnect
/// </summary>
public interface IAutoReconnect
{
{
/// <summary>
/// Enable automatic recconnect
/// </summary>
[JsonProperty("autoReconnect")]
bool AutoReconnect { get; set; }
bool AutoReconnect { get; set; }
/// <summary>
/// Interval in ms to attempt automatic recconnections
/// </summary>
[JsonProperty("autoReconnectIntervalMs")]
int AutoReconnectIntervalMs { get; set; }
}
int AutoReconnectIntervalMs { get; set; }
}
/// <summary>
///
/// </summary>
public enum eGenericCommMethodStatusChangeType
{
/// <summary>
///
/// </summary>
public enum eGenericCommMethodStatusChangeType
{
/// <summary>
/// Connected
/// </summary>
@@ -133,45 +130,45 @@ namespace PepperDash.Core
/// Disconnected
/// </summary>
Disconnected
}
}
/// <summary>
/// This delegate defines handler for IBasicCommunication status changes
/// </summary>
/// <param name="comm">Device firing the status change</param>
/// <param name="status"></param>
public delegate void GenericCommMethodStatusHandler(IBasicCommunication comm, eGenericCommMethodStatusChangeType status);
/// <summary>
/// This delegate defines handler for IBasicCommunication status changes
/// </summary>
/// <param name="comm">Device firing the status change</param>
/// <param name="status"></param>
public delegate void GenericCommMethodStatusHandler(IBasicCommunication comm, eGenericCommMethodStatusChangeType status);
/// <summary>
///
/// </summary>
public class GenericCommMethodReceiveBytesArgs : EventArgs
{
/// <summary>
/// Gets or sets the Bytes
/// </summary>
public byte[] Bytes { get; private set; }
/// <summary>
///
/// </summary>
public class GenericCommMethodReceiveBytesArgs : EventArgs
{
/// <summary>
/// Gets or sets the Bytes
/// </summary>
public byte[] Bytes { get; private set; }
/// <summary>
///
/// </summary>
/// <param name="bytes"></param>
public GenericCommMethodReceiveBytesArgs(byte[] bytes)
{
Bytes = bytes;
}
{
Bytes = bytes;
}
/// <summary>
/// S+ Constructor
/// </summary>
public GenericCommMethodReceiveBytesArgs() { }
}
/// <summary>
/// S+ Constructor
/// </summary>
public GenericCommMethodReceiveBytesArgs() { }
}
/// <summary>
///
/// </summary>
public class GenericCommMethodReceiveTextArgs : EventArgs
{
/// <summary>
///
/// </summary>
public class GenericCommMethodReceiveTextArgs : EventArgs
{
/// <summary>
///
/// </summary>
@@ -185,9 +182,9 @@ namespace PepperDash.Core
/// </summary>
/// <param name="text"></param>
public GenericCommMethodReceiveTextArgs(string text)
{
Text = text;
}
{
Text = text;
}
/// <summary>
///
@@ -195,59 +192,14 @@ namespace PepperDash.Core
/// <param name="text"></param>
/// <param name="delimiter"></param>
public GenericCommMethodReceiveTextArgs(string text, string delimiter)
:this(text)
: this(text)
{
Delimiter = delimiter;
}
/// <summary>
/// S+ Constructor
/// </summary>
public GenericCommMethodReceiveTextArgs() { }
}
/// <summary>
///
/// </summary>
public class ComTextHelper
{
/// <summary>
/// Gets escaped text for a byte array
/// S+ Constructor
/// </summary>
/// <param name="bytes"></param>
/// <returns></returns>
public static string GetEscapedText(byte[] bytes)
{
return String.Concat(bytes.Select(b => string.Format(@"[{0:X2}]", (int)b)).ToArray());
}
/// <summary>
/// Gets escaped text for a string
/// </summary>
/// <param name="text"></param>
/// <returns></returns>
/// <summary>
/// GetEscapedText method
/// </summary>
public static string GetEscapedText(string text)
{
var bytes = Encoding.GetEncoding(28591).GetBytes(text);
return String.Concat(bytes.Select(b => string.Format(@"[{0:X2}]", (int)b)).ToArray());
}
/// <summary>
/// Gets debug text for a string
/// </summary>
/// <param name="text"></param>
/// <returns></returns>
/// <summary>
/// GetDebugText method
/// </summary>
public static string GetDebugText(string text)
{
return Regex.Replace(text, @"[^\u0020-\u007E]", a => GetEscapedText(a.Value));
}
}
public GenericCommMethodReceiveTextArgs() { }
}
}

View File

@@ -9,40 +9,59 @@ using Serilog.Events;
namespace PepperDash.Core.Config
{
/// <summary>
/// Reads a Portal formatted config file
/// </summary>
public class PortalConfigReader
{
/// <summary>
/// Reads the config file, checks if it needs a merge, merges and saves, then returns the merged Object.
/// </summary>
/// <returns>JObject of config file</returns>
public static void ReadAndMergeFileIfNecessary(string filePath, string savePath)
const string template = "template";
const string system = "system";
const string systemUrl = "system_url";
const string templateUrl = "template_url";
const string info = "info";
const string devices = "devices";
const string rooms = "rooms";
const string sourceLists = "sourceLists";
const string destinationLists = "destinationLists";
const string cameraLists = "cameraLists";
const string audioControlPointLists = "audioControlPointLists";
const string tieLines = "tieLines";
const string joinMaps = "joinMaps";
const string global = "global";
/// <summary>
/// Reads the config file, checks if it needs a merge, merges and saves, then returns the merged Object.
/// </summary>
/// <returns>JObject of config file</returns>
public static void ReadAndMergeFileIfNecessary(string filePath, string savePath)
{
try
{
if (!File.Exists(filePath))
{
Debug.Console(1, Debug.ErrorLogLevel.Error,
Debug.LogError(
"ERROR: Configuration file not present. Please load file to {0} and reset program", filePath);
}
using (StreamReader fs = new StreamReader(filePath))
{
var jsonObj = JObject.Parse(fs.ReadToEnd());
if(jsonObj["template"] != null && jsonObj["system"] != null)
if(jsonObj[template] != null && jsonObj[system] != null)
{
// it's a double-config, merge it.
var merged = MergeConfigs(jsonObj);
if (jsonObj["system_url"] != null)
if (jsonObj[systemUrl] != null)
{
merged["systemUrl"] = jsonObj["system_url"].Value<string>();
merged[systemUrl] = jsonObj[systemUrl].Value<string>();
}
if (jsonObj["template_url"] != null)
if (jsonObj[templateUrl] != null)
{
merged["templateUrl"] = jsonObj["template_url"].Value<string>();
merged[templateUrl] = jsonObj[templateUrl].Value<string>();
}
jsonObj = merged;
@@ -77,62 +96,62 @@ namespace PepperDash.Core.Config
var merged = new JObject();
// Put together top-level objects
if (system["info"] != null)
merged.Add("info", Merge(template["info"], system["info"], "infO"));
if (system[info] != null)
merged.Add(info, Merge(template[info], system[info], info));
else
merged.Add("info", template["info"]);
merged.Add(info, template[info]);
merged.Add("devices", MergeArraysOnTopLevelProperty(template["devices"] as JArray,
system["devices"] as JArray, "key", "devices"));
merged.Add(devices, MergeArraysOnTopLevelProperty(template[devices] as JArray,
system[devices] as JArray, "key", devices));
if (system["rooms"] == null)
merged.Add("rooms", template["rooms"]);
if (system[rooms] == null)
merged.Add(rooms, template[rooms]);
else
merged.Add("rooms", MergeArraysOnTopLevelProperty(template["rooms"] as JArray,
system["rooms"] as JArray, "key", "rooms"));
merged.Add(rooms, MergeArraysOnTopLevelProperty(template[rooms] as JArray,
system[rooms] as JArray, "key", rooms));
if (system["sourceLists"] == null)
merged.Add("sourceLists", template["sourceLists"]);
if (system[sourceLists] == null)
merged.Add(sourceLists, template[sourceLists]);
else
merged.Add("sourceLists", Merge(template["sourceLists"], system["sourceLists"], "sourceLists"));
merged.Add(sourceLists, Merge(template[sourceLists], system[sourceLists], sourceLists));
if (system["destinationLists"] == null)
merged.Add("destinationLists", template["destinationLists"]);
if (system[destinationLists] == null)
merged.Add(destinationLists, template[destinationLists]);
else
merged.Add("destinationLists",
Merge(template["destinationLists"], system["destinationLists"], "destinationLists"));
merged.Add(destinationLists,
Merge(template[destinationLists], system[destinationLists], destinationLists));
if (system["cameraLists"] == null)
merged.Add("cameraLists", template["cameraLists"]);
if (system[cameraLists] == null)
merged.Add(cameraLists, template[cameraLists]);
else
merged.Add("cameraLists", Merge(template["cameraLists"], system["cameraLists"], "cameraLists"));
merged.Add(cameraLists, Merge(template[cameraLists], system[cameraLists], cameraLists));
if (system["audioControlPointLists"] == null)
merged.Add("audioControlPointLists", template["audioControlPointLists"]);
if (system[audioControlPointLists] == null)
merged.Add(audioControlPointLists, template[audioControlPointLists]);
else
merged.Add("audioControlPointLists",
Merge(template["audioControlPointLists"], system["audioControlPointLists"], "audioControlPointLists"));
merged.Add(audioControlPointLists,
Merge(template[audioControlPointLists], system[audioControlPointLists], audioControlPointLists));
// Template tie lines take precedence. Config tool doesn't do them at system
// level anyway...
if (template["tieLines"] != null)
merged.Add("tieLines", template["tieLines"]);
else if (system["tieLines"] != null)
merged.Add("tieLines", system["tieLines"]);
if (template[tieLines] != null)
merged.Add(tieLines, template[tieLines]);
else if (system[tieLines] != null)
merged.Add(tieLines, system[tieLines]);
else
merged.Add("tieLines", new JArray());
merged.Add(tieLines, new JArray());
if (template["joinMaps"] != null)
merged.Add("joinMaps", template["joinMaps"]);
if (template[joinMaps] != null)
merged.Add(joinMaps, template[joinMaps]);
else
merged.Add("joinMaps", new JObject());
merged.Add(joinMaps, new JObject());
if (system["global"] != null)
merged.Add("global", Merge(template["global"], system["global"], "global"));
if (system[global] != null)
merged.Add(global, Merge(template[global], system[global], global));
else
merged.Add("global", template["global"]);
merged.Add(global, template[global]);
//Debug.Console(2, "MERGED CONFIG RESULT: \x0d\x0a{0}", merged);
return merged;
@@ -228,7 +247,7 @@ namespace PepperDash.Core.Config
}
catch (Exception e)
{
Debug.Console(1, Debug.ErrorLogLevel.Warning, "Cannot merge items at path {0}: \r{1}", propPath, e);
Debug.LogError($"Cannot merge items at path {propPath}: \r{e}");
}
}
}

View File

@@ -162,7 +162,7 @@ namespace PepperDash.Core.JsonToSimpl
/// <returns></returns>
public static JObject ParseObject(string json)
{
#if NET8_0
#if NET6_0
using (var reader = new JsonTextReader(new System.IO.StringReader(json)))
#else
using (var reader = new JsonTextReader(new Crestron.SimplSharp.CrestronIO.StringReader(json)))
@@ -186,7 +186,7 @@ namespace PepperDash.Core.JsonToSimpl
/// </summary>
public static JArray ParseArray(string json)
{
#if NET8_0
#if NET6_0
using (var reader = new JsonTextReader(new System.IO.StringReader(json)))
#else
using (var reader = new JsonTextReader(new Crestron.SimplSharp.CrestronIO.StringReader(json)))

View File

@@ -1,4 +1,9 @@
using Crestron.SimplSharp;
using System;
using System.Collections.Generic;
using System.Net;
using System.Reflection;
using System.Text.RegularExpressions;
using Crestron.SimplSharp;
using Crestron.SimplSharp.CrestronDataStore;
using Crestron.SimplSharp.CrestronIO;
using Crestron.SimplSharp.CrestronLogger;
@@ -11,15 +16,10 @@ using Serilog.Events;
using Serilog.Formatting.Compact;
using Serilog.Formatting.Json;
using Serilog.Templates;
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Text.RegularExpressions;
namespace PepperDash.Core
{
/// <summary>
/// Contains debug commands for use in various situations
/// </summary>
public static class Debug
{
@@ -40,21 +40,27 @@ namespace PepperDash.Core
private static ILogger _logger;
private static readonly LoggingLevelSwitch _consoleLoggingLevelSwitch;
private static readonly LoggingLevelSwitch _consoleLogLevelSwitch;
private static readonly LoggingLevelSwitch _websocketLoggingLevelSwitch;
private static readonly LoggingLevelSwitch _websocketLogLevelSwitch;
private static readonly LoggingLevelSwitch _errorLogLevelSwitch;
private static readonly LoggingLevelSwitch _fileLevelSwitch;
private static readonly LoggingLevelSwitch _fileLogLevelSwitch;
/// <summary>
/// Gets the minimum log level for the websocket sink.
/// </summary>
public static LogEventLevel WebsocketMinimumLogLevel
{
get { return _websocketLoggingLevelSwitch.MinimumLevel; }
get { return _websocketLogLevelSwitch.MinimumLevel; }
}
private static readonly DebugWebsocketSink _websocketSink;
/// <summary>
/// Gets the websocket sink for debug logging.
/// </summary>
public static DebugWebsocketSink WebsocketSink
{
get { return _websocketSink; }
@@ -91,29 +97,33 @@ namespace PepperDash.Core
private const int SaveTimeoutMs = 30000;
/// <summary>
/// Indicates whether the system is running on an appliance.
/// </summary>
public static bool IsRunningOnAppliance = CrestronEnvironment.DevicePlatform == eDevicePlatform.Appliance;
/// <summary>
/// Gets or sets the PepperDashCoreVersion
/// </summary>
public static string PepperDashCoreVersion { get; private set; }
public static string PepperDashCoreVersion { get; private set; }
private static CTimer _saveTimer;
/// <summary>
/// When true, the IncludedExcludedKeys dict will contain keys to include.
/// When false (default), IncludedExcludedKeys will contain keys to exclude.
/// </summary>
private static bool _excludeAllMode;
/// <summary>
/// When true, the IncludedExcludedKeys dict will contain keys to include.
/// When false (default), IncludedExcludedKeys will contain keys to exclude.
/// </summary>
private static bool _excludeAllMode;
//static bool ExcludeNoKeyMessages;
private static readonly Dictionary<string, object> IncludedExcludedKeys;
private static readonly Dictionary<string, object> IncludedExcludedKeys;
private static readonly LoggerConfiguration _defaultLoggerConfiguration;
private static LoggerConfiguration _loggerConfiguration;
/// <summary>
/// Gets the logger configuration for the debug logging.
/// </summary>
public static LoggerConfiguration LoggerConfiguration => _loggerConfiguration;
static Debug()
@@ -128,13 +138,13 @@ namespace PepperDash.Core
var defaultFileLogLevel = GetStoredLogEventLevel(FileLevelStoreKey);
_consoleLoggingLevelSwitch = new LoggingLevelSwitch(initialMinimumLevel: defaultConsoleLevel);
_consoleLogLevelSwitch = new LoggingLevelSwitch(initialMinimumLevel: defaultConsoleLevel);
_websocketLoggingLevelSwitch = new LoggingLevelSwitch(initialMinimumLevel: defaultWebsocketLevel);
_websocketLogLevelSwitch = new LoggingLevelSwitch(initialMinimumLevel: defaultWebsocketLevel);
_errorLogLevelSwitch = new LoggingLevelSwitch(initialMinimumLevel: defaultErrorLogLevel);
_fileLevelSwitch = new LoggingLevelSwitch(initialMinimumLevel: defaultFileLogLevel);
_fileLogLevelSwitch = new LoggingLevelSwitch(initialMinimumLevel: defaultFileLogLevel);
_websocketSink = new DebugWebsocketSink(new JsonFormatter(renderMessage: true));
@@ -144,7 +154,7 @@ namespace PepperDash.Core
CrestronConsole.PrintLine($"Saving log files to {logFilePath}");
var errorLogTemplate = CrestronEnvironment.DevicePlatform == eDevicePlatform.Appliance
var errorLogTemplate = CrestronEnvironment.DevicePlatform == eDevicePlatform.Appliance
? "{@t:fff}ms [{@l:u4}]{#if Key is not null}[{Key}]{#end} {@m}{#if @x is not null}\r\n{@x}{#end}"
: "[{@t:yyyy-MM-dd HH:mm:ss.fff}][{@l:u4}][{App}]{#if Key is not null}[{Key}]{#end} {@m}{#if @x is not null}\r\n{@x}{#end}";
@@ -152,14 +162,14 @@ namespace PepperDash.Core
.MinimumLevel.Verbose()
.Enrich.FromLogContext()
.Enrich.With(new CrestronEnricher())
.WriteTo.Sink(new DebugConsoleSink(new ExpressionTemplate("[{@t:yyyy-MM-dd HH:mm:ss.fff}][{@l:u4}][{App}]{#if Key is not null}[{Key}]{#end} {@m}{#if @x is not null}\r\n{@x}{#end}")), levelSwitch: _consoleLoggingLevelSwitch)
.WriteTo.Sink(_websocketSink, levelSwitch: _websocketLoggingLevelSwitch)
.WriteTo.Sink(new DebugConsoleSink(new ExpressionTemplate("[{@t:yyyy-MM-dd HH:mm:ss.fff}][{@l:u4}][{App}]{#if Key is not null}[{Key}]{#end} {@m}{#if @x is not null}\r\n{@x}{#end}")), levelSwitch: _consoleLogLevelSwitch)
.WriteTo.Sink(_websocketSink, levelSwitch: _websocketLogLevelSwitch)
.WriteTo.Sink(new DebugErrorLogSink(new ExpressionTemplate(errorLogTemplate)), levelSwitch: _errorLogLevelSwitch)
.WriteTo.File(new RenderedCompactJsonFormatter(), logFilePath,
.WriteTo.File(new RenderedCompactJsonFormatter(), logFilePath,
rollingInterval: RollingInterval.Day,
restrictedToMinimumLevel: LogEventLevel.Debug,
retainedFileCountLimit: CrestronEnvironment.DevicePlatform == eDevicePlatform.Appliance ? 30 : 60,
levelSwitch: _fileLevelSwitch
levelSwitch: _fileLogLevelSwitch
);
try
@@ -193,27 +203,27 @@ namespace PepperDash.Core
CrestronConsole.PrintLine(msg);
LogMessage(LogEventLevel.Information,msg);
LogMessage(LogEventLevel.Information, msg);
IncludedExcludedKeys = new Dictionary<string, object>();
IncludedExcludedKeys = new Dictionary<string, object>();
if (CrestronEnvironment.RuntimeEnvironment == eRuntimeEnvironment.SimplSharpPro)
{
// Add command to console
CrestronConsole.AddNewConsoleCommand(SetDoNotLoadOnNextBootFromConsole, "donotloadonnextboot",
CrestronConsole.AddNewConsoleCommand(SetDoNotLoadOnNextBootFromConsole, "donotloadonnextboot",
"donotloadonnextboot:P [true/false]: Should the application load on next boot", ConsoleAccessLevelEnum.AccessOperator);
CrestronConsole.AddNewConsoleCommand(SetDebugFromConsole, "appdebug",
"appdebug:P [0-5]: Sets the application's console debug message level",
ConsoleAccessLevelEnum.AccessOperator);
CrestronConsole.AddNewConsoleCommand(ShowDebugLog, "appdebuglog",
"appdebuglog:P [all] Use \"all\" for full log.",
"appdebuglog:P [all] Use \"all\" for full log.",
ConsoleAccessLevelEnum.AccessOperator);
CrestronConsole.AddNewConsoleCommand(s => CrestronLogger.Clear(false), "appdebugclear",
"appdebugclear:P Clears the current custom log",
"appdebugclear:P Clears the current custom log",
ConsoleAccessLevelEnum.AccessOperator);
CrestronConsole.AddNewConsoleCommand(SetDebugFilterFromConsole, "appdebugfilter",
"appdebugfilter [params]", ConsoleAccessLevelEnum.AccessOperator);
CrestronConsole.AddNewConsoleCommand(SetDebugFilterFromConsole, "appdebugfilter",
"appdebugfilter [params]", ConsoleAccessLevelEnum.AccessOperator);
}
CrestronEnvironment.ProgramStatusEventHandler += CrestronEnvironment_ProgramStatusEventHandler;
@@ -224,13 +234,16 @@ namespace PepperDash.Core
Level = context.Level;
DoNotLoadConfigOnNextBoot = context.DoNotLoadOnNextBoot;
if(DoNotLoadConfigOnNextBoot)
if (DoNotLoadConfigOnNextBoot)
CrestronConsole.PrintLine(string.Format("Program {0} will not load config after next boot. Use console command go:{0} to load the config manually", InitialParametersClass.ApplicationNumber));
_consoleLoggingLevelSwitch.MinimumLevelChanged += (sender, args) =>
_errorLogLevelSwitch.MinimumLevelChanged += (sender, args) =>
{
LogMessage(LogEventLevel.Information, "Console debug level set to {minimumLevel}", _consoleLoggingLevelSwitch.MinimumLevel);
LogMessage(LogEventLevel.Information, "Error log debug level set to {minimumLevel}", _errorLogLevelSwitch.MinimumLevel);
};
// Set initial error log level based on platform && stored level. If appliance, use stored level, otherwise default to verbose
SetErrorLogMinimumDebugLevel(CrestronEnvironment.DevicePlatform == eDevicePlatform.Appliance ? _errorLogLevelSwitch.MinimumLevel : LogEventLevel.Verbose);
}
/// <summary>
@@ -257,22 +270,28 @@ namespace PepperDash.Core
{
try
{
var result = CrestronDataStoreStatic.GetLocalIntValue(levelStoreKey, out int logLevel);
var result = CrestronDataStoreStatic.GetLocalIntValue(levelStoreKey, out int logLevel);
if (result != CrestronDataStore.CDS_ERROR.CDS_SUCCESS)
{
CrestronConsole.Print($"Unable to retrieve stored log level for {levelStoreKey}.\r\nError: {result}.\r\nSetting level to {LogEventLevel.Information}\r\n");
return LogEventLevel.Information;
CrestronDataStoreStatic.SetLocalIntValue(levelStoreKey, levelStoreKey == ErrorLogLevelStoreKey ? (int)LogEventLevel.Warning : (int)LogEventLevel.Information);
return levelStoreKey == ErrorLogLevelStoreKey ? LogEventLevel.Warning : LogEventLevel.Information;
}
if(logLevel < 0 || logLevel > 5)
if (logLevel < 0 || logLevel > 5)
{
CrestronConsole.PrintLine($"Stored Log level not valid for {levelStoreKey}: {logLevel}. Setting level to {LogEventLevel.Information}");
return LogEventLevel.Information;
}
CrestronConsole.PrintLine($"Stored log level for {levelStoreKey} is {logLevel}");
return (LogEventLevel)logLevel;
} catch (Exception ex)
}
catch (Exception ex)
{
CrestronConsole.PrintLine($"Exception retrieving log level for {levelStoreKey}: {ex.Message}");
return LogEventLevel.Information;
@@ -284,7 +303,7 @@ namespace PepperDash.Core
var assembly = Assembly.GetExecutingAssembly();
var ver =
assembly
.GetCustomAttributes(typeof (AssemblyInformationalVersionAttribute), false);
.GetCustomAttributes(typeof(AssemblyInformationalVersionAttribute), false);
if (ver != null && ver.Length > 0)
{
@@ -335,44 +354,104 @@ namespace PepperDash.Core
if (levelString.Trim() == "?")
{
CrestronConsole.ConsoleCommandResponse(
$@"Used to set the minimum level of debug messages to be printed to the console:
{_logLevels[0]} = 0
{_logLevels[1]} = 1
{_logLevels[2]} = 2
{_logLevels[3]} = 3
{_logLevels[4]} = 4
{_logLevels[5]} = 5");
"Used to set the minimum level of debug messages:\r\n" +
"Usage: appdebug:P [sink] [level]\r\n" +
" sink: console (default), errorlog, file, all\r\n" +
" all: sets all sinks to the specified level\r\n" +
" level: 0-5 or LogEventLevel name\r\n" +
$"{_logLevels[0]} = 0\r\n" +
$"{_logLevels[1]} = 1\r\n" +
$"{_logLevels[2]} = 2\r\n" +
$"{_logLevels[3]} = 3\r\n" +
$"{_logLevels[4]} = 4\r\n" +
$"{_logLevels[5]} = 5");
return;
}
if (string.IsNullOrEmpty(levelString.Trim()))
{
CrestronConsole.ConsoleCommandResponse("AppDebug level = {0}", _consoleLoggingLevelSwitch.MinimumLevel);
CrestronConsole.ConsoleCommandResponse("Console log level = {0}\r\n", _consoleLogLevelSwitch.MinimumLevel);
CrestronConsole.ConsoleCommandResponse("File log level = {0}\r\n", _fileLogLevelSwitch.MinimumLevel);
CrestronConsole.ConsoleCommandResponse("Error log level = {0}\r\n", _errorLogLevelSwitch.MinimumLevel);
return;
}
if(int.TryParse(levelString, out var levelInt))
// Parse tokens: first token is sink (defaults to console), second token is level
var tokens = levelString.Trim().Split(new char[] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries);
string sinkName;
string levelToken;
if (tokens.Length == 1)
{
if(levelInt < 0 || levelInt > 5)
// Single token - assume it's a level for console sink
sinkName = "console";
levelToken = tokens[0];
}
else if (tokens.Length == 2)
{
// Two tokens - first is sink, second is level
sinkName = tokens[0].ToLowerInvariant();
levelToken = tokens[1];
}
else
{
CrestronConsole.ConsoleCommandResponse("Usage: appdebug:P [sink] [level]");
return;
}
// Parse the level using the same logic as before
LogEventLevel level;
if (int.TryParse(levelToken, out var levelInt))
{
if (levelInt < 0 || levelInt > 5)
{
CrestronConsole.ConsoleCommandResponse($"Error: Unable to parse {levelString} to valid log level. If using a number, value must be between 0-5");
CrestronConsole.ConsoleCommandResponse($"Error: Unable to parse {levelToken} to valid log level. If using a number, value must be between 0-5");
return;
}
SetDebugLevel((uint) levelInt);
return;
}
if(Enum.TryParse<LogEventLevel>(levelString, out var levelEnum))
if (!_logLevels.TryGetValue((uint)levelInt, out level))
{
level = LogEventLevel.Information;
CrestronConsole.ConsoleCommandResponse($"{levelInt} not valid. Setting level to {level}");
}
}
else if (Enum.TryParse(levelToken, true, out level))
{
SetDebugLevel(levelEnum);
// Successfully parsed as LogEventLevel enum
}
else
{
CrestronConsole.ConsoleCommandResponse($"Error: Unable to parse {levelToken} to valid log level");
return;
}
CrestronConsole.ConsoleCommandResponse($"Error: Unable to parse {levelString} to valid log level");
}
// Set the level for the specified sink
switch (sinkName)
{
case "console":
SetDebugLevel(level);
break;
case "errorlog":
SetErrorLogMinimumDebugLevel(level);
break;
case "file":
SetFileMinimumDebugLevel(level);
break;
case "all":
SetDebugLevel(level);
SetErrorLogMinimumDebugLevel(level);
SetFileMinimumDebugLevel(level);
break;
default:
CrestronConsole.ConsoleCommandResponse($"Error: Unknown sink '{sinkName}'. Valid sinks: console, errorlog, file");
break;
}
}
catch
{
CrestronConsole.ConsoleCommandResponse("Usage: appdebug:P [0-5]");
CrestronConsole.ConsoleCommandResponse("Usage: appdebug:P [sink] [level]");
}
}
@@ -385,7 +464,7 @@ namespace PepperDash.Core
/// </summary>
public static void SetDebugLevel(uint level)
{
if(!_logLevels.TryGetValue(level, out var logLevel))
if (!_logLevels.TryGetValue(level, out var logLevel))
{
logLevel = LogEventLevel.Information;
@@ -402,14 +481,14 @@ namespace PepperDash.Core
/// </summary>
public static void SetDebugLevel(LogEventLevel level)
{
_consoleLoggingLevelSwitch.MinimumLevel = level;
_consoleLogLevelSwitch.MinimumLevel = level;
CrestronConsole.ConsoleCommandResponse("[Application {0}], Debug level set to {1}\r\n",
InitialParametersClass.ApplicationNumber, _consoleLoggingLevelSwitch.MinimumLevel);
CrestronConsole.ConsoleCommandResponse("[Application {0}] Debug level set to {1}\r\n",
InitialParametersClass.ApplicationNumber, _consoleLogLevelSwitch.MinimumLevel);
CrestronConsole.ConsoleCommandResponse($"Storing level {level}:{(int) level}");
CrestronConsole.ConsoleCommandResponse($"Storing level {level}:{(int)level}");
var err = CrestronDataStoreStatic.SetLocalIntValue(LevelStoreKey, (int) level);
var err = CrestronDataStoreStatic.SetLocalIntValue(LevelStoreKey, (int)level);
CrestronConsole.ConsoleCommandResponse($"Store result: {err}:{(int)level}");
@@ -422,14 +501,14 @@ namespace PepperDash.Core
/// </summary>
public static void SetWebSocketMinimumDebugLevel(LogEventLevel level)
{
_websocketLoggingLevelSwitch.MinimumLevel = level;
_websocketLogLevelSwitch.MinimumLevel = level;
var err = CrestronDataStoreStatic.SetLocalUintValue(WebSocketLevelStoreKey, (uint) level);
var err = CrestronDataStoreStatic.SetLocalUintValue(WebSocketLevelStoreKey, (uint)level);
if (err != CrestronDataStore.CDS_ERROR.CDS_SUCCESS)
LogMessage(LogEventLevel.Information, "Error saving websocket debug level setting: {erro}", err);
LogMessage(LogEventLevel.Information, "Websocket debug level set to {0}", _websocketLoggingLevelSwitch.MinimumLevel);
LogMessage(LogEventLevel.Information, "Websocket debug level set to {0}", _websocketLogLevelSwitch.MinimumLevel);
}
/// <summary>
@@ -439,12 +518,17 @@ namespace PepperDash.Core
{
_errorLogLevelSwitch.MinimumLevel = level;
var err = CrestronDataStoreStatic.SetLocalUintValue(ErrorLogLevelStoreKey, (uint)level);
CrestronConsole.ConsoleCommandResponse("[Application {0}] Error log level set to {1}\r\n",
InitialParametersClass.ApplicationNumber, _errorLogLevelSwitch.MinimumLevel);
CrestronConsole.ConsoleCommandResponse($"Storing level {level}:{(int)level}");
var err = CrestronDataStoreStatic.SetLocalIntValue(ErrorLogLevelStoreKey, (int)level);
CrestronConsole.ConsoleCommandResponse($"Store result: {err}:{(int)level}");
if (err != CrestronDataStore.CDS_ERROR.CDS_SUCCESS)
LogMessage(LogEventLevel.Information, "Error saving Error Log debug level setting: {error}", err);
LogMessage(LogEventLevel.Information, "Error log debug level set to {0}", _websocketLoggingLevelSwitch.MinimumLevel);
CrestronConsole.PrintLine($"Error saving error log debug level setting: {err}");
}
/// <summary>
@@ -452,14 +536,19 @@ namespace PepperDash.Core
/// </summary>
public static void SetFileMinimumDebugLevel(LogEventLevel level)
{
_errorLogLevelSwitch.MinimumLevel = level;
_fileLogLevelSwitch.MinimumLevel = level;
var err = CrestronDataStoreStatic.SetLocalUintValue(ErrorLogLevelStoreKey, (uint)level);
CrestronConsole.ConsoleCommandResponse("[Application {0}] File log level set to {1}\r\n",
InitialParametersClass.ApplicationNumber, _fileLogLevelSwitch.MinimumLevel);
CrestronConsole.ConsoleCommandResponse($"Storing level {level}:{(int)level}");
var err = CrestronDataStoreStatic.SetLocalIntValue(FileLevelStoreKey, (int)level);
CrestronConsole.ConsoleCommandResponse($"Store result: {err}:{(int)level}");
if (err != CrestronDataStore.CDS_ERROR.CDS_SUCCESS)
LogMessage(LogEventLevel.Information, "Error saving File debug level setting: {error}", err);
LogMessage(LogEventLevel.Information, "File debug level set to {0}", _websocketLoggingLevelSwitch.MinimumLevel);
CrestronConsole.PrintLine($"Error saving file debug level setting: {err}");
}
/// <summary>
@@ -491,83 +580,83 @@ namespace PepperDash.Core
/// Callback for console command
/// </summary>
/// <param name="items"></param>
/// <summary>
/// SetDebugFilterFromConsole method
/// </summary>
public static void SetDebugFilterFromConsole(string items)
{
var str = items.Trim();
if (str == "?")
{
CrestronConsole.ConsoleCommandResponse("Usage:\r APPDEBUGFILTER key1 key2 key3....\r " +
"+all: at beginning puts filter into 'default include' mode\r" +
" All keys that follow will be excluded from output.\r" +
"-all: at beginning puts filter into 'default exclude all' mode.\r" +
" All keys that follow will be the only keys that are shown\r" +
"+nokey: Enables messages with no key (default)\r" +
"-nokey: Disables messages with no key.\r" +
"(nokey settings are independent of all other settings)");
return;
}
var keys = Regex.Split(str, @"\s*");
foreach (var keyToken in keys)
{
var lkey = keyToken.ToLower();
if (lkey == "+all")
{
IncludedExcludedKeys.Clear();
_excludeAllMode = false;
}
else if (lkey == "-all")
{
IncludedExcludedKeys.Clear();
_excludeAllMode = true;
}
//else if (lkey == "+nokey")
//{
// ExcludeNoKeyMessages = false;
//}
//else if (lkey == "-nokey")
//{
// ExcludeNoKeyMessages = true;
//}
else
{
string key;
if (lkey.StartsWith("-"))
{
key = lkey.Substring(1);
// if in exclude all mode, we need to remove this from the inclusions
if (_excludeAllMode)
{
if (IncludedExcludedKeys.ContainsKey(key))
IncludedExcludedKeys.Remove(key);
}
// otherwise include all mode, add to the exclusions
else
{
IncludedExcludedKeys[key] = new object();
}
}
else if (lkey.StartsWith("+"))
{
key = lkey.Substring(1);
// if in exclude all mode, we need to add this as inclusion
if (_excludeAllMode)
{
/// <summary>
/// SetDebugFilterFromConsole method
/// </summary>
public static void SetDebugFilterFromConsole(string items)
{
var str = items.Trim();
if (str == "?")
{
CrestronConsole.ConsoleCommandResponse("Usage:\r APPDEBUGFILTER key1 key2 key3....\r " +
"+all: at beginning puts filter into 'default include' mode\r" +
" All keys that follow will be excluded from output.\r" +
"-all: at beginning puts filter into 'default exclude all' mode.\r" +
" All keys that follow will be the only keys that are shown\r" +
"+nokey: Enables messages with no key (default)\r" +
"-nokey: Disables messages with no key.\r" +
"(nokey settings are independent of all other settings)");
return;
}
var keys = Regex.Split(str, @"\s*");
foreach (var keyToken in keys)
{
var lkey = keyToken.ToLower();
if (lkey == "+all")
{
IncludedExcludedKeys.Clear();
_excludeAllMode = false;
}
else if (lkey == "-all")
{
IncludedExcludedKeys.Clear();
_excludeAllMode = true;
}
//else if (lkey == "+nokey")
//{
// ExcludeNoKeyMessages = false;
//}
//else if (lkey == "-nokey")
//{
// ExcludeNoKeyMessages = true;
//}
else
{
string key;
if (lkey.StartsWith("-"))
{
key = lkey.Substring(1);
// if in exclude all mode, we need to remove this from the inclusions
if (_excludeAllMode)
{
if (IncludedExcludedKeys.ContainsKey(key))
IncludedExcludedKeys.Remove(key);
}
// otherwise include all mode, add to the exclusions
else
{
IncludedExcludedKeys[key] = new object();
}
}
else if (lkey.StartsWith("+"))
{
key = lkey.Substring(1);
// if in exclude all mode, we need to add this as inclusion
if (_excludeAllMode)
{
IncludedExcludedKeys[key] = new object();
}
// otherwise include all mode, remove this from exclusions
else
{
if (IncludedExcludedKeys.ContainsKey(key))
IncludedExcludedKeys.Remove(key);
}
}
}
}
}
IncludedExcludedKeys[key] = new object();
}
// otherwise include all mode, remove this from exclusions
else
{
if (IncludedExcludedKeys.ContainsKey(key))
IncludedExcludedKeys.Remove(key);
}
}
}
}
}
@@ -595,7 +684,7 @@ namespace PepperDash.Core
public static object GetDeviceDebugSettingsForKey(string deviceKey)
{
return _contexts.GetDebugSettingsForKey(deviceKey);
}
}
/// <summary>
/// Sets the flag to prevent application starting on next boot
@@ -654,9 +743,15 @@ namespace PepperDash.Core
}
}
/// <summary>
/// Logs a message at the specified log level.
/// </summary>
/// <param name="level">Level to log at</param>
/// <param name="message">Message template</param>
/// <param name="args">Args to put into message template</param>
public static void LogMessage(LogEventLevel level, string message, params object[] args)
{
_logger.Write(level, message, args);
_logger.Write(level, message, args);
}
/// <summary>
@@ -692,7 +787,7 @@ namespace PepperDash.Core
/// </summary>
public static void LogVerbose(IKeyed keyed, string message, params object[] args)
{
using(LogContext.PushProperty("Key", keyed?.Key))
using (LogContext.PushProperty("Key", keyed?.Key))
{
_logger.Write(LogEventLevel.Verbose, message, args);
}
@@ -703,7 +798,7 @@ namespace PepperDash.Core
/// </summary>
public static void LogVerbose(Exception ex, IKeyed keyed, string message, params object[] args)
{
using(LogContext.PushProperty("Key", keyed?.Key))
using (LogContext.PushProperty("Key", keyed?.Key))
{
_logger.Write(LogEventLevel.Verbose, ex, message, args);
}
@@ -722,7 +817,7 @@ namespace PepperDash.Core
/// </summary>
public static void LogVerbose(Exception ex, string message, params object[] args)
{
_logger.Write(LogEventLevel.Verbose, ex, null, message, args);
_logger.Write(LogEventLevel.Verbose, ex, message, args);
}
/// <summary>
@@ -798,7 +893,7 @@ namespace PepperDash.Core
/// </summary>
public static void LogInformation(Exception ex, string message, params object[] args)
{
_logger.Write(LogEventLevel.Information, ex, null, message, args);
_logger.Write(LogEventLevel.Information, ex, message, args);
}
/// <summary>
@@ -836,7 +931,7 @@ namespace PepperDash.Core
/// </summary>
public static void LogWarning(Exception ex, string message, params object[] args)
{
_logger.Write(LogEventLevel.Warning, ex, null, message, args);
_logger.Write(LogEventLevel.Warning, ex, message, args);
}
/// <summary>
@@ -874,7 +969,7 @@ namespace PepperDash.Core
/// </summary>
public static void LogError(Exception ex, string message, params object[] args)
{
_logger.Write(LogEventLevel.Error, ex, null, message, args);
_logger.Write(LogEventLevel.Error, ex, message, args);
}
/// <summary>
@@ -912,7 +1007,7 @@ namespace PepperDash.Core
/// </summary>
public static void LogFatal(Exception ex, string message, params object[] args)
{
_logger.Write(LogEventLevel.Fatal, ex, null, message, args);
_logger.Write(LogEventLevel.Fatal, ex, message, args);
}
#endregion
@@ -923,7 +1018,7 @@ namespace PepperDash.Core
if (!_logLevels.ContainsKey(level)) return;
var logLevel = _logLevels[level];
LogMessage(logLevel, format, items);
}
@@ -978,7 +1073,7 @@ namespace PepperDash.Core
[Obsolete("Use LogMessage methods, Will be removed in 2.2.0 and later versions")]
public static void Console(uint level, IKeyed dev, ErrorLogLevel errorLogLevel,
string format, params object[] items)
{
{
LogMessage(level, dev, format, items);
}
@@ -1122,7 +1217,7 @@ namespace PepperDash.Core
return string.Format(@"\user\debugSettings\program{0}", InitialParametersClass.ApplicationNumber);
}
return string.Format("{0}{1}user{1}debugSettings{1}{2}.json",Directory.GetApplicationRootDirectory(), Path.DirectorySeparatorChar, InitialParametersClass.RoomId);
return string.Format("{0}{1}user{1}debugSettings{1}{2}.json", Directory.GetApplicationRootDirectory(), Path.DirectorySeparatorChar, InitialParametersClass.RoomId);
}
/// <summary>
@@ -1133,15 +1228,15 @@ namespace PepperDash.Core
/// <summary>
/// Error
/// </summary>
Error,
Error,
/// <summary>
/// Warning
/// </summary>
Warning,
Warning,
/// <summary>
/// Notice
/// </summary>
Notice,
Notice,
/// <summary>
/// None
/// </summary>

View File

@@ -1,5 +1,5 @@
using Serilog.Events;
using System;
using System;
using Serilog.Events;
using Log = PepperDash.Core.Debug;
namespace PepperDash.Core.Logging
@@ -11,7 +11,7 @@ namespace PepperDash.Core.Logging
/// </summary>
public static void LogException(this IKeyed device, Exception ex, string message, params object[] args)
{
Log.LogMessage(ex, message, device, args);
Log.LogMessage(ex, message, device: device, args);
}
/// <summary>
@@ -19,7 +19,7 @@ namespace PepperDash.Core.Logging
/// </summary>
public static void LogVerbose(this IKeyed device, Exception ex, string message, params object[] args)
{
Log.LogMessage(LogEventLevel.Verbose, ex, message, device, args);
Log.LogVerbose(ex, device, message, args);
}
/// <summary>
@@ -27,7 +27,7 @@ namespace PepperDash.Core.Logging
/// </summary>
public static void LogVerbose(this IKeyed device, string message, params object[] args)
{
Log.LogMessage(LogEventLevel.Verbose, device, message, args);
Log.LogVerbose(device, message, args);
}
/// <summary>
@@ -35,7 +35,7 @@ namespace PepperDash.Core.Logging
/// </summary>
public static void LogDebug(this IKeyed device, Exception ex, string message, params object[] args)
{
Log.LogMessage(LogEventLevel.Debug, ex, message, device, args);
Log.LogDebug(ex, device, message, args);
}
/// <summary>
@@ -43,7 +43,7 @@ namespace PepperDash.Core.Logging
/// </summary>
public static void LogDebug(this IKeyed device, string message, params object[] args)
{
Log.LogMessage(LogEventLevel.Debug, device, message, args);
Log.LogDebug(device, message, args);
}
/// <summary>
@@ -51,7 +51,7 @@ namespace PepperDash.Core.Logging
/// </summary>
public static void LogInformation(this IKeyed device, Exception ex, string message, params object[] args)
{
Log.LogMessage(LogEventLevel.Information, ex, message, device, args);
Log.LogInformation(ex, device, message, args);
}
/// <summary>
@@ -59,7 +59,7 @@ namespace PepperDash.Core.Logging
/// </summary>
public static void LogInformation(this IKeyed device, string message, params object[] args)
{
Log.LogMessage(LogEventLevel.Information, device, message, args);
Log.LogInformation(device, message, args);
}
/// <summary>
@@ -67,7 +67,7 @@ namespace PepperDash.Core.Logging
/// </summary>
public static void LogWarning(this IKeyed device, Exception ex, string message, params object[] args)
{
Log.LogMessage(LogEventLevel.Warning, ex, message, device, args);
Log.LogWarning(ex, device, message, args);
}
/// <summary>
@@ -75,7 +75,7 @@ namespace PepperDash.Core.Logging
/// </summary>
public static void LogWarning(this IKeyed device, string message, params object[] args)
{
Log.LogMessage(LogEventLevel.Warning, device, message, args);
Log.LogWarning(device, message, args);
}
/// <summary>
@@ -83,7 +83,7 @@ namespace PepperDash.Core.Logging
/// </summary>
public static void LogError(this IKeyed device, Exception ex, string message, params object[] args)
{
Log.LogMessage(LogEventLevel.Error, ex, message, device, args);
Log.LogError(ex, device, message, args);
}
/// <summary>
@@ -91,7 +91,7 @@ namespace PepperDash.Core.Logging
/// </summary>
public static void LogError(this IKeyed device, string message, params object[] args)
{
Log.LogMessage(LogEventLevel.Error, device, message, args);
Log.LogError(device, message, args);
}
/// <summary>
@@ -99,7 +99,7 @@ namespace PepperDash.Core.Logging
/// </summary>
public static void LogFatal(this IKeyed device, Exception ex, string message, params object[] args)
{
Log.LogMessage(LogEventLevel.Fatal, ex, message, device, args);
Log.LogFatal(ex, device, message, args);
}
/// <summary>
@@ -107,7 +107,7 @@ namespace PepperDash.Core.Logging
/// </summary>
public static void LogFatal(this IKeyed device, string message, params object[] args)
{
Log.LogMessage(LogEventLevel.Fatal, device, message, args);
Log.LogFatal(device, message, args);
}
}
}

View File

@@ -5,7 +5,7 @@
<PropertyGroup>
<RootNamespace>PepperDash.Core</RootNamespace>
<AssemblyName>PepperDashCore</AssemblyName>
<TargetFramework>net8</TargetFramework>
<TargetFramework>net472</TargetFramework>
<Deterministic>true</Deterministic>
<NeutralLanguage>en</NeutralLanguage>
<OutputPath>bin\$(Configuration)\</OutputPath>
@@ -44,7 +44,6 @@
<ItemGroup>
<PackageReference Include="BouncyCastle.Cryptography" Version="2.4.0" />
<PackageReference Include="Crestron.SimplSharp.SDK.Library" Version="2.21.90" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="Serilog" Version="3.1.1" />
<PackageReference Include="Serilog.Expressions" Version="4.0.0" />
<PackageReference Include="Serilog.Formatting.Compact" Version="2.0.0" />
@@ -53,6 +52,9 @@
<PackageReference Include="SSH.NET" Version="2024.2.0" />
<PackageReference Include="WebSocketSharp" Version="1.0.3-rc11" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net6'">
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
</ItemGroup>
<ItemGroup>
<Compile Remove="Comm\._GenericSshClient.cs" />
<Compile Remove="Comm\._GenericTcpIpClient.cs" />

View File

@@ -2,17 +2,14 @@
using System;
using System.Collections.Generic;
using System.Reflection;
using Crestron.SimplSharp;
using Crestron.SimplSharpPro;
using Crestron.SimplSharpPro.DeviceSupport;
using Crestron.SimplSharpPro.EthernetCommunication;
using Newtonsoft.Json;
using PepperDash.Essentials.Core.Communications;
using PepperDash.Core;
using PepperDash.Essentials.Core.Config;
using PepperDash.Essentials.Core.Devices;
using PepperDash.Essentials.Core.JoinMaps;
using PepperDash.Essentials.Core.Monitoring;
using Serilog.Events;
//using PepperDash.Essentials.Devices.Common.Cameras;
@@ -356,22 +353,22 @@ namespace PepperDash.Essentials.Core.Bridges
/// </summary>
public class EiscApiPropertiesConfig
{
[JsonProperty("control")]
/// <summary>
/// Gets or sets the Control
/// </summary>
[JsonProperty("control")]
public EssentialsControlPropertiesConfig Control { get; set; }
[JsonProperty("devices")]
/// <summary>
/// Gets or sets the Devices
/// </summary>
[JsonProperty("devices")]
public List<ApiDevicePropertiesConfig> Devices { get; set; }
[JsonProperty("rooms")]
/// <summary>
/// Gets or sets the Rooms
/// </summary>
[JsonProperty("rooms")]
public List<ApiRoomPropertiesConfig> Rooms { get; set; }
@@ -380,22 +377,22 @@ namespace PepperDash.Essentials.Core.Bridges
/// </summary>
public class ApiDevicePropertiesConfig
{
[JsonProperty("deviceKey")]
/// <summary>
/// Gets or sets the DeviceKey
/// </summary>
[JsonProperty("deviceKey")]
public string DeviceKey { get; set; }
[JsonProperty("joinStart")]
/// <summary>
/// Gets or sets the JoinStart
/// </summary>
[JsonProperty("joinStart")]
public uint JoinStart { get; set; }
[JsonProperty("joinMapKey")]
/// <summary>
/// Gets or sets the JoinMapKey
/// </summary>
[JsonProperty("joinMapKey")]
public string JoinMapKey { get; set; }
}
@@ -404,22 +401,22 @@ namespace PepperDash.Essentials.Core.Bridges
/// </summary>
public class ApiRoomPropertiesConfig
{
[JsonProperty("roomKey")]
/// <summary>
/// Gets or sets the RoomKey
/// </summary>
[JsonProperty("roomKey")]
public string RoomKey { get; set; }
[JsonProperty("joinStart")]
/// <summary>
/// Gets or sets the JoinStart
/// </summary>
[JsonProperty("joinStart")]
public uint JoinStart { get; set; }
[JsonProperty("joinMapKey")]
/// <summary>
/// Gets or sets the JoinMapKey
/// </summary>
[JsonProperty("joinMapKey")]
public string JoinMapKey { get; set; }
}

View File

@@ -1,4 +1,4 @@
using PepperDash.Essentials.Core.Devices;
using PepperDash.Core;
using Serilog.Events;
//using PepperDash.Essentials.Devices.Common.Cameras;

View File

@@ -7,6 +7,13 @@ namespace PepperDash.Essentials.Core.Bridges
/// </summary>
public interface IBridgeAdvanced
{
/// <summary>
/// Links the bridge to the API using the provided trilist, join start, join map key, and bridge.
/// </summary>
/// <param name="trilist">The trilist to link to.</param>
/// <param name="joinStart">The starting join number.</param>
/// <param name="joinMapKey">The key for the join map.</param>
/// <param name="bridge">The EISC API bridge.</param>
void LinkToApi(BasicTriList trilist, uint joinStart, string joinMapKey, EiscApiAdvanced bridge);
}
}

View File

@@ -1,7 +1,6 @@
using PepperDash.Essentials.Core.JoinMaps;
using System;
namespace PepperDash.Essentials.Core.Bridges.JoinMaps
namespace PepperDash.Essentials.Core.Bridges
{
/// <summary>
/// Represents a AirMediaControllerJoinMap

View File

@@ -1,7 +1,6 @@
using PepperDash.Essentials.Core.JoinMaps;
using System;
using System;
namespace PepperDash.Essentials.Core.Bridges.JoinMaps
namespace PepperDash.Essentials.Core.Bridges
{
/// <summary>
/// Represents a AppleTvJoinMap

View File

@@ -1,7 +1,6 @@
using PepperDash.Essentials.Core.JoinMaps;
using System;
using System;
namespace PepperDash.Essentials.Core.Bridges.JoinMaps
namespace PepperDash.Essentials.Core.Bridges
{
/// <summary>
/// Represents a C2nRthsControllerJoinMap

View File

@@ -1,7 +1,6 @@
using PepperDash.Essentials.Core.JoinMaps;
using System;
using System;
namespace PepperDash.Essentials.Core.Bridges.JoinMaps
namespace PepperDash.Essentials.Core.Bridges
{
/// <summary>
/// Represents a CameraControllerJoinMap

View File

@@ -1,7 +1,6 @@
using PepperDash.Essentials.Core.JoinMaps;
using System;
using System;
namespace PepperDash.Essentials.Core.Bridges.JoinMaps
namespace PepperDash.Essentials.Core.Bridges
{
/// <summary>
/// Represents a CenOdtOccupancySensorBaseJoinMap

View File

@@ -1,7 +1,6 @@
using PepperDash.Essentials.Core.JoinMaps;
using System;
using System;
namespace PepperDash.Essentials.Core.Bridges.JoinMaps
namespace PepperDash.Essentials.Core.Bridges
{
/// <summary>
/// Represents a DisplayControllerJoinMap

View File

@@ -1,8 +1,6 @@
using PepperDash.Essentials.Core.JoinMaps;
using System;
using System;
namespace PepperDash.Essentials.Core.Bridges.JoinMaps
{
namespace PepperDash.Essentials.Core.Bridges {
/// <summary>
/// Represents a DmBladeChassisControllerJoinMap
/// </summary>

View File

@@ -1,7 +1,6 @@
using PepperDash.Essentials.Core.JoinMaps;
using System;
namespace PepperDash.Essentials.Core.Bridges.JoinMaps
namespace PepperDash.Essentials.Core.Bridges
{
/// <summary>
/// Represents a DmChassisControllerJoinMap

View File

@@ -1,7 +1,6 @@
using PepperDash.Essentials.Core.JoinMaps;
using System;
namespace PepperDash.Essentials.Core.Bridges.JoinMaps
namespace PepperDash.Essentials.Core.Bridges
{
/// <summary>
/// Represents a DmRmcControllerJoinMap

View File

@@ -1,7 +1,6 @@
using PepperDash.Essentials.Core.JoinMaps;
using System;
using System;
namespace PepperDash.Essentials.Core.Bridges.JoinMaps
namespace PepperDash.Essentials.Core.Bridges
{
/// <summary>
/// Represents a DmTxControllerJoinMap

View File

@@ -1,7 +1,6 @@
using PepperDash.Essentials.Core.JoinMaps;
using System;
using System;
namespace PepperDash.Essentials.Core.Bridges.JoinMaps
namespace PepperDash.Essentials.Core.Bridges
{
/// <summary>
/// Represents a DmpsAudioOutputControllerJoinMap

View File

@@ -1,7 +1,6 @@
using PepperDash.Essentials.Core.JoinMaps;
using System;
using System;
namespace PepperDash.Essentials.Core.Bridges.JoinMaps
namespace PepperDash.Essentials.Core.Bridges
{
/// <summary>
/// Represents a DmpsMicrophoneControllerJoinMap

View File

@@ -1,7 +1,6 @@
using PepperDash.Essentials.Core.JoinMaps;
using System;
using System;
namespace PepperDash.Essentials.Core.Bridges.JoinMaps
namespace PepperDash.Essentials.Core.Bridges
{
/// <summary>
/// Represents a DmpsRoutingControllerJoinMap

View File

@@ -1,4 +1,4 @@
using PepperDash.Essentials.Core.JoinMaps;
using PepperDash.Essentials.Core;
namespace PepperDash.Essentials.Core.Bridges.JoinMaps
{

View File

@@ -1,8 +1,7 @@
using PepperDash.Essentials.Core.JoinMaps;
using System;
using System;
namespace PepperDash.Essentials.Core.Bridges.JoinMaps
namespace PepperDash.Essentials.Core.Bridges
{
/// <summary>
/// Represents a GenericLightingJoinMap

View File

@@ -1,7 +1,6 @@
using PepperDash.Essentials.Core.JoinMaps;
using System;
using System;
namespace PepperDash.Essentials.Core.Bridges.JoinMaps
namespace PepperDash.Essentials.Core.Bridges
{
/// <summary>
/// Represents a GenericRelayControllerJoinMap

View File

@@ -1,7 +1,6 @@
using PepperDash.Essentials.Core.JoinMaps;
using System;
using System;
namespace PepperDash.Essentials.Core.Bridges.JoinMaps
namespace PepperDash.Essentials.Core.Bridges
{
/// <summary>
/// Represents a GlsOccupancySensorBaseJoinMap

View File

@@ -1,5 +1,5 @@
using PepperDash.Essentials.Core.JoinMaps;
using System;
using System;
using PepperDash.Essentials.Core;
namespace PepperDash.Essentials.Core.Bridges.JoinMaps
{

View File

@@ -1,7 +1,6 @@
using PepperDash.Essentials.Core.JoinMaps;
using System;
using System;
namespace PepperDash.Essentials.Core.Bridges.JoinMaps
namespace PepperDash.Essentials.Core.Bridges
{
/// <summary>
/// Represents a HdMdNxM4kEControllerJoinMap

View File

@@ -1,7 +1,6 @@
using PepperDash.Essentials.Core.JoinMaps;
using System;
using System;
namespace PepperDash.Essentials.Core.Bridges.JoinMaps
namespace PepperDash.Essentials.Core.Bridges
{
/// <summary>
/// Represents a HdMdxxxCEControllerJoinMap

View File

@@ -1,7 +1,7 @@
using PepperDash.Essentials.Core.JoinMaps;
using System;
using System;
using PepperDash.Essentials.Core;
namespace PepperDash.Essentials.Core.Bridges.JoinMaps
namespace PepperDash.Essentials.Core.Bridges
{
/// <summary>
/// Represents a HdPsXxxControllerJoinMap

View File

@@ -1,7 +1,6 @@
using PepperDash.Essentials.Core.JoinMaps;
using System;
using System;
namespace PepperDash.Essentials.Core.Bridges.JoinMaps
namespace PepperDash.Essentials.Core.Bridges
{
/// <summary>
/// Represents a Hrxxx0WirelessRemoteControllerJoinMap

View File

@@ -1,7 +1,6 @@
using PepperDash.Essentials.Core.JoinMaps;
using System;
using System;
namespace PepperDash.Essentials.Core.Bridges.JoinMaps
namespace PepperDash.Essentials.Core.Bridges
{
/// <summary>
/// Represents a IAnalogInputJoinMap

View File

@@ -1,7 +1,6 @@
using PepperDash.Essentials.Core.JoinMaps;
using System;
using System;
namespace PepperDash.Essentials.Core.Bridges.JoinMaps
namespace PepperDash.Essentials.Core.Bridges
{
/// <summary>
/// Represents a IBasicCommunicationJoinMap

View File

@@ -1,7 +1,6 @@
using PepperDash.Essentials.Core.JoinMaps;
using System;
using System;
namespace PepperDash.Essentials.Core.Bridges.JoinMaps
namespace PepperDash.Essentials.Core.Bridges
{
/// <summary>
/// Represents a IDigitalInputJoinMap

View File

@@ -1,7 +1,6 @@
using PepperDash.Essentials.Core.JoinMaps;
using System;
using System;
namespace PepperDash.Essentials.Core.Bridges.JoinMaps
namespace PepperDash.Essentials.Core.Bridges
{
/// <summary>
/// Represents a IDigitalOutputJoinMap

View File

@@ -1,7 +1,10 @@
using PepperDash.Essentials.Core.JoinMaps;
using System;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Crestron.SimplSharp;
namespace PepperDash.Essentials.Core.Bridges.JoinMaps
namespace PepperDash.Essentials.Core.Bridges
{
/// <summary>
/// Join map for IRBlurayBase devices

View File

@@ -1,7 +1,6 @@
using PepperDash.Essentials.Core.JoinMaps;
using System;
using System;
namespace PepperDash.Essentials.Core.Bridges.JoinMaps
namespace PepperDash.Essentials.Core.Bridges
{
/// <summary>
/// Represents a PduJoinMapBase

View File

@@ -1,8 +1,7 @@
using PepperDash.Essentials.Core.JoinMaps;
using System;
using System;
namespace PepperDash.Essentials.Core.Bridges.JoinMaps
namespace PepperDash.Essentials.Core.Bridges
{
/// <summary>
/// Represents a SetTopBoxControllerJoinMap

View File

@@ -1,7 +1,6 @@
using PepperDash.Essentials.Core.JoinMaps;
using System;
using System;
namespace PepperDash.Essentials.Core.Bridges.JoinMaps
namespace PepperDash.Essentials.Core.Bridges
{
/// <summary>
/// Represents a StatusSignControllerJoinMap

View File

@@ -1,7 +1,6 @@
using PepperDash.Essentials.Core.JoinMaps;
using System;
using System;
namespace PepperDash.Essentials.Core.Bridges.JoinMaps
namespace PepperDash.Essentials.Core.Bridges
{
/// <summary>
/// Represents a SystemMonitorJoinMap

View File

@@ -1,5 +1,5 @@
using PepperDash.Essentials.Core.JoinMaps;
using System;
using PepperDash.Essentials.Core;
namespace PepperDash.Essentials.Core.Bridges.JoinMaps
{
/// <summary>

View File

@@ -1,72 +0,0 @@
using System;
using Crestron.SimplSharpPro;
namespace PepperDash.Essentials.Core.Bridges
{
/// <summary>
/// Helper class for various Sig events
/// </summary>
public class SigHelper
{
/// <summary>
/// Runs action when Sig is pressed
/// </summary>
/// <param name="sig"></param>
public static void Pressed(Sig sig, Action act) { if (sig.BoolValue) act(); }
/// <summary>
/// Runs action when Sig is released
/// </summary>
public static void Released(Sig sig, Action act) { if (!sig.BoolValue) act(); }
/// <summary>
/// SetBoolOutAction method
/// </summary>
public static void SetBoolOutAction(BoolOutputSig sig, Action<bool> a)
{
if (sig != null)
sig.UserObject = a;
}
/// <summary>
/// Safely clears action of non-null sig.
/// </summary>
public static void ClearBoolOutAction(BoolOutputSig sig)
{
if (sig != null)
sig.UserObject = null;
}
/// <summary>
/// Does a timed ramp, where the time is scaled proportional to the
/// remaining range to cover
/// </summary>
/// <param name="sig">Ushort sig to scale</param>
/// <param name="newLevel">Level to go to</param>
/// <param name="time">In ms (not hundredths like Crestron Sig ramp function)</param>
/// <summary>
/// RampTimeScaled method
/// </summary>
public static void RampTimeScaled(Sig sig, ushort newLevel, uint time)
{
var level = sig.UShortValue;
var diff = Math.Abs(level - newLevel);
var scaledTime = (uint)(diff * time / 65535);
Ramp(sig, newLevel, scaledTime);
}
/// <summary>
/// Ramps signal
/// </summary>
/// <param name="sig"></param>
/// <param name="level"></param>
/// <param name="time">In ms (not hundredths like Crestron Sig ramp function)</param>
/// <summary>
/// Ramp method
/// </summary>
public static void Ramp(Sig sig, ushort level, uint time)
{
sig.CreateRamp(level, time / 10);
}
}
}

View File

@@ -1,20 +1,26 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using Crestron.SimplSharp;
using Crestron.SimplSharpPro;
using Crestron.SimplSharpPro.DM;
using PepperDash.Core;
using Serilog.Events;
namespace PepperDash.Essentials.Core.Communications
namespace PepperDash.Essentials.Core
{
/// <summary>
/// Represents a CecPortController
/// </summary>
public class CecPortController : Device, IBasicCommunicationWithStreamDebugging
/// <summary>
/// Represents a CecPortController
/// </summary>
public class CecPortController : Device, IBasicCommunicationWithStreamDebugging
{
/// <summary>
/// Gets or sets the StreamDebugging
/// </summary>
public CommunicationStreamDebugging StreamDebugging { get; private set; }
/// <summary>
/// Gets or sets the StreamDebugging
/// </summary>
public CommunicationStreamDebugging StreamDebugging { get; private set; }
public event EventHandler<GenericCommMethodReceiveBytesArgs> BytesReceived;
public event EventHandler<GenericCommMethodReceiveTextArgs> TextReceived;
@@ -27,16 +33,16 @@ namespace PepperDash.Essentials.Core.Communications
ICec Port;
public CecPortController(string key, Func<EssentialsControlPropertiesConfig, ICec> postActivationFunc,
EssentialsControlPropertiesConfig config):base(key)
EssentialsControlPropertiesConfig config) : base(key)
{
StreamDebugging = new CommunicationStreamDebugging(key);
StreamDebugging = new CommunicationStreamDebugging(key);
AddPostActivationAction(() =>
{
Port = postActivationFunc(config);
Port.StreamCec.CecChange += StreamCec_CecChange;
});
});
}
public CecPortController(string key, ICec port)
@@ -52,27 +58,25 @@ namespace PepperDash.Essentials.Core.Communications
if (args.EventId == CecEventIds.CecMessageReceivedEventId)
OnDataReceived(cecDevice.Received.StringValue);
else if (args.EventId == CecEventIds.ErrorFeedbackEventId)
if(cecDevice.ErrorFeedback.BoolValue)
if (cecDevice.ErrorFeedback.BoolValue)
Debug.LogMessage(LogEventLevel.Verbose, this, "CEC NAK Error");
}
void OnDataReceived(string s)
{
var bytesHandler = BytesReceived;
var bytesHandler = BytesReceived;
if (bytesHandler != null)
{
var bytes = Encoding.GetEncoding(28591).GetBytes(s);
if (StreamDebugging.RxStreamDebuggingIsEnabled)
Debug.LogMessage(LogEventLevel.Information, this, "Received: '{0}'", ComTextHelper.GetEscapedText(bytes));
this.PrintReceivedBytes(bytes);
bytesHandler(this, new GenericCommMethodReceiveBytesArgs(bytes));
}
var textHandler = TextReceived;
if (textHandler != null)
{
if (StreamDebugging.RxStreamDebuggingIsEnabled)
Debug.LogMessage(LogEventLevel.Information, this, "Received: '{0}'", s);
textHandler(this, new GenericCommMethodReceiveTextArgs(s));
}
if (textHandler != null)
{
this.PrintReceivedText(s);
textHandler(this, new GenericCommMethodReceiveTextArgs(s));
}
}
#region IBasicCommunication Members
@@ -84,8 +88,7 @@ namespace PepperDash.Essentials.Core.Communications
{
if (Port == null)
return;
if (StreamDebugging.TxStreamDebuggingIsEnabled)
Debug.LogMessage(LogEventLevel.Information, this, "Sending {0} characters of text: '{1}'", text.Length, text);
this.PrintSentText(text);
Port.StreamCec.Send.StringValue = text;
}
@@ -97,8 +100,8 @@ namespace PepperDash.Essentials.Core.Communications
if (Port == null)
return;
var text = Encoding.GetEncoding(28591).GetString(bytes, 0, bytes.Length);
if (StreamDebugging.TxStreamDebuggingIsEnabled)
Debug.LogMessage(LogEventLevel.Information, this, "Sending {0} bytes: '{1}'", bytes.Length, ComTextHelper.GetEscapedText(bytes));
this.PrintSentBytes(bytes);
Debug.LogMessage(LogEventLevel.Information, this, "Sending {0} bytes: '{1}'", bytes.Length, ComTextHelper.GetEscapedText(bytes));
Port.StreamCec.Send.StringValue = text;
}
@@ -133,7 +136,7 @@ namespace PepperDash.Essentials.Core.Communications
foreach (var t in split)
{
if (t.StartsWith(@"\") && t.Length == 4)
b.Append((char)Convert.ToByte(t.Substring(2, 2), 16));
b.Append((char)(Convert.ToByte(t.Substring(2, 2), 16)));
else
b.Append(t);
}

View File

@@ -0,0 +1,258 @@
using System;
using System.Text;
using System.Text.RegularExpressions;
using Crestron.SimplSharpPro;
using PepperDash.Core;
using PepperDash.Core.Logging;
using Serilog.Events;
namespace PepperDash.Essentials.Core
{
/// <summary>
/// Represents a ComPortController
/// </summary>
public class ComPortController : Device, IBasicCommunicationWithStreamDebugging
{
/// <summary>
/// Gets or sets the StreamDebugging
/// </summary>
public CommunicationStreamDebugging StreamDebugging { get; private set; }
/// <summary>
/// Event fired when bytes are received
/// </summary>
public event EventHandler<GenericCommMethodReceiveBytesArgs> BytesReceived;
/// <summary>
/// Event fired when text is received
/// </summary>
public event EventHandler<GenericCommMethodReceiveTextArgs> TextReceived;
/// <summary>
/// Gets or sets the IsConnected
/// </summary>
public bool IsConnected { get { return true; } }
ComPort Port;
ComPort.ComPortSpec Spec;
/// <summary>
/// Constructor
/// </summary>
/// <param name="key"></param>
/// <param name="postActivationFunc"></param>
/// <param name="spec"></param>
/// <param name="config"></param>
public ComPortController(string key, Func<EssentialsControlPropertiesConfig, ComPort> postActivationFunc,
ComPort.ComPortSpec spec, EssentialsControlPropertiesConfig config) : base(key)
{
StreamDebugging = new CommunicationStreamDebugging(key);
Spec = spec;
AddPostActivationAction(() =>
{
Port = postActivationFunc(config);
RegisterAndConfigureComPort();
});
}
/// <summary>
/// Constructor
/// </summary>
/// <param name="key">Device key</param>
/// <param name="port">COM port instance</param>
/// <param name="spec">COM port specification</param>
public ComPortController(string key, ComPort port, ComPort.ComPortSpec spec)
: base(key)
{
if (port == null)
{
Debug.LogMessage(LogEventLevel.Information, this, "ERROR: Invalid com port, continuing but comms will not function");
return;
}
Port = port;
Spec = spec;
//IsConnected = new BoolFeedback(CommonBoolCue.IsConnected, () => true);
RegisterAndConfigureComPort();
}
private void RegisterAndConfigureComPort()
{
if (Port == null)
{
Debug.LogMessage(LogEventLevel.Information, this, "Configured com Port for this device does not exist.");
return;
}
// TODO [ ] - Remove commented out code once verified working
//if (Port.Parent is CrestronControlSystem || Port.Parent is CenIoCom102)
if (Port.Parent is GenericBase genericDevice && genericDevice.Registerable)
{
//this.LogInformation($"INFO: Attempting to register {Key} using {Port.Parent.GetType().Name}-comport-{Port.ID}");
var result = genericDevice.Register();
if (result != eDeviceRegistrationUnRegistrationResponse.Success)
{
this.LogError($"ERROR: Cannot register {Key} using {Port.Parent.GetType().Name}-comport-{Port.ID} (result == {result})");
return; // false
}
}
var specResult = Port.SetComPortSpec(Spec);
if (specResult != 0)
{
this.LogError($"ERROR: Cannot set comspec for {Key} using {Port.Parent.GetType().Name}-comport-{Port.ID} (result == {specResult})");
return;
}
//this.LogInformation($"INFO: Successfully set comspec for {Key} using {Port.Parent.GetType().Name}-comport-{Port.ID} (result == {specResult})");
// TODO [ ] - Remove debug logging once verified working
// if (Port.Parent is CenIoCom102)
// {
// Port.PropertyChanged += (s, e) =>
// {
// this.LogInformation($@"RegisterAndConfigureComPort: PropertyChanged Fired >>
// comPort-'{Port.ID}',
// Property Changed-'{e.Property}',
// Value Changed-'{e.Value}',
// deviceName-'{Port.DeviceName}',
// parentDevice-'{Port.ParentDevice}',
// parent-`{Port.Parent}`,
// online-`{Port.IsOnline}`,
// present-`{Port.Present}`,
// supportedBaudRates-'{Port.SupportedBaudRates}'");
// };
// Port.ExtendedInformationChanged += (s, e) =>
// {
// this.LogInformation($@"RegisterAndConfigureComPort: ExtendedInformationChanged Fired >>
// comPort-'{Port.ID}',
// {e.Protocol},
// {e.BaudRate},
// {e.Parity},
// {e.DataBits},
// {e.StopBits},
// HW Handshake-'{e.HardwareHandshakeSetting}',
// SW Handshake-'{e.SoftwareHandshakeSetting}'");
// };
// }
Port.SerialDataReceived += Port_SerialDataReceived;
}
/// <summary>
/// Destructor
/// </summary>
~ComPortController()
{
Port.SerialDataReceived -= Port_SerialDataReceived;
}
void Port_SerialDataReceived(ComPort ReceivingComPort, ComPortSerialDataEventArgs args)
{
OnDataReceived(args.SerialData);
}
void OnDataReceived(string s)
{
var eventSubscribed = false;
var bytesHandler = BytesReceived;
if (bytesHandler != null)
{
var bytes = Encoding.GetEncoding(28591).GetBytes(s);
this.PrintReceivedBytes(bytes);
bytesHandler(this, new GenericCommMethodReceiveBytesArgs(bytes));
eventSubscribed = true;
}
var textHandler = TextReceived;
if (textHandler != null)
{
this.PrintReceivedText(s);
textHandler(this, new GenericCommMethodReceiveTextArgs(s));
eventSubscribed = true;
}
if (!eventSubscribed) Debug.LogMessage(LogEventLevel.Warning, this, "Received data but no handler is registered");
}
/// <summary>
/// Deactivate method
/// </summary>
/// <inheritdoc />
public override bool Deactivate()
{
return Port.UnRegister() == eDeviceRegistrationUnRegistrationResponse.Success;
}
#region IBasicCommunication Members
/// <summary>
/// SendText method
/// </summary>
public void SendText(string text)
{
if (Port == null)
return;
this.PrintSentText(text);
Port.Send(text);
}
/// <summary>
/// SendBytes method
/// </summary>
public void SendBytes(byte[] bytes)
{
if (Port == null)
return;
var text = Encoding.GetEncoding(28591).GetString(bytes, 0, bytes.Length);
this.PrintSentBytes(bytes);
Port.Send(text);
}
/// <summary>
/// Connect method
/// </summary>
public void Connect()
{
}
/// <summary>
/// Disconnect method
/// </summary>
public void Disconnect()
{
}
#endregion
/// <summary>
///
/// </summary>
/// <param name="s"></param>
/// <summary>
/// SimulateReceive method
/// </summary>
public void SimulateReceive(string s)
{
// split out hex chars and build string
var split = Regex.Split(s, @"(\\[Xx][0-9a-fA-F][0-9a-fA-F])");
StringBuilder b = new StringBuilder();
foreach (var t in split)
{
if (t.StartsWith(@"\") && t.Length == 4)
b.Append((char)(Convert.ToByte(t.Substring(2, 2), 16)));
else
b.Append(t);
}
OnDataReceived(b.ToString());
}
}
}

View File

@@ -1,12 +1,19 @@

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Crestron.SimplSharp;
using Crestron.SimplSharpPro;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using PepperDash.Core;
namespace PepperDash.Essentials.Core.Communications
namespace PepperDash.Essentials.Core
{
/// <summary>
/// This converter creates a proper ComPort.ComPortSpec struct from more-friendly JSON values. It uses

View File

@@ -0,0 +1,138 @@
using System;
using System.Collections.Generic;
using System.Text;
using Crestron.SimplSharp.CrestronSockets;
using Crestron.SimplSharpPro.DeviceSupport;
using Newtonsoft.Json;
using PepperDash.Core;
using PepperDash.Core.Logging;
using PepperDash.Essentials.Core.Bridges;
using PepperDash.Essentials.Core.Config;
using PepperDash.Essentials.Core.Devices;
namespace PepperDash.Essentials.Core
{
/// <summary>
/// Implements IBasicCommunication and sends all communication through an EISC
/// </summary>
[Description("Generic communication wrapper class for any IBasicCommunication type")]
public class CommBridge : EssentialsBridgeableDevice, IBasicCommunication
{
private EiscApiAdvanced eisc;
private IBasicCommunicationJoinMap joinMap;
/// <summary>
/// Event triggered when text is received through the communication bridge.
/// </summary>
public event EventHandler<GenericCommMethodReceiveTextArgs> TextReceived;
/// <summary>
/// Event triggered when bytes are received through the communication bridge.
/// </summary>
public event EventHandler<GenericCommMethodReceiveBytesArgs> BytesReceived;
/// <summary>
/// Indicates whether the communication bridge is currently connected.
/// </summary>
public bool IsConnected { get; private set; }
/// <summary>
/// Initializes a new instance of the <see cref="CommBridge"/> class.
/// </summary>
/// <param name="key">The unique key for the communication bridge.</param>
/// <param name="name">The display name for the communication bridge.</param>
public CommBridge(string key, string name)
: base(key, name)
{
}
/// <summary>
/// Sends a byte array through the communication bridge.
/// </summary>
/// <param name="bytes">The byte array to send.</param>
public void SendBytes(byte[] bytes)
{
if (eisc == null)
{
this.LogWarning("EISC is null, cannot send bytes.");
return;
}
eisc.Eisc.SetString(joinMap.SendText.JoinNumber, Encoding.ASCII.GetString(bytes, 0, bytes.Length));
}
/// <summary>
/// Sends a text string through the communication bridge.
/// </summary>
/// <param name="text">The text string to send.</param>
public void SendText(string text)
{
if (eisc == null)
{
this.LogWarning("EISC is null, cannot send text.");
return;
}
eisc.Eisc.SetString(joinMap.SendText.JoinNumber, text);
}
/// <summary>
/// Initiates a connection through the communication bridge.
/// </summary>
public void Connect()
{
if (eisc == null)
{
this.LogWarning("EISC is null, cannot connect.");
return;
}
eisc.Eisc.SetBool(joinMap.Connect.JoinNumber, true);
}
/// <summary>
/// Terminates the connection through the communication bridge.
/// </summary>
public void Disconnect()
{
if (eisc == null)
{
this.LogWarning("EISC is null, cannot disconnect.");
return;
}
eisc.Eisc.SetBool(joinMap.Connect.JoinNumber, false);
}
/// <inheritdoc />
public override void LinkToApi(BasicTriList trilist, uint joinStart, string joinMapKey, EiscApiAdvanced bridge)
{
joinMap = new IBasicCommunicationJoinMap(joinStart);
var joinMapSerialized = JoinMapHelper.GetSerializedJoinMapForDevice(joinMapKey);
if (!string.IsNullOrEmpty(joinMapSerialized))
joinMap = JsonConvert.DeserializeObject<IBasicCommunicationJoinMap>(joinMapSerialized);
if (bridge != null)
{
bridge.AddJoinMap(Key, joinMap);
}
else
{
this.LogWarning("Please update config to use 'eiscapiadvanced' to get all join map features for this device.");
}
this.LogDebug("Linking to Trilist '{0}'", trilist.ID.ToString("X"));
eisc = bridge;
trilist.SetBoolSigAction(joinMap.Connected.JoinNumber, (b) => IsConnected = b);
trilist.SetStringSigAction(joinMap.TextReceived.JoinNumber, (s) =>
{
TextReceived?.Invoke(this, new GenericCommMethodReceiveTextArgs(s));
BytesReceived?.Invoke(this, new GenericCommMethodReceiveBytesArgs(Encoding.ASCII.GetBytes(s)));
});
}
}
}

View File

@@ -0,0 +1,290 @@
using System;
using Crestron.SimplSharpPro;
using Crestron.SimplSharpPro.DM;
using Newtonsoft.Json;
using PepperDash.Core;
using PepperDash.Essentials.Core.Config;
using Serilog.Events;
namespace PepperDash.Essentials.Core
{
/// <summary>
///
/// </summary>
public class CommFactory
{
public static EssentialsControlPropertiesConfig GetControlPropertiesConfig(DeviceConfig deviceConfig)
{
try
{
return JsonConvert.DeserializeObject<EssentialsControlPropertiesConfig>
(deviceConfig.Properties["control"].ToString());
//Debug.LogMessage(LogEventLevel.Verbose, "Control TEST: {0}", JsonConvert.SerializeObject(controlConfig));
}
catch (Exception e)
{
Debug.LogMessage(LogEventLevel.Information, "ERROR: [{0}] Control properties deserialize failed:\r{1}", deviceConfig.Key, e);
return null;
}
}
/// <summary>
/// Returns a comm method of either com port, TCP, SSH, and puts this into the DeviceManager
/// </summary>
/// <param name="deviceConfig">The Device config object</param>
/// <summary>
/// CreateCommForDevice method
/// </summary>
public static IBasicCommunication CreateCommForDevice(DeviceConfig deviceConfig)
{
EssentialsControlPropertiesConfig controlConfig = GetControlPropertiesConfig(deviceConfig);
if (controlConfig == null)
return null;
IBasicCommunication comm = null;
try
{
var c = controlConfig.TcpSshProperties;
switch (controlConfig.Method)
{
case eControlMethod.Com:
comm = new ComPortController(deviceConfig.Key + "-com", GetComPort, controlConfig.ComParams.Value, controlConfig);
break;
case eControlMethod.ComBridge:
comm = new CommBridge(deviceConfig.Key + "-simpl", deviceConfig.Name + " Simpl");
break;
case eControlMethod.Cec:
comm = new CecPortController(deviceConfig.Key + "-cec", GetCecPort, controlConfig);
break;
case eControlMethod.IR:
break;
case eControlMethod.Ssh:
{
var ssh = new GenericSshClient(deviceConfig.Key + "-ssh", c.Address, c.Port, c.Username, c.Password);
ssh.AutoReconnect = c.AutoReconnect;
if (ssh.AutoReconnect)
ssh.AutoReconnectIntervalMs = c.AutoReconnectIntervalMs;
comm = ssh;
break;
}
case eControlMethod.Tcpip:
{
var tcp = new GenericTcpIpClient(deviceConfig.Key + "-tcp", c.Address, c.Port, c.BufferSize);
tcp.AutoReconnect = c.AutoReconnect;
if (tcp.AutoReconnect)
tcp.AutoReconnectIntervalMs = c.AutoReconnectIntervalMs;
comm = tcp;
break;
}
case eControlMethod.Udp:
{
var udp = new GenericUdpServer(deviceConfig.Key + "-udp", c.Address, c.Port, c.BufferSize);
comm = udp;
break;
}
case eControlMethod.Telnet:
break;
case eControlMethod.SecureTcpIp:
{
var secureTcp = new GenericSecureTcpIpClient(deviceConfig.Key + "-secureTcp", c.Address, c.Port, c.BufferSize);
secureTcp.AutoReconnect = c.AutoReconnect;
if (secureTcp.AutoReconnect)
secureTcp.AutoReconnectIntervalMs = c.AutoReconnectIntervalMs;
comm = secureTcp;
break;
}
default:
break;
}
}
catch (Exception e)
{
Debug.LogMessage(LogEventLevel.Information, "Cannot create communication from JSON:\r{0}\r\rException:\r{1}",
deviceConfig.Properties.ToString(), e);
}
// put it in the device manager if it's the right flavor
if (comm is Device comDev)
DeviceManager.AddDevice(comDev);
return comm;
}
/// <summary>
/// GetComPort method
/// </summary>
public static ComPort GetComPort(EssentialsControlPropertiesConfig config)
{
var comPar = config.ComParams;
var dev = GetIComPortsDeviceFromManagedDevice(config.ControlPortDevKey);
if (dev != null && config.ControlPortNumber <= dev.NumberOfComPorts)
return dev.ComPorts[config.ControlPortNumber.Value];
Debug.LogMessage(LogEventLevel.Information, "GetComPort: Device '{0}' does not have com port {1}", config.ControlPortDevKey, config.ControlPortNumber);
return null;
}
/// <summary>
/// Gets an ICec port from a RoutingInput or RoutingOutput on a device
/// </summary>
/// <param name="config"></param>
/// <returns></returns>
/// <summary>
/// GetCecPort method
/// </summary>
public static ICec GetCecPort(ControlPropertiesConfig config)
{
try
{
var dev = DeviceManager.GetDeviceForKey(config.ControlPortDevKey);
Debug.LogMessage(LogEventLevel.Information, "GetCecPort: device '{0}' {1}", config.ControlPortDevKey, dev == null
? "is not valid, failed to get cec port"
: "found in device manager, attempting to get cec port");
if (dev == null)
return null;
if (String.IsNullOrEmpty(config.ControlPortName))
{
Debug.LogMessage(LogEventLevel.Information, "GetCecPort: '{0}' - Configuration missing 'ControlPortName'", config.ControlPortDevKey);
return null;
}
var inputsOutputs = dev as IRoutingInputsOutputs;
if (inputsOutputs == null)
{
Debug.LogMessage(LogEventLevel.Information, "GetCecPort: Device '{0}' does not support IRoutingInputsOutputs, failed to get CEC port called '{1}'",
config.ControlPortDevKey, config.ControlPortName);
return null;
}
var inputPort = inputsOutputs.InputPorts[config.ControlPortName];
if (inputPort != null && inputPort.Port is ICec)
return inputPort.Port as ICec;
var outputPort = inputsOutputs.OutputPorts[config.ControlPortName];
if (outputPort != null && outputPort.Port is ICec)
return outputPort.Port as ICec;
}
catch (Exception ex)
{
Debug.LogMessage(LogEventLevel.Debug, "GetCecPort Exception Message: {0}", ex.Message);
Debug.LogMessage(LogEventLevel.Verbose, "GetCecPort Exception StackTrace: {0}", ex.StackTrace);
if (ex.InnerException != null)
Debug.LogMessage(LogEventLevel.Information, "GetCecPort Exception InnerException: {0}", ex.InnerException);
}
Debug.LogMessage(LogEventLevel.Information, "GetCecPort: Device '{0}' does not have a CEC port called '{1}'",
config.ControlPortDevKey, config.ControlPortName);
return null;
}
/// <summary>
/// Helper to grab the IComPorts device for this PortDeviceKey. Key "controlSystem" will
/// return the ControlSystem object from the Global class.
/// </summary>
/// <returns>IComPorts device or null if the device is not found or does not implement IComPorts</returns>
/// <summary>
/// GetIComPortsDeviceFromManagedDevice method
/// </summary>
public static IComPorts GetIComPortsDeviceFromManagedDevice(string ComPortDevKey)
{
if ((ComPortDevKey.Equals("controlSystem", System.StringComparison.OrdinalIgnoreCase)
|| ComPortDevKey.Equals("processor", System.StringComparison.OrdinalIgnoreCase))
&& Global.ControlSystem is IComPorts)
return Global.ControlSystem;
else
{
var dev = DeviceManager.GetDeviceForKey(ComPortDevKey) as IComPorts;
if (dev == null)
Debug.LogMessage(LogEventLevel.Information, "ComPortConfig: Cannot find com port device '{0}'", ComPortDevKey);
return dev;
}
}
}
/// <summary>
/// Represents a EssentialsControlPropertiesConfig
/// </summary>
public class EssentialsControlPropertiesConfig :
ControlPropertiesConfig
{
[JsonProperty("comParams", NullValueHandling = NullValueHandling.Ignore)]
[JsonConverter(typeof(ComSpecJsonConverter))]
public ComPort.ComPortSpec? ComParams { get; set; }
[JsonProperty("cresnetId", NullValueHandling = NullValueHandling.Ignore)]
public string CresnetId { get; set; }
/// <summary>
/// Attempts to provide uint conversion of string CresnetId
/// </summary>
[JsonIgnore]
public uint CresnetIdInt
{
get
{
try
{
return Convert.ToUInt32(CresnetId, 16);
}
catch (Exception)
{
throw new FormatException(string.Format("ERROR:Unable to convert Cresnet ID: {0} to hex. Error:\n{1}", CresnetId));
}
}
}
[JsonProperty("infinetId", NullValueHandling = NullValueHandling.Ignore)]
/// <summary>
/// Gets or sets the InfinetId
/// </summary>
public string InfinetId { get; set; }
/// <summary>
/// Attepmts to provide uiont conversion of string InifinetId
/// </summary>
[JsonIgnore]
public uint InfinetIdInt
{
get
{
try
{
return Convert.ToUInt32(InfinetId, 16);
}
catch (Exception)
{
throw new FormatException(string.Format("ERROR:Unable to conver Infinet ID: {0} to hex. Error:\n{1}", InfinetId));
}
}
}
}
/// <summary>
/// Represents a IrControlSpec
/// </summary>
public class IrControlSpec
{
/// <summary>
/// Gets or sets the PortDeviceKey
/// </summary>
public string PortDeviceKey { get; set; }
/// <summary>
/// Gets or sets the PortNumber
/// </summary>
public uint PortNumber { get; set; }
/// <summary>
/// Gets or sets the File
/// </summary>
public string File { get; set; }
}
}

View File

@@ -0,0 +1,25 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Crestron.SimplSharp;
using Crestron.SimplSharpPro;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using PepperDash.Core;
namespace PepperDash.Essentials.Core
{
/// <summary>
///
/// </summary>
public interface IComPortsDevice
{
IComPorts Device { get; }
}
}

View File

@@ -1,12 +1,15 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Crestron.SimplSharp;
using PepperDash.Core;
using PepperDash.Essentials.Core.Config;
using PepperDash.Essentials.Core.Devices;
using PepperDash.Essentials.Core.Monitoring;
using Serilog.Events;
namespace PepperDash.Essentials.Core.Communications
namespace PepperDash.Essentials.Core
{
/// <summary>
/// Represents a ConsoleCommMockDevice

View File

@@ -1,19 +1,19 @@
using System;

using System;
using System.Collections.Generic;
using Crestron.SimplSharp.CrestronSockets;
using Crestron.SimplSharpPro.DeviceSupport;
using Newtonsoft.Json;
using PepperDash.Core;
using PepperDash.Essentials.Core.Bridges;
using PepperDash.Essentials.Core.Devices;
using PepperDash.Essentials.Core.Config;
using Serilog.Events;
using PepperDash.Essentials.Core.Bridges.JoinMaps;
using PepperDash.Essentials.Core.JoinMaps;
using PepperDash.Essentials.Core.Config.Essentials;
using PepperDash.Essentials.Core.Touchpanels;
namespace PepperDash.Essentials.Core.Communications
namespace PepperDash.Essentials.Core
{
/// <summary>
/// Serves as a generic wrapper class for all styles of IBasicCommuncation ports
@@ -120,7 +120,7 @@ namespace PepperDash.Essentials.Core.Communications
if (sComm == null) return;
sComm.ConnectionChange += (s, a) =>
{
trilist.SetUshort(joinMap.Status.JoinNumber, (ushort)a.Client.ClientStatus);
trilist.SetUshort(joinMap.Status.JoinNumber, (ushort)(a.Client.ClientStatus));
trilist.SetBool(joinMap.Connected.JoinNumber, a.Client.ClientStatus ==
SocketStatus.SOCKET_STATUS_CONNECTED);
};

View File

@@ -1,7 +1,8 @@
using Crestron.SimplSharp.Net.Http;
using PepperDash.Core;
using System;
namespace PepperDash.Essentials.Core.Communications
namespace PepperDash.Essentials.Core
{
[Obsolete("Please use the builtin HttpClient class instead: https://learn.microsoft.com/en-us/dotnet/fundamentals/networking/http/httpclient-guidelines")]
/// <summary>
@@ -32,17 +33,17 @@ namespace PepperDash.Essentials.Core.Communications
/// </summary>
public void SendText(string path)
{
var request = new HttpClientRequest();
var url = string.Format("http://{0}/{1}", Client.HostName, path);
HttpClientRequest request = new HttpClientRequest();
string url = string.Format("http://{0}/{1}", Client.HostName, path);
request.Url = new UrlParser(url);
var error = Client.DispatchAsyncEx(request, Response, request);
HttpClient.DISPATCHASYNC_ERROR error = Client.DispatchAsyncEx(request, Response, request);
}
public void SendText(string format, params object[] items)
{
var request = new HttpClientRequest();
var url = string.Format("http://{0}/{1}", Client.HostName, string.Format(format, items));
HttpClientRequest request = new HttpClientRequest();
string url = string.Format("http://{0}/{1}", Client.HostName, string.Format(format, items));
request.Url = new UrlParser(url);
var error = Client.DispatchAsyncEx(request, Response, request);
HttpClient.DISPATCHASYNC_ERROR error = Client.DispatchAsyncEx(request, Response, request);
}
/// <summary>
@@ -50,8 +51,8 @@ namespace PepperDash.Essentials.Core.Communications
/// </summary>
public void SendTextNoResponse(string format, params object[] items)
{
var request = new HttpClientRequest();
var url = string.Format("http://{0}/{1}", Client.HostName, string.Format(format, items));
HttpClientRequest request = new HttpClientRequest();
string url = string.Format("http://{0}/{1}", Client.HostName, string.Format(format, items));
request.Url = new UrlParser(url);
Client.Dispatch(request);
}

View File

@@ -1,15 +1,18 @@

using System;
using System.Collections.Generic;
using Crestron.SimplSharp;
using Crestron.SimplSharp.CrestronIO;
using Crestron.SimplSharpPro;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using PepperDash.Core;
using PepperDash.Essentials.Core.Config;
using PepperDash.Essentials.Core.Devices;
using Serilog.Events;
namespace PepperDash.Essentials.Core.Communications
namespace PepperDash.Essentials.Core
{
/// <summary>
///

View File

@@ -1,200 +0,0 @@
using System;
using System.Text;
using System.Text.RegularExpressions;
using Crestron.SimplSharpPro;
using Serilog.Events;
namespace PepperDash.Essentials.Core.Communications
{
/// <summary>
/// Represents a ComPortController
/// </summary>
public class ComPortController : Device, IBasicCommunicationWithStreamDebugging
{
/// <summary>
/// Gets or sets the StreamDebugging
/// </summary>
public CommunicationStreamDebugging StreamDebugging { get; private set; }
public event EventHandler<GenericCommMethodReceiveBytesArgs> BytesReceived;
public event EventHandler<GenericCommMethodReceiveTextArgs> TextReceived;
/// <summary>
/// Gets or sets the IsConnected
/// </summary>
public bool IsConnected { get { return true; } }
ComPort Port;
ComPort.ComPortSpec Spec;
public ComPortController(string key, Func<EssentialsControlPropertiesConfig, ComPort> postActivationFunc,
ComPort.ComPortSpec spec, EssentialsControlPropertiesConfig config) : base(key)
{
StreamDebugging = new CommunicationStreamDebugging(key);
Spec = spec;
AddPostActivationAction(() =>
{
Port = postActivationFunc(config);
RegisterAndConfigureComPort();
});
}
public ComPortController(string key, ComPort port, ComPort.ComPortSpec spec)
: base(key)
{
if (port == null)
{
Debug.LogMessage(LogEventLevel.Information, this, "ERROR: Invalid com port, continuing but comms will not function");
return;
}
Port = port;
Spec = spec;
//IsConnected = new BoolFeedback(CommonBoolCue.IsConnected, () => true);
RegisterAndConfigureComPort();
}
private void RegisterAndConfigureComPort()
{
if (Port == null)
{
Debug.LogMessage(LogEventLevel.Information, this, "Configured com Port for this device does not exist.");
return;
}
if (Port.Parent is CrestronControlSystem)
{
var result = Port.Register();
if (result != eDeviceRegistrationUnRegistrationResponse.Success)
{
Debug.LogMessage(LogEventLevel.Information, this, "ERROR: Cannot register Com port: {0}", result);
return; // false
}
}
var specResult = Port.SetComPortSpec(Spec);
if (specResult != 0)
{
Debug.LogMessage(LogEventLevel.Information, this, "WARNING: Cannot set comspec");
return;
}
Port.SerialDataReceived += Port_SerialDataReceived;
}
~ComPortController()
{
Port.SerialDataReceived -= Port_SerialDataReceived;
}
void Port_SerialDataReceived(ComPort ReceivingComPort, ComPortSerialDataEventArgs args)
{
OnDataReceived(args.SerialData);
}
void OnDataReceived(string s)
{
var eventSubscribed = false;
var bytesHandler = BytesReceived;
if (bytesHandler != null)
{
var bytes = Encoding.GetEncoding(28591).GetBytes(s);
if (StreamDebugging.RxStreamDebuggingIsEnabled)
Debug.LogMessage(LogEventLevel.Information, this, "Received: '{0}'", ComTextHelper.GetEscapedText(bytes));
bytesHandler(this, new GenericCommMethodReceiveBytesArgs(bytes));
eventSubscribed = true;
}
var textHandler = TextReceived;
if (textHandler != null)
{
if (StreamDebugging.RxStreamDebuggingIsEnabled)
Debug.LogMessage(LogEventLevel.Information, this, "Received: '{0}'", s);
textHandler(this, new GenericCommMethodReceiveTextArgs(s));
eventSubscribed = true;
}
if(!eventSubscribed) Debug.LogMessage(LogEventLevel.Warning, this, "Received data but no handler is registered");
}
/// <summary>
/// Deactivate method
/// </summary>
/// <inheritdoc />
public override bool Deactivate()
{
return Port.UnRegister() == eDeviceRegistrationUnRegistrationResponse.Success;
}
#region IBasicCommunication Members
/// <summary>
/// SendText method
/// </summary>
public void SendText(string text)
{
if (Port == null)
return;
if (StreamDebugging.TxStreamDebuggingIsEnabled)
Debug.LogMessage(LogEventLevel.Information, this, "Sending {0} characters of text: '{1}'", text.Length, text);
Port.Send(text);
}
/// <summary>
/// SendBytes method
/// </summary>
public void SendBytes(byte[] bytes)
{
if (Port == null)
return;
var text = Encoding.GetEncoding(28591).GetString(bytes, 0, bytes.Length);
if (StreamDebugging.TxStreamDebuggingIsEnabled)
Debug.LogMessage(LogEventLevel.Information, this, "Sending {0} bytes: '{1}'", bytes.Length, ComTextHelper.GetEscapedText(bytes));
Port.Send(text);
}
/// <summary>
/// Connect method
/// </summary>
public void Connect()
{
}
/// <summary>
/// Disconnect method
/// </summary>
public void Disconnect()
{
}
#endregion
/// <summary>
///
/// </summary>
/// <param name="s"></param>
/// <summary>
/// SimulateReceive method
/// </summary>
public void SimulateReceive(string s)
{
// split out hex chars and build string
var split = Regex.Split(s, @"(\\[Xx][0-9a-fA-F][0-9a-fA-F])");
var b = new StringBuilder();
foreach (var t in split)
{
if (t.StartsWith(@"\") && t.Length == 4)
b.Append((char)Convert.ToByte(t.Substring(2, 2), 16));
else
b.Append(t);
}
OnDataReceived(b.ToString());
}
}
}

View File

@@ -1,49 +0,0 @@
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
namespace PepperDash.Essentials.Core.Communications
{
/// <summary>
///
/// </summary>
public class ComTextHelper
{
/// <summary>
/// Gets escaped text for a byte array
/// </summary>
/// <param name="bytes"></param>
/// <returns></returns>
public static string GetEscapedText(byte[] bytes)
{
return string.Concat(bytes.Select(b => string.Format(@"[{0:X2}]", (int)b)).ToArray());
}
/// <summary>
/// Gets escaped text for a string
/// </summary>
/// <param name="text"></param>
/// <returns></returns>
/// <summary>
/// GetEscapedText method
/// </summary>
public static string GetEscapedText(string text)
{
var bytes = Encoding.GetEncoding(28591).GetBytes(text);
return string.Concat(bytes.Select(b => string.Format(@"[{0:X2}]", (int)b)).ToArray());
}
/// <summary>
/// Gets debug text for a string
/// </summary>
/// <param name="text"></param>
/// <returns></returns>
/// <summary>
/// GetDebugText method
/// </summary>
public static string GetDebugText(string text)
{
return Regex.Replace(text, @"[^\u0020-\u007E]", a => GetEscapedText(a.Value));
}
}
}

View File

@@ -1,289 +0,0 @@
using System;
using Crestron.SimplSharpPro;
using Crestron.SimplSharpPro.DM;
using Newtonsoft.Json;
using PepperDash.Essentials.Core.Config;
using PepperDash.Essentials.Core.Devices;
using PepperDash.Essentials.Core.Routing;
using Serilog.Events;
namespace PepperDash.Essentials.Core.Communications
{
/// <summary>
///
/// </summary>
public class CommFactory
{
public static EssentialsControlPropertiesConfig GetControlPropertiesConfig(DeviceConfig deviceConfig)
{
try
{
return JsonConvert.DeserializeObject<EssentialsControlPropertiesConfig>
(deviceConfig.Properties["control"].ToString());
//Debug.LogMessage(LogEventLevel.Verbose, "Control TEST: {0}", JsonConvert.SerializeObject(controlConfig));
}
catch (Exception e)
{
Debug.LogMessage(LogEventLevel.Information, "ERROR: [{0}] Control properties deserialize failed:\r{1}", deviceConfig.Key, e);
return null;
}
}
/// <summary>
/// Returns a comm method of either com port, TCP, SSH, and puts this into the DeviceManager
/// </summary>
/// <param name="deviceConfig">The Device config object</param>
/// <summary>
/// CreateCommForDevice method
/// </summary>
public static IBasicCommunication CreateCommForDevice(DeviceConfig deviceConfig)
{
EssentialsControlPropertiesConfig controlConfig = GetControlPropertiesConfig(deviceConfig);
if (controlConfig == null)
return null;
IBasicCommunication comm = null;
try
{
var c = controlConfig.TcpSshProperties;
switch (controlConfig.Method)
{
case eControlMethod.Com:
comm = new ComPortController(deviceConfig.Key + "-com", GetComPort, controlConfig.ComParams.Value, controlConfig);
break;
case eControlMethod.Cec:
comm = new CecPortController(deviceConfig.Key + "-cec", GetCecPort, controlConfig);
break;
case eControlMethod.IR:
break;
case eControlMethod.Ssh:
{
var ssh = new GenericSshClient(deviceConfig.Key + "-ssh", c.Address, c.Port, c.Username, c.Password);
ssh.AutoReconnect = c.AutoReconnect;
if(ssh.AutoReconnect)
ssh.AutoReconnectIntervalMs = c.AutoReconnectIntervalMs;
comm = ssh;
break;
}
case eControlMethod.Tcpip:
{
var tcp = new GenericTcpIpClient(deviceConfig.Key + "-tcp", c.Address, c.Port, c.BufferSize);
tcp.AutoReconnect = c.AutoReconnect;
if (tcp.AutoReconnect)
tcp.AutoReconnectIntervalMs = c.AutoReconnectIntervalMs;
comm = tcp;
break;
}
case eControlMethod.Udp:
{
var udp = new GenericUdpServer(deviceConfig.Key + "-udp", c.Address, c.Port, c.BufferSize);
comm = udp;
break;
}
case eControlMethod.Telnet:
break;
case eControlMethod.SecureTcpIp:
{
var secureTcp = new GenericSecureTcpIpClient(deviceConfig.Key + "-secureTcp", c.Address, c.Port, c.BufferSize);
secureTcp.AutoReconnect = c.AutoReconnect;
if (secureTcp.AutoReconnect)
secureTcp.AutoReconnectIntervalMs = c.AutoReconnectIntervalMs;
comm = secureTcp;
break;
}
default:
break;
}
}
catch (Exception e)
{
Debug.LogMessage(LogEventLevel.Information, "Cannot create communication from JSON:\r{0}\r\rException:\r{1}",
deviceConfig.Properties.ToString(), e);
}
// put it in the device manager if it's the right flavor
var comDev = comm as Device;
if (comDev != null)
DeviceManager.AddDevice(comDev);
return comm;
}
/// <summary>
/// GetComPort method
/// </summary>
public static ComPort GetComPort(EssentialsControlPropertiesConfig config)
{
var comPar = config.ComParams;
var dev = GetIComPortsDeviceFromManagedDevice(config.ControlPortDevKey);
if (dev != null && config.ControlPortNumber <= dev.NumberOfComPorts)
return dev.ComPorts[config.ControlPortNumber.Value];
Debug.LogMessage(LogEventLevel.Information, "GetComPort: Device '{0}' does not have com port {1}", config.ControlPortDevKey, config.ControlPortNumber);
return null;
}
/// <summary>
/// Gets an ICec port from a RoutingInput or RoutingOutput on a device
/// </summary>
/// <param name="config"></param>
/// <returns></returns>
/// <summary>
/// GetCecPort method
/// </summary>
public static ICec GetCecPort(ControlPropertiesConfig config)
{
try
{
var dev = DeviceManager.GetDeviceForKey(config.ControlPortDevKey);
Debug.LogMessage(LogEventLevel.Information, "GetCecPort: device '{0}' {1}", config.ControlPortDevKey, dev == null
? "is not valid, failed to get cec port"
: "found in device manager, attempting to get cec port");
if (dev == null)
return null;
if (string.IsNullOrEmpty(config.ControlPortName))
{
Debug.LogMessage(LogEventLevel.Information, "GetCecPort: '{0}' - Configuration missing 'ControlPortName'", config.ControlPortDevKey);
return null;
}
var inputsOutputs = dev as IRoutingInputsOutputs;
if (inputsOutputs == null)
{
Debug.LogMessage(LogEventLevel.Information, "GetCecPort: Device '{0}' does not support IRoutingInputsOutputs, failed to get CEC port called '{1}'",
config.ControlPortDevKey, config.ControlPortName);
return null;
}
var inputPort = inputsOutputs.InputPorts[config.ControlPortName];
if (inputPort != null && inputPort.Port is ICec)
return inputPort.Port as ICec;
var outputPort = inputsOutputs.OutputPorts[config.ControlPortName];
if (outputPort != null && outputPort.Port is ICec)
return outputPort.Port as ICec;
}
catch (Exception ex)
{
Debug.LogMessage(LogEventLevel.Debug, "GetCecPort Exception Message: {0}", ex.Message);
Debug.LogMessage(LogEventLevel.Verbose, "GetCecPort Exception StackTrace: {0}", ex.StackTrace);
if (ex.InnerException != null)
Debug.LogMessage(LogEventLevel.Information, "GetCecPort Exception InnerException: {0}", ex.InnerException);
}
Debug.LogMessage(LogEventLevel.Information, "GetCecPort: Device '{0}' does not have a CEC port called '{1}'",
config.ControlPortDevKey, config.ControlPortName);
return null;
}
/// <summary>
/// Helper to grab the IComPorts device for this PortDeviceKey. Key "controlSystem" will
/// return the ControlSystem object from the Global class.
/// </summary>
/// <returns>IComPorts device or null if the device is not found or does not implement IComPorts</returns>
/// <summary>
/// GetIComPortsDeviceFromManagedDevice method
/// </summary>
public static IComPorts GetIComPortsDeviceFromManagedDevice(string ComPortDevKey)
{
if ((ComPortDevKey.Equals("controlSystem", StringComparison.OrdinalIgnoreCase)
|| ComPortDevKey.Equals("processor", StringComparison.OrdinalIgnoreCase))
&& Global.ControlSystem is IComPorts)
return Global.ControlSystem;
else
{
var dev = DeviceManager.GetDeviceForKey(ComPortDevKey) as IComPorts;
if (dev == null)
Debug.LogMessage(LogEventLevel.Information, "ComPortConfig: Cannot find com port device '{0}'", ComPortDevKey);
return dev;
}
}
}
/// <summary>
/// Represents a EssentialsControlPropertiesConfig
/// </summary>
public class EssentialsControlPropertiesConfig :
ControlPropertiesConfig
{
[JsonProperty("comParams", NullValueHandling = NullValueHandling.Ignore)]
[JsonConverter(typeof(ComSpecJsonConverter))]
public ComPort.ComPortSpec? ComParams { get; set; }
[JsonProperty("cresnetId", NullValueHandling = NullValueHandling.Ignore)]
public string CresnetId { get; set; }
/// <summary>
/// Attempts to provide uint conversion of string CresnetId
/// </summary>
[JsonIgnore]
public uint CresnetIdInt
{
get
{
try
{
return Convert.ToUInt32(CresnetId, 16);
}
catch (Exception)
{
throw new FormatException(string.Format("ERROR:Unable to convert Cresnet ID: {0} to hex. Error:\n{1}", CresnetId));
}
}
}
[JsonProperty("infinetId", NullValueHandling = NullValueHandling.Ignore)]
/// <summary>
/// Gets or sets the InfinetId
/// </summary>
public string InfinetId { get; set; }
/// <summary>
/// Attepmts to provide uiont conversion of string InifinetId
/// </summary>
[JsonIgnore]
public uint InfinetIdInt
{
get
{
try
{
return Convert.ToUInt32(InfinetId, 16);
}
catch (Exception)
{
throw new FormatException(string.Format("ERROR:Unable to conver Infinet ID: {0} to hex. Error:\n{1}", InfinetId));
}
}
}
}
/// <summary>
/// Represents a IrControlSpec
/// </summary>
public class IrControlSpec
{
/// <summary>
/// Gets or sets the PortDeviceKey
/// </summary>
public string PortDeviceKey { get; set; }
/// <summary>
/// Gets or sets the PortNumber
/// </summary>
public uint PortNumber { get; set; }
/// <summary>
/// Gets or sets the File
/// </summary>
public string File { get; set; }
}
}

View File

@@ -1,91 +0,0 @@
using System;
namespace PepperDash.Essentials.Core.Communications
{
/// <summary>
///
/// </summary>
public enum eGenericCommMethodStatusChangeType
{
/// <summary>
/// Connected
/// </summary>
Connected,
/// <summary>
/// Disconnected
/// </summary>
Disconnected
}
/// <summary>
/// This delegate defines handler for IBasicCommunication status changes
/// </summary>
/// <param name="comm">Device firing the status change</param>
/// <param name="status"></param>
public delegate void GenericCommMethodStatusHandler(IBasicCommunication comm, eGenericCommMethodStatusChangeType status);
/// <summary>
///
/// </summary>
public class GenericCommMethodReceiveBytesArgs : EventArgs
{
/// <summary>
/// Gets or sets the Bytes
/// </summary>
public byte[] Bytes { get; private set; }
/// <summary>
///
/// </summary>
/// <param name="bytes"></param>
public GenericCommMethodReceiveBytesArgs(byte[] bytes)
{
Bytes = bytes;
}
/// <summary>
/// S+ Constructor
/// </summary>
public GenericCommMethodReceiveBytesArgs() { }
}
/// <summary>
///
/// </summary>
public class GenericCommMethodReceiveTextArgs : EventArgs
{
/// <summary>
///
/// </summary>
public string Text { get; private set; }
/// <summary>
///
/// </summary>
public string Delimiter { get; private set; }
/// <summary>
///
/// </summary>
/// <param name="text"></param>
public GenericCommMethodReceiveTextArgs(string text)
{
Text = text;
}
/// <summary>
///
/// </summary>
/// <param name="text"></param>
/// <param name="delimiter"></param>
public GenericCommMethodReceiveTextArgs(string text, string delimiter)
:this(text)
{
Delimiter = delimiter;
}
/// <summary>
/// S+ Constructor
/// </summary>
public GenericCommMethodReceiveTextArgs() { }
}
}

View File

@@ -1,173 +0,0 @@
using System;
using System.Text;
using System.Text.RegularExpressions;
namespace PepperDash.Essentials.Core.Communications
{
/// <summary>
/// Defines the string event handler for line events on the gather
/// </summary>
/// <param name="text"></param>
public delegate void LineReceivedHandler(string text);
/// <summary>
/// Attaches to IBasicCommunication as a text gather
/// </summary>
public class CommunicationGather
{
/// <summary>
/// Event that fires when a line is received from the IBasicCommunication source.
/// The event merely contains the text, not an EventArgs type class.
/// </summary>
public event EventHandler<GenericCommMethodReceiveTextArgs> LineReceived;
/// <summary>
/// The communication port that this gathers on
/// </summary>
public ICommunicationReceiver Port { get; private set; }
/// <summary>
/// Default false. If true, the delimiter will be included in the line output
/// events
/// </summary>
public bool IncludeDelimiter { get; set; }
/// <summary>
/// For receive buffer
/// </summary>
StringBuilder ReceiveBuffer = new StringBuilder();
/// <summary>
/// Delimiter, like it says!
/// </summary>
char Delimiter;
string[] StringDelimiters;
/// <summary>
/// Constructor for using a char delimiter
/// </summary>
/// <param name="port"></param>
/// <param name="delimiter"></param>
public CommunicationGather(ICommunicationReceiver port, char delimiter)
{
Port = port;
Delimiter = delimiter;
port.TextReceived += new EventHandler<GenericCommMethodReceiveTextArgs>(Port_TextReceived);
}
/// <summary>
/// Constructor for using a single string delimiter
/// </summary>
/// <param name="port"></param>
/// <param name="delimiter"></param>
public CommunicationGather(ICommunicationReceiver port, string delimiter)
:this(port, new string[] { delimiter} )
{
}
/// <summary>
/// Constructor for using an array of string delimiters
/// </summary>
/// <param name="port"></param>
/// <param name="delimiters"></param>
public CommunicationGather(ICommunicationReceiver port, string[] delimiters)
{
Port = port;
StringDelimiters = delimiters;
port.TextReceived += Port_TextReceivedStringDelimiter;
}
/// <summary>
/// Stop method
/// </summary>
public void Stop()
{
Port.TextReceived -= Port_TextReceived;
Port.TextReceived -= Port_TextReceivedStringDelimiter;
}
/// <summary>
/// Handler for raw data coming from port
/// </summary>
void Port_TextReceived(object sender, GenericCommMethodReceiveTextArgs args)
{
var handler = LineReceived;
if (handler != null)
{
ReceiveBuffer.Append(args.Text);
var str = ReceiveBuffer.ToString();
var lines = str.Split(Delimiter);
if (lines.Length > 0)
{
for (int i = 0; i < lines.Length - 1; i++)
{
string strToSend = null;
if (IncludeDelimiter)
strToSend = lines[i] + Delimiter;
else
strToSend = lines[i];
handler(this, new GenericCommMethodReceiveTextArgs(strToSend));
}
ReceiveBuffer = new StringBuilder(lines[lines.Length - 1]);
}
}
}
/// <summary>
///
/// </summary>
/// <param name="sender"></param>
/// <param name="args"></param>
void Port_TextReceivedStringDelimiter(object sender, GenericCommMethodReceiveTextArgs args)
{
var handler = LineReceived;
if (handler != null)
{
// Receive buffer should either be empty or not contain the delimiter
// If the line does not have a delimiter, append the
ReceiveBuffer.Append(args.Text);
var str = ReceiveBuffer.ToString();
// Case: Receiving DEVICE get version\x0d\0x0a+OK "value":"1234"\x0d\x0a
// RX: DEV
// Split: (1) "DEV"
// RX: I
// Split: (1) "DEVI"
// RX: CE get version
// Split: (1) "DEVICE get version"
// RX: \x0d\x0a+OK "value":"1234"\x0d\x0a
// Split: (2) DEVICE get version, +OK "value":"1234"
// Iterate the delimiters and fire an event for any matching delimiter
foreach (var delimiter in StringDelimiters)
{
var lines = Regex.Split(str, delimiter);
if (lines.Length == 1)
continue;
for (int i = 0; i < lines.Length - 1; i++)
{
string strToSend = null;
if (IncludeDelimiter)
strToSend = lines[i] + delimiter;
else
strToSend = lines[i];
handler(this, new GenericCommMethodReceiveTextArgs(strToSend, delimiter));
}
ReceiveBuffer = new StringBuilder(lines[lines.Length - 1]);
}
}
}
/// <summary>
/// Deconstructor. Disconnects from port TextReceived events.
/// </summary>
~CommunicationGather()
{
Stop();
}
}
}

View File

@@ -1,182 +0,0 @@
using System;
using Crestron.SimplSharp;
namespace PepperDash.Essentials.Core.Communications
{
/// <summary>
/// Controls the ability to disable/enable debugging of TX/RX data sent to/from a device with a built in timer to disable
/// </summary>
public class CommunicationStreamDebugging
{
/// <summary>
/// Device Key that this instance configures
/// </summary>
public string ParentDeviceKey { get; private set; }
/// <summary>
/// Timer to disable automatically if not manually disabled
/// </summary>
private CTimer DebugExpiryPeriod;
/// <summary>
/// Gets or sets the DebugSetting
/// </summary>
public eStreamDebuggingSetting DebugSetting { get; private set; }
private uint _DebugTimeoutInMs;
private const uint _DefaultDebugTimeoutMin = 30;
/// <summary>
/// Timeout in Minutes
/// </summary>
public uint DebugTimeoutMinutes
{
get
{
return _DebugTimeoutInMs/60000;
}
}
/// <summary>
/// Gets or sets the RxStreamDebuggingIsEnabled
/// </summary>
public bool RxStreamDebuggingIsEnabled{ get; private set; }
/// <summary>
/// Indicates that transmit stream debugging is enabled
/// </summary>
public bool TxStreamDebuggingIsEnabled { get; private set; }
/// <summary>
/// Constructor
/// </summary>
/// <param name="parentDeviceKey"></param>
public CommunicationStreamDebugging(string parentDeviceKey)
{
ParentDeviceKey = parentDeviceKey;
}
/// <summary>
/// Sets the debugging setting and if not setting to off, assumes the default of 30 mintues
/// </summary>
/// <param name="setting"></param>
/// <summary>
/// SetDebuggingWithDefaultTimeout method
/// </summary>
public void SetDebuggingWithDefaultTimeout(eStreamDebuggingSetting setting)
{
if (setting == eStreamDebuggingSetting.Off)
{
DisableDebugging();
return;
}
SetDebuggingWithSpecificTimeout(setting, _DefaultDebugTimeoutMin);
}
/// <summary>
/// Sets the debugging setting for the specified number of minutes
/// </summary>
/// <param name="setting"></param>
/// <param name="minutes"></param>
/// <summary>
/// SetDebuggingWithSpecificTimeout method
/// </summary>
public void SetDebuggingWithSpecificTimeout(eStreamDebuggingSetting setting, uint minutes)
{
if (setting == eStreamDebuggingSetting.Off)
{
DisableDebugging();
return;
}
_DebugTimeoutInMs = minutes * 60000;
StopDebugTimer();
DebugExpiryPeriod = new CTimer((o) => DisableDebugging(), _DebugTimeoutInMs);
if ((setting & eStreamDebuggingSetting.Rx) == eStreamDebuggingSetting.Rx)
RxStreamDebuggingIsEnabled = true;
if ((setting & eStreamDebuggingSetting.Tx) == eStreamDebuggingSetting.Tx)
TxStreamDebuggingIsEnabled = true;
Debug.SetDeviceDebugSettings(ParentDeviceKey, setting);
}
/// <summary>
/// Disabled debugging
/// </summary>
private void DisableDebugging()
{
StopDebugTimer();
Debug.SetDeviceDebugSettings(ParentDeviceKey, eStreamDebuggingSetting.Off);
}
private void StopDebugTimer()
{
RxStreamDebuggingIsEnabled = false;
TxStreamDebuggingIsEnabled = false;
if (DebugExpiryPeriod == null)
{
return;
}
DebugExpiryPeriod.Stop();
DebugExpiryPeriod.Dispose();
DebugExpiryPeriod = null;
}
}
/// <summary>
/// The available settings for stream debugging
/// </summary>
[Flags]
/// <summary>
/// Enumeration of eStreamDebuggingSetting values
/// </summary>
public enum eStreamDebuggingSetting
{
/// <summary>
/// Debug off
/// </summary>
Off = 0,
/// <summary>
/// Debug received data
/// </summary>
Rx = 1,
/// <summary>
/// Debug transmitted data
/// </summary>
Tx = 2,
/// <summary>
/// Debug both received and transmitted data
/// </summary>
Both = Rx | Tx
}
/// <summary>
/// The available settings for stream debugging response types
/// </summary>
[Flags]
public enum eStreamDebuggingDataTypeSettings
{
/// <summary>
/// Debug data in byte format
/// </summary>
Bytes = 0,
/// <summary>
/// Debug data in text format
/// </summary>
Text = 1,
/// <summary>
/// Debug data in both byte and text formats
/// </summary>
Both = Bytes | Text,
}
}

View File

@@ -1,92 +0,0 @@
using System;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
namespace PepperDash.Essentials.Core.Communications
{
/// <summary>
/// Represents a ControlPropertiesConfig
/// </summary>
public class ControlPropertiesConfig
{
/// <summary>
/// The method of control
/// </summary>
[JsonProperty("method")]
[JsonConverter(typeof(StringEnumConverter))]
public eControlMethod Method { get; set; }
/// <summary>
/// The key of the device that contains the control port
/// </summary>
[JsonProperty("controlPortDevKey", NullValueHandling = NullValueHandling.Ignore)]
public string ControlPortDevKey { get; set; }
/// <summary>
/// The number of the control port on the device specified by ControlPortDevKey
/// </summary>
[JsonProperty("controlPortNumber", NullValueHandling = NullValueHandling.Ignore)] // In case "null" is present in config on this value
public uint? ControlPortNumber { get; set; }
/// <summary>
/// The name of the control port on the device specified by ControlPortDevKey
/// </summary>
[JsonProperty("controlPortName", NullValueHandling = NullValueHandling.Ignore)] // In case "null" is present in config on this value
public string ControlPortName { get; set; }
/// <summary>
/// Properties for ethernet based communications
/// </summary>
[JsonProperty("tcpSshProperties", NullValueHandling = NullValueHandling.Ignore)]
public TcpSshPropertiesConfig TcpSshProperties { get; set; }
/// <summary>
/// The filename and path for the IR file
/// </summary>
[JsonProperty("irFile", NullValueHandling = NullValueHandling.Ignore)]
public string IrFile { get; set; }
/// <summary>
/// The IpId of a Crestron device
/// </summary>
[JsonProperty("ipId", NullValueHandling = NullValueHandling.Ignore)]
public string IpId { get; set; }
/// <summary>
/// Readonly uint representation of the IpId
/// </summary>
[JsonIgnore]
public uint IpIdInt { get { return Convert.ToUInt32(IpId, 16); } }
/// <summary>
/// Char indicating end of line
/// </summary>
[JsonProperty("endOfLineChar", NullValueHandling = NullValueHandling.Ignore)]
public char EndOfLineChar { get; set; }
/// <summary>
/// Defaults to Environment.NewLine;
/// </summary>
[JsonProperty("endOfLineString", NullValueHandling = NullValueHandling.Ignore)]
public string EndOfLineString { get; set; }
/// <summary>
/// Indicates
/// </summary>
[JsonProperty("deviceReadyResponsePattern", NullValueHandling = NullValueHandling.Ignore)]
public string DeviceReadyResponsePattern { get; set; }
/// <summary>
/// Used when communcating to programs running in VC-4
/// </summary>
[JsonProperty("roomId", NullValueHandling = NullValueHandling.Ignore)]
public string RoomId { get; set; }
/// <summary>
/// Constructor
/// </summary>
public ControlPropertiesConfig()
{
}
}
}

View File

@@ -1,247 +0,0 @@
/*PepperDash Technology Corp.
Copyright: 2017
------------------------------------
***Notice of Ownership and Copyright***
The material in which this notice appears is the property of PepperDash Technology Corporation,
which claims copyright under the laws of the United States of America in the entire body of material
and in all parts thereof, regardless of the use to which it is being put. Any use, in whole or in part,
of this material by another party without the express written permission of PepperDash Technology Corporation is prohibited.
PepperDash Technology Corporation reserves all rights under applicable laws.
------------------------------------ */
using System;
using Crestron.SimplSharp.CrestronSockets;
namespace PepperDash.Essentials.Core.Communications
{
/// <summary>
/// Delegate for notifying of socket status changes
/// </summary>
/// <param name="client"></param>
public delegate void GenericSocketStatusChangeEventDelegate(ISocketStatus client);
/// <summary>
/// EventArgs class for socket status changes
/// </summary>
public class GenericSocketStatusChageEventArgs : EventArgs
{
/// <summary>
/// Gets or sets the Client
/// </summary>
public ISocketStatus Client { get; private set; }
/// <summary>
///
/// </summary>
/// <param name="client"></param>
public GenericSocketStatusChageEventArgs(ISocketStatus client)
{
Client = client;
}
/// <summary>
/// S+ Constructor
/// </summary>
public GenericSocketStatusChageEventArgs() { }
}
/// <summary>
/// Delegate for notifying of TCP Server state changes
/// </summary>
/// <param name="state"></param>
public delegate void GenericTcpServerStateChangedEventDelegate(ServerState state);
/// <summary>
/// EventArgs class for TCP Server state changes
/// </summary>
public class GenericTcpServerStateChangedEventArgs : EventArgs
{
/// <summary>
/// Gets or sets the State
/// </summary>
public ServerState State { get; private set; }
/// <summary>
///
/// </summary>
/// <param name="state"></param>
public GenericTcpServerStateChangedEventArgs(ServerState state)
{
State = state;
}
/// <summary>
/// S+ Constructor
/// </summary>
public GenericTcpServerStateChangedEventArgs() { }
}
/// <summary>
/// Delegate for TCP Server socket status changes
/// </summary>
/// <param name="socket"></param>
/// <param name="clientIndex"></param>
/// <param name="clientStatus"></param>
public delegate void GenericTcpServerSocketStatusChangeEventDelegate(object socket, uint clientIndex, SocketStatus clientStatus);
/// <summary>
/// EventArgs for TCP server socket status changes
/// </summary>
public class GenericTcpServerSocketStatusChangeEventArgs : EventArgs
{
/// <summary>
///
/// </summary>
public object Socket { get; private set; }
/// <summary>
///
/// </summary>
public uint ReceivedFromClientIndex { get; private set; }
/// <summary>
///
/// </summary>
public SocketStatus ClientStatus { get; set; }
/// <summary>
///
/// </summary>
/// <param name="socket"></param>
/// <param name="clientStatus"></param>
public GenericTcpServerSocketStatusChangeEventArgs(object socket, SocketStatus clientStatus)
{
Socket = socket;
ClientStatus = clientStatus;
}
/// <summary>
///
/// </summary>
/// <param name="socket"></param>
/// <param name="clientIndex"></param>
/// <param name="clientStatus"></param>
public GenericTcpServerSocketStatusChangeEventArgs(object socket, uint clientIndex, SocketStatus clientStatus)
{
Socket = socket;
ReceivedFromClientIndex = clientIndex;
ClientStatus = clientStatus;
}
/// <summary>
/// S+ Constructor
/// </summary>
public GenericTcpServerSocketStatusChangeEventArgs() { }
}
/// <summary>
/// EventArgs for TCP server com method receive text
/// </summary>
public class GenericTcpServerCommMethodReceiveTextArgs : EventArgs
{
/// <summary>
///
/// </summary>
public uint ReceivedFromClientIndex { get; private set; }
/// <summary>
///
/// </summary>
public ushort ReceivedFromClientIndexShort
{
get
{
return (ushort)ReceivedFromClientIndex;
}
}
/// <summary>
/// Gets or sets the Text
/// </summary>
public string Text { get; private set; }
/// <summary>
///
/// </summary>
/// <param name="text"></param>
public GenericTcpServerCommMethodReceiveTextArgs(string text)
{
Text = text;
}
/// <summary>
///
/// </summary>
/// <param name="text"></param>
/// <param name="clientIndex"></param>
public GenericTcpServerCommMethodReceiveTextArgs(string text, uint clientIndex)
{
Text = text;
ReceivedFromClientIndex = clientIndex;
}
/// <summary>
/// S+ Constructor
/// </summary>
public GenericTcpServerCommMethodReceiveTextArgs() { }
}
/// <summary>
/// EventArgs for TCP server client ready for communication
/// </summary>
public class GenericTcpServerClientReadyForcommunicationsEventArgs : EventArgs
{
/// <summary>
///
/// </summary>
public bool IsReady;
/// <summary>
///
/// </summary>
/// <param name="isReady"></param>
public GenericTcpServerClientReadyForcommunicationsEventArgs(bool isReady)
{
IsReady = isReady;
}
/// <summary>
/// S+ Constructor
/// </summary>
public GenericTcpServerClientReadyForcommunicationsEventArgs() { }
}
/// <summary>
/// EventArgs for UDP connected
/// </summary>
public class GenericUdpConnectedEventArgs : EventArgs
{
/// <summary>
///
/// </summary>
public ushort UConnected;
/// <summary>
///
/// </summary>
public bool Connected;
/// <summary>
/// Constructor
/// </summary>
public GenericUdpConnectedEventArgs() { }
/// <summary>
///
/// </summary>
/// <param name="uconnected"></param>
public GenericUdpConnectedEventArgs(ushort uconnected)
{
UConnected = uconnected;
}
/// <summary>
///
/// </summary>
/// <param name="connected"></param>
public GenericUdpConnectedEventArgs(bool connected)
{
Connected = connected;
}
}
}

View File

@@ -1,21 +0,0 @@
using Newtonsoft.Json;
namespace PepperDash.Essentials.Core.Communications
{
/// <summary>
/// Describes a device that can automatically attempt to reconnect
/// </summary>
public interface IAutoReconnect
{
/// <summary>
/// Enable automatic recconnect
/// </summary>
[JsonProperty("autoReconnect")]
bool AutoReconnect { get; set; }
/// <summary>
/// Interval in ms to attempt automatic recconnections
/// </summary>
[JsonProperty("autoReconnectIntervalMs")]
int AutoReconnectIntervalMs { get; set; }
}
}

View File

@@ -1,20 +0,0 @@
namespace PepperDash.Essentials.Core.Communications
{
/// <summary>
/// Defines the contract for IBasicCommunication
/// </summary>
public interface IBasicCommunication : ICommunicationReceiver
{
/// <summary>
/// Send text to the device
/// </summary>
/// <param name="text"></param>
void SendText(string text);
/// <summary>
/// Send bytes to the device
/// </summary>
/// <param name="bytes"></param>
void SendBytes(byte[] bytes);
}
}

View File

@@ -1,10 +0,0 @@
namespace PepperDash.Essentials.Core.Communications
{
/// <summary>
/// Represents a device that implements IBasicCommunication and IStreamDebugging
/// </summary>
public interface IBasicCommunicationWithStreamDebugging : IBasicCommunication, IStreamDebugging
{
}
}

View File

@@ -1,13 +0,0 @@
using Crestron.SimplSharpPro;
namespace PepperDash.Essentials.Core.Communications
{
/// <summary>
///
/// </summary>
public interface IComPortsDevice
{
IComPorts Device { get; }
}
}

View File

@@ -1,34 +0,0 @@
using System;
using Newtonsoft.Json;
namespace PepperDash.Essentials.Core.Communications
{
/// <summary>
/// An incoming communication stream
/// </summary>
public interface ICommunicationReceiver : IKeyed
{
/// <summary>
/// Notifies of bytes received
/// </summary>
event EventHandler<GenericCommMethodReceiveBytesArgs> BytesReceived;
/// <summary>
/// Notifies of text received
/// </summary>
event EventHandler<GenericCommMethodReceiveTextArgs> TextReceived;
/// <summary>
/// Indicates connection status
/// </summary>
[JsonProperty("isConnected")]
bool IsConnected { get; }
/// <summary>
/// Connect to the device
/// </summary>
void Connect();
/// <summary>
/// Disconnect from the device
/// </summary>
void Disconnect();
}
}

View File

@@ -1,25 +0,0 @@
using System;
using Crestron.SimplSharp.CrestronSockets;
using Newtonsoft.Json;
namespace PepperDash.Essentials.Core.Communications
{
/// <summary>
/// For IBasicCommunication classes that have SocketStatus. GenericSshClient,
/// GenericTcpIpClient
/// </summary>
public interface ISocketStatus : IBasicCommunication
{
/// <summary>
/// Notifies of socket status changes
/// </summary>
event EventHandler<GenericSocketStatusChageEventArgs> ConnectionChange;
/// <summary>
/// The current socket status of the client
/// </summary>
[JsonProperty("clientStatus")]
[JsonConverter(typeof(Newtonsoft.Json.Converters.StringEnumConverter))]
SocketStatus ClientStatus { get; }
}
}

View File

@@ -1,10 +0,0 @@
namespace PepperDash.Essentials.Core.Communications
{
/// <summary>
/// Describes a device that implements ISocketStatus and IStreamDebugging
/// </summary>
public interface ISocketStatusWithStreamDebugging : ISocketStatus, IStreamDebugging
{
}
}

View File

@@ -1,16 +0,0 @@
using Newtonsoft.Json;
namespace PepperDash.Essentials.Core.Communications
{
/// <summary>
/// Represents a device with stream debugging capablities
/// </summary>
public interface IStreamDebugging
{
/// <summary>
/// Object to enable stream debugging
/// </summary>
[JsonProperty("streamDebugging")]
CommunicationStreamDebugging StreamDebugging { get; }
}
}

View File

@@ -1,59 +0,0 @@
using Newtonsoft.Json;
namespace PepperDash.Essentials.Core.Communications
{
/// <summary>
/// Represents a TcpClientConfigObject
/// </summary>
public class TcpClientConfigObject
{
/// <summary>
/// TcpSsh Properties
/// </summary>
[JsonProperty("control")]
public ControlPropertiesConfig Control { get; set; }
/// <summary>
/// Bool value for secure. Currently not implemented in TCP sockets as they are not dynamic
/// </summary>
[JsonProperty("secure")]
public bool Secure { get; set; }
/// <summary>
/// Require a shared key that both server and client negotiate. If negotiation fails server disconnects the client
/// </summary>
[JsonProperty("sharedKeyRequired")]
public bool SharedKeyRequired { get; set; }
/// <summary>
/// The shared key that must match on the server and client
/// </summary>
[JsonProperty("sharedKey")]
public string SharedKey { get; set; }
/// <summary>
/// Require a heartbeat on the client/server connection that will cause the server/client to disconnect if the heartbeat is not received.
/// heartbeats do not raise received events.
/// </summary>
[JsonProperty("heartbeatRequired")]
public bool HeartbeatRequired { get; set; }
/// <summary>
/// The interval in seconds for the heartbeat from the client. If not received client is disconnected
/// </summary>
[JsonProperty("heartbeatRequiredIntervalInSeconds")]
public ushort HeartbeatRequiredIntervalInSeconds { get; set; }
/// <summary>
/// HeartbeatString that will be checked against the message received. defaults to heartbeat if no string is provided.
/// </summary>
[JsonProperty("heartbeatStringToMatch")]
public string HeartbeatStringToMatch { get; set; }
/// <summary>
/// Receive Queue size must be greater than 20 or defaults to 20
/// </summary>
[JsonProperty("receiveQueueSize")]
public int ReceiveQueueSize { get; set; }
}
}

View File

@@ -1,54 +0,0 @@
namespace PepperDash.Essentials.Core.Communications
{
/// <summary>
/// Tcp Server Config object with properties for a tcp server with shared key and heartbeat capabilities
/// </summary>
public class TcpServerConfigObject
{
/// <summary>
/// Uique key
/// </summary>
public string Key { get; set; }
/// <summary>
/// Max Clients that the server will allow to connect.
/// </summary>
public ushort MaxClients { get; set; }
/// <summary>
/// Bool value for secure. Currently not implemented in TCP sockets as they are not dynamic
/// </summary>
public bool Secure { get; set; }
/// <summary>
/// Port for the server to listen on
/// </summary>
public int Port { get; set; }
/// <summary>
/// Require a shared key that both server and client negotiate. If negotiation fails server disconnects the client
/// </summary>
public bool SharedKeyRequired { get; set; }
/// <summary>
/// The shared key that must match on the server and client
/// </summary>
public string SharedKey { get; set; }
/// <summary>
/// Require a heartbeat on the client/server connection that will cause the server/client to disconnect if the heartbeat is not received.
/// heartbeats do not raise received events.
/// </summary>
public bool HeartbeatRequired { get; set; }
/// <summary>
/// The interval in seconds for the heartbeat from the client. If not received client is disconnected
/// </summary>
public ushort HeartbeatRequiredIntervalInSeconds { get; set; }
/// <summary>
/// HeartbeatString that will be checked against the message received. defaults to heartbeat if no string is provided.
/// </summary>
public string HeartbeatStringToMatch { get; set; }
/// <summary>
/// Client buffer size. See Crestron help. defaults to 2000 if not greater than 2000
/// </summary>
public int BufferSize { get; set; }
/// <summary>
/// Receive Queue size must be greater than 20 or defaults to 20
/// </summary>
public int ReceiveQueueSize { get; set; }
}
}

View File

@@ -1,59 +0,0 @@
using Newtonsoft.Json;
namespace PepperDash.Essentials.Core.Communications
{
/// <summary>
/// Represents a TcpSshPropertiesConfig
/// </summary>
public class TcpSshPropertiesConfig
{
/// <summary>
/// Address to connect to
/// </summary>
[JsonProperty(Required = Required.Always)]
public string Address { get; set; }
/// <summary>
/// Port to connect to
/// </summary>
[JsonProperty(Required = Required.Always)]
public int Port { get; set; }
/// <summary>
/// Username credential
/// </summary>
public string Username { get; set; }
/// <summary>
/// Gets or sets the Password
/// </summary>
public string Password { get; set; }
/// <summary>
/// Defaults to 32768
/// </summary>
public int BufferSize { get; set; }
/// <summary>
/// Gets or sets the AutoReconnect
/// </summary>
public bool AutoReconnect { get; set; }
/// <summary>
/// Gets or sets the AutoReconnectIntervalMs
/// </summary>
public int AutoReconnectIntervalMs { get; set; }
/// <summary>
/// Default constructor
/// </summary>
public TcpSshPropertiesConfig()
{
BufferSize = 32768;
AutoReconnect = true;
AutoReconnectIntervalMs = 5000;
Username = "";
Password = "";
}
}
}

View File

@@ -1,73 +0,0 @@
namespace PepperDash.Essentials.Core.Communications
{
/// <summary>
/// Crestron Control Methods for a comm object
/// </summary>
public enum eControlMethod
{
/// <summary>
///
/// </summary>
None = 0,
/// <summary>
/// RS232/422/485
/// </summary>
Com,
/// <summary>
/// Crestron IpId (most Crestron ethernet devices)
/// </summary>
IpId,
/// <summary>
/// Crestron IpIdTcp (HD-MD series, etc.)
/// </summary>
IpidTcp,
/// <summary>
/// Crestron IR control
/// </summary>
IR,
/// <summary>
/// SSH client
/// </summary>
Ssh,
/// <summary>
/// TCP/IP client
/// </summary>
Tcpip,
/// <summary>
/// Telnet
/// </summary>
Telnet,
/// <summary>
/// Crestnet device
/// </summary>
Cresnet,
/// <summary>
/// CEC Control, via a DM HDMI port
/// </summary>
Cec,
/// <summary>
/// UDP Server
/// </summary>
Udp,
/// <summary>
/// HTTP client
/// </summary>
Http,
/// <summary>
/// HTTPS client
/// </summary>
Https,
/// <summary>
/// Websocket client
/// </summary>
Ws,
/// <summary>
/// Secure Websocket client
/// </summary>
Wss,
/// <summary>
/// Secure TCP/IP
/// </summary>
SecureTcpIp
}
}

View File

@@ -1,6 +1,10 @@
using Newtonsoft.Json;
using PepperDash.Essentials.Core.Devices;
using Crestron.SimplSharpPro;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace PepperDash.Essentials.Core.Config
{

View File

@@ -0,0 +1,29 @@
using System;
using Newtonsoft.Json;
namespace PepperDash.Essentials.Core.Config
{
/// <summary>
/// Represents the base properties for a streaming device.
/// </summary>
public class BaseStreamingDeviceProperties
{
/// <summary>
/// The multicast video address for the streaming device.
/// </summary>
[JsonProperty("multicastVideoAddress", NullValueHandling = NullValueHandling.Ignore)]
public string MulticastVideoAddress { get; set; }
/// <summary>
/// The multicast audio address for the streaming device.
/// </summary>
[JsonProperty("multicastAudioAddress", NullValueHandling = NullValueHandling.Ignore)]
public string MulticastAudioAddress { get; set; }
/// <summary>
/// The URL for the streaming device's media stream.
/// </summary>
[JsonProperty("streamUrl", NullValueHandling = NullValueHandling.Ignore)]
public string StreamUrl { get; set; }
}
}

View File

@@ -6,7 +6,6 @@ using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using PepperDash.Essentials.Core.Devices;
using PepperDash.Essentials.Core.Routing;
namespace PepperDash.Essentials.Core.Config
{

View File

@@ -1,4 +1,15 @@
namespace PepperDash.Essentials.Core.Config
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Crestron.SimplSharp;
using PepperDash.Essentials.Core;
using PepperDash.Core;
using Newtonsoft.Json.Linq;
namespace PepperDash.Essentials.Core.Config
{
/// <summary>
/// Represents a ConfigPropertiesHelpers

View File

@@ -1,9 +1,15 @@
using System;
using System.Collections.Generic;
using Crestron.SimplSharp;
using Crestron.SimplSharp.CrestronIO;
using Crestron.SimplSharpPro;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using PepperDash.Core;
using PepperDash.Essentials.Core;
namespace PepperDash.Essentials.Core.Config
{
@@ -12,41 +18,41 @@ namespace PepperDash.Essentials.Core.Config
/// </summary>
public class DeviceConfig
{
[JsonProperty("key")]
/// <summary>
/// Gets or sets the Key
/// </summary>
[JsonProperty("key")]
public string Key { get; set; }
[JsonProperty("uid")]
/// <summary>
/// Gets or sets the Uid
/// </summary>
[JsonProperty("uid")]
public int Uid { get; set; }
[JsonProperty("name")]
/// <summary>
/// Gets or sets the Name
/// </summary>
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("group")]
/// <summary>
/// Gets or sets the Group
/// </summary>
[JsonProperty("group")]
public string Group { get; set; }
[JsonProperty("type")]
/// <summary>
/// Gets or sets the Type
/// </summary>
[JsonProperty("type")]
public string Type { get; set; }
[JsonProperty("properties")]
[JsonConverter(typeof(DevicePropertiesConverter))]
/// <summary>
/// Gets or sets the Properties
/// </summary>
[JsonProperty("properties")]
[JsonConverter(typeof(DevicePropertiesConverter))]
public JToken Properties { get; set; }
public DeviceConfig(DeviceConfig dc)
@@ -62,7 +68,7 @@ namespace PepperDash.Essentials.Core.Config
//Properties = JToken.FromObject(dc.Properties);
}
public DeviceConfig() {}
public DeviceConfig() { }
}
/// <summary>

View File

@@ -5,10 +5,13 @@ using System.Linq;
using System.Text;
using Crestron.SimplSharp;
using Crestron.SimplSharp.CrestronIO;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using PepperDash.Core;
using PepperDash.Core.Config;
using Serilog.Events;
namespace PepperDash.Essentials.Core.Config.Essentials
namespace PepperDash.Essentials.Core.Config
{
/// <summary>
/// Loads the ConfigObject from the file
@@ -34,7 +37,7 @@ namespace PepperDash.Essentials.Core.Config.Essentials
// Check for local config file first
var filePath = Global.FilePathPrefix + ConfigWriter.LocalConfigFolder + Global.DirectorySeparator + Global.ConfigFileName;
var localConfigFound = false;
bool localConfigFound = false;
Debug.LogMessage(LogEventLevel.Information, "Attempting to load Local config file: '{0}'", filePath);
@@ -110,7 +113,7 @@ namespace PepperDash.Essentials.Core.Config.Essentials
}
// Read the file
using (var fs = new StreamReader(filePath))
using (StreamReader fs = new StreamReader(filePath))
{
Debug.LogMessage(LogEventLevel.Information, "Loading config file: '{0}'", filePath);
@@ -121,22 +124,35 @@ namespace PepperDash.Essentials.Core.Config.Essentials
Debug.LogMessage(LogEventLevel.Information, "Successfully Loaded Local Config");
return true;
}
}
else
{
var doubleObj = JObject.Parse(fs.ReadToEnd());
ConfigObject = PortalConfigReader.MergeConfigs(doubleObj).ToObject<EssentialsConfig>();
var parsedConfig = JObject.Parse(fs.ReadToEnd());
// Extract SystemUrl and TemplateUrl into final config output
if (doubleObj["system_url"] != null)
// Check if it's a v2 config (check for "version" node)
// this means it's already merged by the Portal API
// from the v2 config tool
var isV2Config = parsedConfig["versions"] != null;
if (isV2Config)
{
ConfigObject.SystemUrl = doubleObj["system_url"].Value<string>();
Debug.LogMessage(LogEventLevel.Information, "Config file is a v2 format, no merge necessary.");
ConfigObject = parsedConfig.ToObject<EssentialsConfig>();
Debug.LogMessage(LogEventLevel.Information, "Successfully Loaded v2 Config");
return true;
}
if (doubleObj["template_url"] != null)
// Extract SystemUrl and TemplateUrl into final config output
ConfigObject = PortalConfigReader.MergeConfigs(parsedConfig).ToObject<EssentialsConfig>();
if (parsedConfig["system_url"] != null)
{
ConfigObject.TemplateUrl = doubleObj["template_url"].Value<string>();
ConfigObject.SystemUrl = parsedConfig["system_url"].Value<string>();
}
if (parsedConfig["template_url"] != null)
{
ConfigObject.TemplateUrl = parsedConfig["template_url"].Value<string>();
}
}
@@ -210,7 +226,7 @@ namespace PepperDash.Essentials.Core.Config.Essentials
{
debugStringWidth = 51;
}
var qualifier = filePathLength % 2 != 0
var qualifier = (filePathLength % 2 != 0)
? " Using Local Config File "
: " Using Local Config File ";
var bookend1 = (debugStringWidth - qualifier.Length) / 2;

View File

@@ -1,13 +1,20 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Crestron.SimplSharp;
using Crestron.SimplSharp.CrestronIO;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Crestron.SimplSharp.Net.Http;
using Crestron.SimplSharpPro.Diagnostics;
using PepperDash.Core;
using Serilog.Events;
namespace PepperDash.Essentials.Core.Config.Essentials
namespace PepperDash.Essentials.Core.Config
{
public static class ConfigUpdater
{
@@ -139,7 +146,7 @@ namespace PepperDash.Essentials.Core.Config.Essentials
{
Debug.LogMessage(LogEventLevel.Information, "{0} Existing files found in archive folder. Deleting.", archivedConfigFiles.Length);
for (var i = 0; i < archivedConfigFiles.Length; i++ )
for (int i = 0; i < archivedConfigFiles.Length; i++ )
{
var file = archivedConfigFiles[i];
Debug.LogMessage(LogEventLevel.Information, "Deleting archived file: '{0}'", file.FullName);
@@ -189,7 +196,7 @@ namespace PepperDash.Essentials.Core.Config.Essentials
OnStatusUpdate(eUpdateStatus.RestartingProgram);
var response = string.Empty;
string response = string.Empty;
CrestronConsole.SendControlSystemCommand(string.Format("progreset -p:{0}", InitialParametersClass.ApplicationNumber), ref response);

View File

@@ -1,14 +1,17 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Crestron.SimplSharp;
using Crestron.SimplSharp.CrestronIO;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using PepperDash.Core;
using Serilog.Events;
namespace PepperDash.Essentials.Core.Config.Essentials
namespace PepperDash.Essentials.Core.Config
{
/// <summary>
/// Responsible for updating config at runtime, and writing the updates out to a local file
@@ -33,7 +36,7 @@ namespace PepperDash.Essentials.Core.Config.Essentials
/// </summary>
public static bool UpdateDeviceProperties(string deviceKey, JToken properties)
{
var success = false;
bool success = false;
// Get the current device config
var deviceConfig = ConfigReader.ConfigObject.Devices.FirstOrDefault(d => d.Key.Equals(deviceKey));
@@ -58,7 +61,7 @@ namespace PepperDash.Essentials.Core.Config.Essentials
/// </summary>
public static bool UpdateDeviceConfig(DeviceConfig config)
{
var success = false;
bool success = false;
var deviceConfigIndex = ConfigReader.ConfigObject.Devices.FindIndex(d => d.Key.Equals(config.Key));
@@ -81,7 +84,7 @@ namespace PepperDash.Essentials.Core.Config.Essentials
/// </summary>
public static bool UpdateRoomConfig(DeviceConfig config)
{
var success = false;
bool success = false;
var roomConfigIndex = ConfigReader.ConfigObject.Rooms.FindIndex(d => d.Key.Equals(config.Key));
@@ -146,7 +149,7 @@ namespace PepperDash.Essentials.Core.Config.Essentials
{
if (fileLock.TryEnter())
{
using (var sw = new StreamWriter(filePath))
using (StreamWriter sw = new StreamWriter(filePath))
{
sw.Write(configData);
sw.Flush();

View File

@@ -1,21 +1,36 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using Crestron.SimplSharp.CrestronIO;
using Newtonsoft.Json;
namespace PepperDash.Essentials.Core.Config.Essentials
using PepperDash.Core;
namespace PepperDash.Essentials.Core.Config
{
/// <summary>
/// Loads the ConfigObject from the file
/// </summary>
public class EssentialsConfig : BasicConfig
{
[JsonProperty("system_url")]
/// <summary>
/// Gets or sets the SystemUrl
/// </summary>
[JsonProperty("system_url")]
public string SystemUrl { get; set; }
[JsonProperty("template_url")]
/// <summary>
/// Gets or sets the TemplateUrl
/// </summary>
[JsonProperty("template_url")]
public string TemplateUrl { get; set; }
/// <summary>
/// Gets the SystemUuid extracted from the SystemUrl
/// </summary>
[JsonProperty("systemUuid")]
public string SystemUuid
{
@@ -27,17 +42,20 @@ namespace PepperDash.Essentials.Core.Config.Essentials
if (SystemUrl.Contains("#"))
{
var result = Regex.Match(SystemUrl, @"https?:\/\/.*\/systems\/(.*)\/#.*");
var uuid = result.Groups[1].Value;
string uuid = result.Groups[1].Value;
return uuid;
} else
{
var result = Regex.Match(SystemUrl, @"https?:\/\/.*\/systems\/(.*)\/.*");
var uuid = result.Groups[1].Value;
string uuid = result.Groups[1].Value;
return uuid;
}
}
}
/// <summary>
/// Gets the TemplateUuid extracted from the TemplateUrl
/// </summary>
[JsonProperty("templateUuid")]
public string TemplateUuid
{
@@ -49,41 +67,95 @@ namespace PepperDash.Essentials.Core.Config.Essentials
if (TemplateUrl.Contains("#"))
{
var result = Regex.Match(TemplateUrl, @"https?:\/\/.*\/templates\/(.*)\/#.*");
var uuid = result.Groups[1].Value;
string uuid = result.Groups[1].Value;
return uuid;
} else
{
var result = Regex.Match(TemplateUrl, @"https?:\/\/.*\/system-templates\/(.*)\/system-template-versions\/(.*)\/.*");
var uuid = result.Groups[2].Value;
string uuid = result.Groups[2].Value;
return uuid;
}
}
}
[JsonProperty("rooms")]
/// <summary>
/// Gets or sets the Rooms
/// </summary>
[JsonProperty("rooms")]
public List<DeviceConfig> Rooms { get; set; }
/// <summary>
/// Gets or sets the Versions
/// </summary>
public VersionData Versions { get; set; }
/// <summary>
/// Initializes a new instance of the <see cref="EssentialsConfig"/> class.
/// </summary>
public EssentialsConfig()
: base()
{
Rooms = new List<DeviceConfig>();
}
}
/// <summary>
/// Represents a SystemTemplateConfigs
/// </summary>
public class SystemTemplateConfigs
/// <summary>
/// Represents version data for Essentials and its packages
/// </summary>
public class VersionData
{
/// <summary>
/// Gets or sets the Essentials version
/// </summary>
[JsonProperty("essentials")]
public NugetVersion Essentials { get; set; }
/// <summary>
/// Gets or sets the list of Packages
/// </summary>
[JsonProperty("packages")]
public List<NugetVersion> Packages { get; set; }
/// <summary>
/// Initializes a new instance of the <see cref="VersionData"/> class.
/// </summary>
public VersionData()
{
Packages = new List<NugetVersion>();
}
}
/// <summary>
/// Represents a NugetVersion
/// </summary>
public class NugetVersion
{
/// <summary>
/// Gets or sets the Version
/// </summary>
[JsonProperty("version")]
public string Version { get; set; }
/// <summary>
/// Gets or sets the PackageId
/// </summary>
[JsonProperty("packageId")]
public string PackageId { get; set; }
}
/// <summary>
/// Represents a SystemTemplateConfigs
/// </summary>
public class SystemTemplateConfigs
{
/// <summary>
/// Gets or sets the System
/// </summary>
/// <summary>
/// Gets or sets the System
/// </summary>
public EssentialsConfig System { get; set; }
/// <summary>
/// Gets or sets the Template
/// </summary>
public EssentialsConfig Template { get; set; }
}
}

View File

@@ -1,4 +1,10 @@
namespace PepperDash.Essentials.Core.Config
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace PepperDash.Essentials.Core
{
/// <summary>
/// Defines the contract for ILoadConfig

View File

@@ -2,7 +2,6 @@
using Crestron.SimplSharp;
using Newtonsoft.Json;
using PepperDash.Essentials.Core.Global;
using System;
using System.Collections.Generic;
@@ -113,7 +112,7 @@ namespace PepperDash.Essentials.Core.Config
/// The information gathered by the processor at runtime about it's NICs and their IP addresses.
/// </summary>
[JsonProperty("ipInfo")]
public Dictionary<short, Core.Global.EthernetAdapterInfo> IpInfo
public Dictionary<short, EthernetAdapterInfo> IpInfo
{
get
{

View File

@@ -1,4 +1,10 @@
namespace PepperDash.Essentials.Core.Config
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Crestron.SimplSharp;
namespace PepperDash.Essentials.Core.Config
{
/// <summary>
/// Represents a SourceDevicePropertiesConfigBase

View File

@@ -1,13 +1,13 @@
using System.Linq;
using System;
using System.Linq;
using Crestron.SimplSharpPro;
using Crestron.SimplSharpPro.DeviceSupport;
using PepperDash.Core;
using PepperDash.Core.JsonStandardObjects;
using PepperDash.Essentials.Core.Bridges;
using PepperDash.Essentials.Core.Devices;
using PepperDash.Essentials.Core.Feedbacks;
using PepperDash.Essentials.Core.Monitoring;
using Serilog.Events;
namespace PepperDash.Essentials.Core.Crestron
namespace PepperDash.Essentials.Core
{
public abstract class CrestronGenericBaseDevice : EssentialsDevice, IOnline, IHasFeedback, ICommunicationMonitor, IUsageTracking
{
@@ -16,7 +16,7 @@ namespace PepperDash.Essentials.Core.Crestron
/// <summary>
/// Gets or sets the Feedbacks
/// </summary>
public FeedbackCollection<Feedbacks.Feedback> Feedbacks { get; private set; }
public FeedbackCollection<Feedback> Feedbacks { get; private set; }
/// <summary>
/// Gets or sets the IsOnline
@@ -39,7 +39,7 @@ namespace PepperDash.Essentials.Core.Crestron
protected CrestronGenericBaseDevice(string key, string name, GenericBase hardware)
: base(key, name)
{
Feedbacks = new FeedbackCollection<Feedbacks.Feedback>();
Feedbacks = new FeedbackCollection<Feedback>();
Hardware = hardware;
IsOnline = new BoolFeedback("IsOnlineFeedback", () => Hardware.IsOnline);
@@ -53,7 +53,7 @@ namespace PepperDash.Essentials.Core.Crestron
protected CrestronGenericBaseDevice(string key, string name)
: base(key, name)
{
Feedbacks = new FeedbackCollection<Feedbacks.Feedback>();
Feedbacks = new FeedbackCollection<Feedback>();
}
@@ -142,7 +142,7 @@ namespace PepperDash.Essentials.Core.Crestron
/// <summary>
/// AddToFeedbackList method
/// </summary>
public void AddToFeedbackList(params Feedbacks.Feedback[] newFbs)
public void AddToFeedbackList(params Feedback[] newFbs)
{
foreach (var f in newFbs)
{
@@ -186,10 +186,8 @@ namespace PepperDash.Essentials.Core.Crestron
/// </summary>
public UsageTracking UsageTracker { get; set; }
FeedbackCollection<Feedbacks.Feedback> IHasFeedback.Feedbacks => throw new System.NotImplementedException();
#endregion
}
}
public abstract class CrestronGenericBridgeableBaseDevice : CrestronGenericBaseDevice, IBridgeAdvanced
{

View File

@@ -0,0 +1,128 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Crestron.SimplSharp;
using Crestron.SimplSharpPro;
using Crestron.SimplSharpPro.DeviceSupport;
using PepperDash.Core;
namespace PepperDash.Essentials.Core
{
/// <summary>
/// A bridge class to cover the basic features of GenericBase hardware
/// </summary>
public class CrestronGenericBaseDevice : Device, IOnline, IHasFeedback, ICommunicationMonitor, IUsageTracking
{
public virtual GenericBase Hardware { get; protected set; }
public BoolFeedback IsOnline { get; private set; }
public BoolFeedback IsRegistered { get; private set; }
public StringFeedback IpConnectionsText { get; private set; }
public CrestronGenericBaseDevice(string key, string name, GenericBase hardware)
: base(key, name)
{
Hardware = hardware;
IsOnline = new BoolFeedback(CommonBoolCue.IsOnlineFeedback, () => Hardware.IsOnline);
IsRegistered = new BoolFeedback(new Cue("IsRegistered", 0, eCueType.Bool), () => Hardware.Registered);
IpConnectionsText = new StringFeedback(CommonStringCue.IpConnectionsText, () =>
string.Join(",", Hardware.ConnectedIpList.Select(cip => cip.DeviceIpAddress).ToArray()));
CommunicationMonitor = new CrestronGenericBaseCommunicationMonitor(this, hardware, 120000, 300000);
}
/// <summary>
/// Make sure that overriding classes call this!
/// Registers the Crestron device, connects up to the base events, starts communication monitor
/// </summary>
public override bool CustomActivate()
{
Debug.LogMessage(LogEventLevel.Information, this, "Activating");
var response = Hardware.RegisterWithLogging(Key);
if (response != eDeviceRegistrationUnRegistrationResponse.Success)
{
<<<<<<< HEAD
Debug.LogMessage(LogEventLevel.Information, this, "ERROR: Cannot register Crestron device: {0}", response);
return false;
}
=======
Debug.LogMessage(LogEventLevel.Information, this, "ERROR: Cannot register Crestron device: {0}", response);
return false;
}
>>>>>>> origin/feature/ecs-342-neil
Hardware.OnlineStatusChange += new OnlineStatusChangeEventHandler(Hardware_OnlineStatusChange);
CommunicationMonitor.Start();
return true;
}
/// <summary>
/// This disconnects events and unregisters the base hardware device.
/// </summary>
/// <returns></returns>
public override bool Deactivate()
{
CommunicationMonitor.Stop();
Hardware.OnlineStatusChange -= Hardware_OnlineStatusChange;
return Hardware.UnRegister() == eDeviceRegistrationUnRegistrationResponse.Success;
}
/// <summary>
/// Returns a list containing the Outputs that we want to expose.
/// </summary>
public virtual List<Feedback> Feedbacks
{
get
{
return new List<Feedback>
{
IsOnline,
IsRegistered,
IpConnectionsText
};
}
}
void Hardware_OnlineStatusChange(GenericBase currentDevice, OnlineOfflineEventArgs args)
{
IsOnline.FireUpdate();
}
#region IStatusMonitor Members
public StatusMonitorBase CommunicationMonitor { get; private set; }
#endregion
#region IUsageTracking Members
public UsageTracking UsageTracker { get; set; }
#endregion
}
//***********************************************************************************
public class CrestronGenericBaseDeviceEventIds
{
public const uint IsOnline = 1;
public const uint IpConnectionsText =2;
}
/// <summary>
/// Adds logging to Register() failure
/// </summary>
public static class GenericBaseExtensions
{
public static eDeviceRegistrationUnRegistrationResponse RegisterWithLogging(this GenericBase device, string key)
{
var result = device.Register();
if (result != eDeviceRegistrationUnRegistrationResponse.Success)
{
Debug.LogMessage(LogEventLevel.Information, "Cannot register device '{0}': {1}", key, result);
}
return result;
}
}
}

Some files were not shown because too many files have changed in this diff Show More