Last active
December 8, 2016 20:08
-
-
Save dotMorten/e13551aafa8cc2f4c5bd939efa5f4fb3 to your computer and use it in GitHub Desktop.
Simple Iotivity Server and Light Resource created using .NET calling into the Iotivity C-API
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Runtime.InteropServices; | |
using System.Threading; | |
using System.Threading.Tasks; | |
namespace SimpleServerNet | |
{ | |
class Program | |
{ | |
private static CancellationTokenSource ct; | |
private static TaskCompletionSource<object> tcs; | |
static void Main(string[] args) | |
{ | |
Console.WriteLine("Initializing..."); | |
var initResult = OCInit1(OCMode.OC_SERVER, OCTransportFlags.OC_DEFAULT_FLAGS, OCTransportFlags.OC_DEFAULT_FLAGS); | |
if (initResult != OCStackResult.OC_STACK_OK) | |
{ | |
throw new Exception(initResult.ToString()); | |
} | |
//Start OCProcess Loop | |
ct = new CancellationTokenSource(); | |
tcs = new TaskCompletionSource<object>(); | |
ThreadPool.QueueUserWorkItem(async (s) => | |
{ | |
Console.WriteLine("Running OCStack. Press any key to close"); | |
while (!ct.IsCancellationRequested) | |
{ | |
var reprocessResult = OCProcess(); | |
await Task.Delay(1); | |
} | |
tcs.SetResult(null); | |
tcs = null; | |
ct = null; | |
}); | |
// Create an iotivity resource | |
IntPtr lightHandle = IntPtr.Zero; | |
OCStackResult res1 = OCCreateResource(out lightHandle, "core.light", "oic.if.baseline", "/light/1", handler.OCEntityHandler, IntPtr.Zero, OCResourceProperty.OC_DISCOVERABLE | OCResourceProperty.OC_OBSERVABLE); | |
// Run until keypress | |
var k = Console.ReadKey(); | |
Console.WriteLine("Cancelling..."); | |
ct.Cancel(); | |
tcs.Task.Wait(); | |
var stopResult = OCStop(); | |
} | |
private static CallbackHandler handler = new CallbackHandler(); | |
public class CallbackHandler | |
{ | |
public OCEntityHandlerResult OCEntityHandler(OCEntityHandlerFlag flag, OCEntityHandlerRequest entityHandlerRequest, IntPtr callbackParam) | |
{ | |
Console.Write("OCEntityHandler callback called. Flag: " + flag.ToString()); | |
return OCEntityHandlerResult.OC_EH_OK; | |
} | |
} | |
#region Interop Imports | |
internal static class Constants | |
{ | |
internal const string DLL_IMPORT_TARGET = "octbstack.dll"; | |
} | |
[DllImport(Constants.DLL_IMPORT_TARGET)] | |
internal static extern OCStackResult OCInit1(OCMode mode, OCTransportFlags serverFlags, OCTransportFlags clientFlags); | |
[DllImport(Constants.DLL_IMPORT_TARGET)] | |
internal static extern OCStackResult OCStop(); | |
[DllImport(Constants.DLL_IMPORT_TARGET)] | |
internal static extern OCStackResult OCProcess(); | |
[DllImport(Constants.DLL_IMPORT_TARGET)] | |
internal static extern OCStackResult OCCreateResource([Out] out IntPtr handle, | |
[MarshalAs(UnmanagedType.LPStr)] string resourceTypeName, | |
[MarshalAs(UnmanagedType.LPStr)] string resourceInterfaceName, | |
[MarshalAs(UnmanagedType.LPStr)] string uri, | |
OCEntityHandler entityHandler, | |
IntPtr callbackParam, | |
OCResourceProperty resourceProperties); | |
#endregion | |
#region OC Type Definitions | |
/** | |
* Incoming requests handled by the server. Requests are passed in as a parameter to the | |
* OCEntityHandler callback API. | |
* The OCEntityHandler callback API must be implemented in the application in order | |
* to receive these requests. | |
*/ | |
[StructLayout(LayoutKind.Sequential)] | |
internal class OCEntityHandlerRequest | |
{ | |
/** Associated resource.*/ | |
public IntPtr resource; | |
/** Associated request handle.*/ | |
public IntPtr requestHandle; | |
/** the REST method retrieved from received request PDU.*/ | |
public OCMethod method; | |
/** description of endpoint that sent the request.*/ | |
public OCDevAddr devAddr; | |
/** resource query send by client.*/ | |
[MarshalAs(UnmanagedType.LPStr)] | |
public string query; | |
/** Information associated with observation - valid only when OCEntityHandler flag includes | |
* ::OC_OBSERVE_FLAG.*/ | |
public OCObservationInfo obsInfo; | |
/** Number of the received vendor specific header options.*/ | |
public byte numRcvdVendorSpecificHeaderOptions; | |
/** Pointer to the array of the received vendor specific header options.*/ | |
public OCHeaderOption rcvdVendorSpecificHeaderOptions; | |
/** Message id.*/ | |
UInt16 messageID; | |
/** the payload from the request PDU.*/ | |
public OCPayload payload; | |
} | |
/** | |
* Data structure to encapsulate IPv4/IPv6/Contiki/lwIP device addresses. | |
* OCDevAddr must be the same as CAEndpoint (in CACommon.h). | |
*/ | |
[StructLayout(LayoutKind.Sequential)] | |
internal class OCDevAddr | |
{ | |
public OCDevAddr(UInt16 port) | |
{ | |
addr = new char[66]; | |
routeData = new char[66]; | |
deviceId = new char[37]; // 37 incl null terminating char | |
adapter = OCTransportAdapter.OC_ADAPTER_IP; | |
flags = OCTransportFlags.OC_IP_USE_V4; | |
this.port = port; | |
ifindex = 0; | |
} | |
/** adapter type.*/ | |
public OCTransportAdapter adapter; | |
/** transport modifiers.*/ | |
public OCTransportFlags flags; | |
/** for IP.*/ | |
public UInt16 port; | |
/** address for all adapters.*/ | |
public char[] addr; | |
/** usually zero for default interface.*/ | |
public UInt32 ifindex; | |
/** destination GatewayID:ClientId.*/ | |
public char[] routeData; | |
/** destination DeviceID.*/ | |
public char[] deviceId; | |
} | |
/** | |
* This structure will be used to define the vendor specific header options to be included | |
* in communication packets. | |
*/ | |
[StructLayout(LayoutKind.Sequential)] | |
internal class OCHeaderOption | |
{ | |
public OCHeaderOption() | |
{ | |
optionData = new byte[1024]; | |
} | |
/** The protocol ID this option applies to.*/ | |
public OCTransportProtocolID protocolID; | |
/** The header option ID which will be added to communication packets.*/ | |
public UInt16 optionID; | |
/** its length 191.*/ | |
public UInt16 optionLength; | |
/** pointer to its data.*/ | |
public byte[] optionData; | |
#if SUPPORTS_DEFAULT_CTOR | |
OCHeaderOption() = default; | |
OCHeaderOption(OCTransportProtocolID pid, | |
uint16_t optId, | |
uint16_t optlen, | |
const uint8_t* optData) | |
: protocolID(pid), | |
optionID(optId), | |
optionLength(optlen) | |
{ | |
// parameter includes the null terminator. | |
optionLength = optionLength < MAX_HEADER_OPTION_DATA_LENGTH ? | |
optionLength : MAX_HEADER_OPTION_DATA_LENGTH; | |
memcpy(optionData, optData, optionLength); | |
optionData[optionLength - 1] = '\0'; | |
} | |
#endif | |
} | |
[StructLayout(LayoutKind.Sequential)] | |
internal class OCPayload | |
{ | |
public OCPayload(OCPayloadType type = OCPayloadType.PAYLOAD_TYPE_INVALID) | |
{ | |
this.type = type; | |
} | |
/** The type of message that was received */ | |
public OCPayloadType type; | |
} | |
/** | |
* Possible returned values from entity handler. | |
*/ | |
[StructLayout(LayoutKind.Sequential)] | |
internal class OCObservationInfo | |
{ | |
/** Action associated with observation request.*/ | |
OCObserveAction action; | |
/** Identifier for observation being registered/deregistered.*/ | |
byte obsId; | |
} | |
#endregion | |
#region OC Enum Definitions | |
/** Enum to describe the type of object held by the OCPayload object.*/ | |
internal enum OCPayloadType | |
{ | |
/** Contents of the payload are invalid */ | |
PAYLOAD_TYPE_INVALID, | |
/** The payload is an OCDiscoveryPayload */ | |
PAYLOAD_TYPE_DISCOVERY, | |
/** The payload of the device */ | |
PAYLOAD_TYPE_DEVICE, | |
/** The payload type of the platform */ | |
PAYLOAD_TYPE_PLATFORM, | |
/** The payload is an OCRepPayload */ | |
PAYLOAD_TYPE_REPRESENTATION, | |
/** The payload is an OCSecurityPayload */ | |
PAYLOAD_TYPE_SECURITY, | |
/** The payload is an OCPresencePayload */ | |
PAYLOAD_TYPE_PRESENCE | |
} | |
/** | |
* Action associated with observation. | |
*/ | |
internal enum OCObserveAction | |
{ | |
/** To Register. */ | |
OC_OBSERVE_REGISTER = 0, | |
/** To Deregister. */ | |
OC_OBSERVE_DEREGISTER = 1, | |
/** Others. */ | |
OC_OBSERVE_NO_OPTION = 2, | |
} | |
internal enum OCMode | |
{ | |
OC_CLIENT = 0, | |
OC_SERVER, | |
OC_CLIENT_SERVER, | |
OC_GATEWAY /**< Client server mode along with routing capabilities.*/ | |
} | |
/** | |
* Declares Stack Results & Errors. | |
*/ | |
internal enum OCStackResult | |
{ | |
/** Success status code - START HERE.*/ | |
OC_STACK_OK = 0, /** 203, 205*/ | |
OC_STACK_RESOURCE_CREATED, /** 201*/ | |
OC_STACK_RESOURCE_DELETED, /** 202*/ | |
OC_STACK_CONTINUE, | |
OC_STACK_RESOURCE_CHANGED, /** 204*/ | |
/** Success status code - END HERE.*/ | |
/** Error status code - START HERE.*/ | |
OC_STACK_INVALID_URI = 20, | |
OC_STACK_INVALID_QUERY, /** 400*/ | |
OC_STACK_INVALID_IP, | |
OC_STACK_INVALID_PORT, | |
OC_STACK_INVALID_CALLBACK, | |
OC_STACK_INVALID_METHOD, | |
/** Invalid parameter.*/ | |
OC_STACK_INVALID_PARAM, | |
OC_STACK_INVALID_OBSERVE_PARAM, | |
OC_STACK_NO_MEMORY, | |
OC_STACK_COMM_ERROR, /** 504*/ | |
OC_STACK_TIMEOUT, | |
OC_STACK_ADAPTER_NOT_ENABLED, | |
OC_STACK_NOTIMPL, | |
/** Resource not found.*/ | |
OC_STACK_NO_RESOURCE, /** 404*/ | |
/** e.g: not supported method or interface.*/ | |
OC_STACK_RESOURCE_ERROR, | |
OC_STACK_SLOW_RESOURCE, | |
OC_STACK_DUPLICATE_REQUEST, | |
/** Resource has no registered observers.*/ | |
OC_STACK_NO_OBSERVERS, | |
OC_STACK_OBSERVER_NOT_FOUND, | |
OC_STACK_VIRTUAL_DO_NOT_HANDLE, | |
OC_STACK_INVALID_OPTION, /** 402*/ | |
/** The remote reply contained malformed data.*/ | |
OC_STACK_MALFORMED_RESPONSE, | |
OC_STACK_PERSISTENT_BUFFER_REQUIRED, | |
OC_STACK_INVALID_REQUEST_HANDLE, | |
OC_STACK_INVALID_DEVICE_INFO, | |
OC_STACK_INVALID_JSON, | |
/** Request is not authorized by Resource Server. */ | |
OC_STACK_UNAUTHORIZED_REQ, /** 401*/ | |
OC_STACK_TOO_LARGE_REQ, /** 413*/ | |
/** Error code from PDM */ | |
OC_STACK_PDM_IS_NOT_INITIALIZED, | |
OC_STACK_DUPLICATE_UUID, | |
OC_STACK_INCONSISTENT_DB, | |
/** | |
* Error code from OTM | |
* This error is pushed from DTLS interface when handshake failure happens | |
*/ | |
OC_STACK_AUTHENTICATION_FAILURE, | |
OC_STACK_NOT_ALLOWED_OXM, | |
/** Request come from endpoint which is not mapped to the resource. */ | |
OC_STACK_BAD_ENDPOINT, | |
/** Insert all new error codes here!.*/ | |
#if WITH_PRESENCE | |
OC_STACK_PRESENCE_STOPPED = 128, | |
OC_STACK_PRESENCE_TIMEOUT, | |
OC_STACK_PRESENCE_DO_NOT_HANDLE, | |
#endif | |
/** Request is denied by the user*/ | |
OC_STACK_USER_DENIED_REQ, | |
/** ERROR code from server */ | |
OC_STACK_FORBIDDEN_REQ, /** 403*/ | |
OC_STACK_INTERNAL_SERVER_ERROR, /** 500*/ | |
/** ERROR in stack.*/ | |
OC_STACK_ERROR = 255 | |
/** Error status code - END HERE.*/ | |
} | |
/** | |
* Enum layout assumes some targets have 16-bit integer (e.g., Arduino). | |
*/ | |
[Flags] | |
internal enum OCTransportFlags | |
{ | |
/** default flag is 0*/ | |
OC_DEFAULT_FLAGS = 0, | |
/** Insecure transport is the default (subject to change).*/ | |
/** secure the transport path*/ | |
OC_FLAG_SECURE = (1 << 4), | |
/** IPv4 & IPv6 auto-selection is the default.*/ | |
/** IP & TCP adapter only.*/ | |
OC_IP_USE_V6 = (1 << 5), | |
/** IP & TCP adapter only.*/ | |
OC_IP_USE_V4 = (1 << 6), | |
/** Multicast only.*/ | |
OC_MULTICAST = (1 << 7), | |
/** Link-Local multicast is the default multicast scope for IPv6. | |
* These are placed here to correspond to the IPv6 multicast address bits.*/ | |
/** IPv6 Interface-Local scope (loopback).*/ | |
OC_SCOPE_INTERFACE = 0x1, | |
/** IPv6 Link-Local scope (default).*/ | |
OC_SCOPE_LINK = 0x2, | |
/** IPv6 Realm-Local scope. */ | |
OC_SCOPE_REALM = 0x3, | |
/** IPv6 Admin-Local scope. */ | |
OC_SCOPE_ADMIN = 0x4, | |
/** IPv6 Site-Local scope. */ | |
OC_SCOPE_SITE = 0x5, | |
/** IPv6 Organization-Local scope. */ | |
OC_SCOPE_ORG = 0x8, | |
/**IPv6 Global scope. */ | |
OC_SCOPE_GLOBAL = 0xE, | |
} | |
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] | |
internal delegate OCEntityHandlerResult OCEntityHandler(OCEntityHandlerFlag flag, OCEntityHandlerRequest entityHandlerRequest, IntPtr callbackParam); | |
/** | |
* Possible returned values from entity handler. | |
*/ | |
internal enum OCEntityHandlerResult | |
{ | |
OC_EH_OK = 0, | |
OC_EH_ERROR, | |
OC_EH_SLOW, | |
OC_EH_RESOURCE_CREATED = 201, | |
OC_EH_RESOURCE_DELETED = 202, | |
OC_EH_VALID = 203, | |
OC_EH_CHANGED = 204, | |
OC_EH_CONTENT = 205, | |
OC_EH_BAD_REQ = 400, | |
OC_EH_UNAUTHORIZED_REQ = 401, | |
OC_EH_BAD_OPT = 402, | |
OC_EH_FORBIDDEN = 403, | |
OC_EH_RESOURCE_NOT_FOUND = 404, | |
OC_EH_METHOD_NOT_ALLOWED = 405, | |
OC_EH_NOT_ACCEPTABLE = 406, | |
OC_EH_TOO_LARGE = 413, | |
OC_EH_UNSUPPORTED_MEDIA_TYPE = 415, | |
OC_EH_INTERNAL_SERVER_ERROR = 500, | |
OC_EH_BAD_GATEWAY = 502, | |
OC_EH_SERVICE_UNAVAILABLE = 503, | |
OC_EH_RETRANSMIT_TIMEOUT = 504 | |
} | |
/** | |
* Entity's state | |
*/ | |
internal enum OCEntityHandlerFlag | |
{ | |
/** Request state.*/ | |
OC_REQUEST_FLAG = (1 << 1), | |
/** Observe state.*/ | |
OC_OBSERVE_FLAG = (1 << 2) | |
} | |
/** | |
* Resource Properties. | |
* The value of a policy property is defined as bitmap. | |
* The LSB represents OC_DISCOVERABLE and Second LSB bit represents OC_OBSERVABLE and so on. | |
* Not including the policy property is equivalent to zero. | |
* | |
*/ | |
[Flags] | |
internal enum OCResourceProperty : byte | |
{ | |
/** When none of the bits are set, the resource is non-discoverable & | |
* non-observable by the client.*/ | |
OC_RES_PROP_NONE = (0), | |
/** When this bit is set, the resource is allowed to be discovered by clients.*/ | |
OC_DISCOVERABLE = (1 << 0), | |
/** When this bit is set, the resource is allowed to be observed by clients.*/ | |
OC_OBSERVABLE = (1 << 1), | |
/** When this bit is set, the resource is initialized, otherwise the resource | |
* is 'inactive'. 'inactive' signifies that the resource has been marked for | |
* deletion or is already deleted.*/ | |
OC_ACTIVE = (1 << 2), | |
/** When this bit is set, the resource has been marked as 'slow'. | |
* 'slow' signifies that responses from this resource can expect delays in | |
* processing its requests from clients.*/ | |
OC_SLOW = (1 << 3), | |
#if __WITH_DTLS__ || __WITH_TLS__ | |
/** When this bit is set, the resource is a secure resource.*/ | |
OC_SECURE = (1 << 4), | |
#else | |
OC_SECURE = (0), | |
#endif | |
/** When this bit is set, the resource is allowed to be discovered only | |
* if discovery request contains an explicit querystring. | |
* Ex: GET /oic/res?rt=oic.sec.acl */ | |
OC_EXPLICIT_DISCOVERABLE = (1 << 5) | |
#if WITH_MQ | |
/** When this bit is set, the resource is allowed to be published */ | |
, OC_MQ_PUBLISHER = (1 << 6) | |
#endif | |
#if MQ_BROKER | |
/** When this bit is set, the resource is allowed to be notified as MQ broker.*/ | |
, OC_MQ_BROKER = (1 << 7) | |
#endif | |
} | |
/** | |
* OCDoResource methods to dispatch the request | |
*/ | |
internal enum OCMethod | |
{ | |
OC_REST_NOMETHOD = 0, | |
/** Read.*/ | |
OC_REST_GET = (1 << 0), | |
/** Write.*/ | |
OC_REST_PUT = (1 << 1), | |
/** Update.*/ | |
OC_REST_POST = (1 << 2), | |
/** Delete.*/ | |
OC_REST_DELETE = (1 << 3), | |
/** Register observe request for most up date notifications ONLY.*/ | |
OC_REST_OBSERVE = (1 << 4), | |
/** Register observe request for all notifications, including stale notifications.*/ | |
OC_REST_OBSERVE_ALL = (1 << 5), | |
#if WITH_PRESENCE | |
/** Subscribe for all presence notifications of a particular resource.*/ | |
OC_REST_PRESENCE = (1 << 7), | |
#endif | |
/** Allows OCDoResource caller to do discovery.*/ | |
OC_REST_DISCOVER = (1 << 8) | |
} | |
/** | |
* These enums (OCTransportAdapter and OCTransportFlags) must | |
* be kept synchronized with OCConnectivityType (below) as well as | |
* CATransportAdapter and CATransportFlags (in CACommon.h). | |
*/ | |
internal enum OCTransportAdapter | |
{ | |
/** value zero indicates discovery.*/ | |
OC_DEFAULT_ADAPTER = 0, | |
/** IPv4 and IPv6, including 6LoWPAN.*/ | |
OC_ADAPTER_IP = (1 << 0), | |
/** GATT over Bluetooth LE.*/ | |
OC_ADAPTER_GATT_BTLE = (1 << 1), | |
/** RFCOMM over Bluetooth EDR.*/ | |
OC_ADAPTER_RFCOMM_BTEDR = (1 << 2), | |
#if RA_ADAPTER | |
/**Remote Access over XMPP.*/ | |
OC_ADAPTER_REMOTE_ACCESS = (1 << 3), | |
#endif | |
/** CoAP over TCP.*/ | |
OC_ADAPTER_TCP = (1 << 4), | |
/** NFC Transport for Messaging.*/ | |
OC_ADAPTER_NFC = (1 << 5) | |
} | |
/** | |
* Transport Protocol IDs. | |
*/ | |
internal enum OCTransportProtocolID | |
{ | |
/** For invalid ID.*/ | |
OC_INVALID_ID = (1 << 0), | |
/* For coap ID.*/ | |
OC_COAP_ID = (1 << 1) | |
} | |
#endregion | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment