Comparing IPAddress (stored as varbinary) - c#

I have an IPAddress column on my Activity table. This is stored as a varbinary(16) so that it can be efficient (moreso than storing as a string) and also support IPv6. When I store, I basically get the value of (new System.Net.IPAddress("127.0.0.1")).GetAddressBytes().
What I want to be able to do is search for all IP addresses that begin with certain bytes, e.g. "127.*". I can easily get the bytes for that, so just assume that I am able to get new byte[] { 127 }.
Given that, how can I actually write a LINQ to SQL query to get me the data I want?
Sadly, I don't have StartsWith, though I essentially want the equivalent of Activity.Where(a => a.IPAddress.StartsWith(new byte[] { 127 })).

A while ago, I had to find the location of a given IP. We got the IP from the request. There are free databases which gave us this mapping. In IPv4, when we say the IP as "a.b.c.d" it is essentially......
a * (256^3) + b * (256^2) + c * (256) + d
http://www.aboutmyip.com/AboutMyXApp/IP2Integer.jsp
so when you say you want an IP address starting with "a", you are looking for IPs between a * 256^ 3 and a * 256^3 + 256 * (256^2) (b = 256) + 256 *(256) (c=256) + 256( d=256) (lower / upper limit may vary a little bit depending on whether you want to include/exclude the limits).
That said, there are specific IPs reserved for specific purposes(like 127.0.0.1 which is localhost, 0.0.0.0 cannot be an IP etc).
So your linq query would be
from i in iList where i >= MIN && i <= MAX select i;
where iList is your initial list
MIN is your min value for your range
MAX is your max value for your range

If the data is returned as a byte array, why not reference the first byte of the array? Sounds like;
Activity.Where(a => a.IpAddress[0] == 127);
might be what your looking for?
You could store the IP address as a hex string, where 127.0.0.1 = "7F000001" then if you want to find an IP address starting with 192.168.* you can use
Activity.Where(a => a.IpAddress.StartsWith("C0A8"));

Related

Check condition for hexadecimal number

I have a hexadecimal number written in the text file. I need to check the condition for hexadecimal number in if-else. For example, the Start Number written in the text file is 1100 and End Number is 10FF. The start number,1100 is the addition of End number with 1. This increment process done by other system.
In my case, the system will proceed to the next process after read the numbers in the text file.
This is my code:
var data = File
.ReadAllLines(Path)
.Select(x => x.Split('='))
.Where(x => x.Length > 1)
.ToDictionary(x => x[0].Trim(), x => x[1]);
Console.WriteLine("Start: {0}", data["Start"]);
Console.WriteLine("End: {0}", data["End"]);
if (data["Start"] == data["End"]+1)
{
//it will proceed to next process
}
else
{
//prompt not meet end number
}
The problem is, the if (data["Start"] == data["End"]+1) does not functioning. How can I resolve this issue? Do I need to convert the hexadecimal number to int first?
In C# if you concatenate string with number, number will get converted to a string and appended to the end of original string:
If you want to perform some math on your numbers, you need to convert them to correct data type first (in your case - integer).
To do this, you can use one of these commands:
if (Convert.ToInt32(data["Start"], 16) == Convert.ToInt32(data["End"], 16) + 1)
or
if (int.Parse(data["Start"], NumberStyles.HexNumber) == int.Parse(data["End"], NumberStyles.HexNumber) + 1)
They will convert your string that contains hexadecimal number to decimal representation of this number, and then it will behave as a number (addition will work as expected, for example).
"10FF" is not 0x10FF.
In c#, The fact that a string happens to contain text that can be parsed as a hexa-decimal number (or any number for that matter) doesn't mean it will be implicitly converted to that number.
In fact, it's the other way around - c# will implicitly convert the number to a string when using the + operator between a string and a number - so "10FF" + 1 will result with "10FF1".
Note I've started this line with "In c#" - because other languages might not follow the same rules. In T-SQL, for instance, implicit conversions from varchar to int happens all the time and it's a very common "gotcha" for inexperienced devs.
So you need to convert your strings to ints, as Lasse V. Karlsen wrote in the comments.
You can either do that by using Convert.ToInt32(string, 16) or by using int.Parse(str, NumberStyles.HexNumber) - if you're sure that the text will always contain the string representation of a hexa-decimal number.
For text that you're not sure about, you better use int.TryParse to avoid the risk of a FormatException - but note that TryParse returns bool and the int value is returned via an out parameter: int.TryParse(str, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out var val)

Read a Siemens PLC s7 String in C# with S7netplus

I have trouble to read data in DB of a Siemens PLC S7 1500 using S7netplus.
The situation:
I have a C# application running.
I connect on the PLC very well.
I can read data such as Boolean, UInt, UShot, Bytes
But I don't know how to read String data (see the image below)
To read the other datas like Boolean I use this call:
plc.Read("DB105.DBX0.0")
I understood that this read in the Datablock 105 (DB105) with a datatype Boolean (DBX) at the offset 0.0
I would like to apply the same type of reading for the string. So I Tried "DB105.DBB10.0" in my example. But it return a value "40" in Byte type (and i should have something else)
I saw that there is another reading method
plc.ReadBytes(DataType DB, int DBNumber, int StartByteArray, int lengthToRead)
But I have difficulties to see how to apply it to my example (I know that I have to convert it to string after).
TO resume:
- Is there a simple way with a string like "DB105.DBX0.0" to read string data in a Siemens PLC?
- If not how to use the ReadBytes function in my example?
Thanks for your help
I managed to read my string value by the ReadBytes method.
In my example I needed to pass values like this:
plc.Read(DataType.DataBlock, 105, 12, VarType.String, 40);
Why 12? Because the 2 first octets of a byte string are for the length. So 10 to 12 return a value as 40 which is the length.
I have override the read method to accept the 'easy string' call like this:
public T Read<T>(object pValue)
{
var splitValue = pValue.ToString().Split('.');
//check if it is a string template (3 separation ., 2 if not)
if (splitValue.Count() > 3 && splitValue[1].Substring(2, 1) == "S")
{
DataType dType;
//If we have to read string in other dataType need development to make here.
if (splitValue[0].Substring(0, 2) == "DB")
dType = DataType.DataBlock;
else
throw new Exception("Data Type not supported for string value yet.");
int length = Convert.ToInt32(splitValue[3]);
int start = Convert.ToInt32(splitValue[1].Substring(3, splitValue[1].Length - 3));
int MemoryNumber = Convert.ToInt32(splitValue[0].Substring(2, splitValue[0].Length - 2));
// the 2 first bits are for the length of the string. So we have to pass it
int startString = start + 2;
var value = ReadFull(dType, MemoryNumber, startString, VarType.String, length);
return (T)value;
}
else
{
var value = plc.Read(pValue.ToString());
//Cast with good format.
return (T)value;
}
}
So now I can call my read function like this:
with basic existing call:
var element = mPlc.Read<bool>("DB10.DBX1.4").ToString(); => read in Datablock 10 a boolean value on the byte 1 and octet 4
var element = mPlc.Read<uint>("DB10.DBD4.0").ToString(); => read in datablock 10 a int value on the byte 4 and octet 0
with the overrided call for the string:
var element = mPlc.Read<string>("DB105.DBS10.0.40").ToString() => read in the datablock 105 a string value on the byte 10 and octet 0 with a length of 40
Hope this could help for anyone else :)
I did it slightly simpler; I ignore the first byte, and then read the second byte to give me the string length. I then use this to give me the length of the bytes for the string. For example the PLC gave me DB offset of 288 for the start of the string. This is using the S7Plus NuGet, with a DB address of 666.
Note, requesting strings seriously slows down the communication, so probably better to only request them when there is a new value.
TempStringLength(0) = PLC.Read(DataType.DataBlock, 666, 289, VarType.Byte, 1) 'Length of String.'
TempStringArray(0) = PLC.Read(DataType.DataBlock, 666, 290, VarType.String, TempStringLength(0))'Actual String.'

Way to generate a unique number that does not repeat in a reasonable time?

I'm integrating/testing with a remote web service and even though it's the "QA" endpoint, it still enforces a unique email address on every call.
I can think of DateTime.Now.Ticks (e.g. 634970372342724417) and Guid.NewGuid(), but neither of those can be coalesced into an email with max. 20 chars (or can they?).
I suppose it's not that hard to write out to a file a number that contains the last number used and then use email1#x.com, email2#x.com, etc... but if I can avoid persisting state I always do.
Does anyone have a trick or an algorithm that gives something of a short length "guid" that is unique to a reasonably long time period (say a year) that I could use for my email addresses of max length 20 chars with (max length of guid) = 14 = 20 - length of "#x.com"?
If you assume that you will not generate two e-mail addresses at the same 'tick', then you can indeed use the ticks to generate an e-mail address.
However, if ticks is a 64-bit number, and you write out that number, you will end up with more than 20 characters.
The trick is to encode your 64-bit number using a different scheme.
Assume that you can use the 26 characters from the western alphabet + 10 digits. This makes 36 possible characters. If you take 5 bits, you can represent 32 characters. That should be enough.
Take the 64-bits and divide them in groups of 5 bits (64 /5 is about 13 groups). Translate every 5 bits to one character. That way you end up with 13 characters, and you can still add a character in front of it).
long ticks = DateTime.Now.Ticks;
byte[] bytes = BitConverter.GetBytes(ticks);
string id = Convert.ToBase64String(bytes)
.Replace('+', '_')
.Replace('/', '-')
.TrimEnd('=');
Console.WriteLine (id);
Yields:
Gq1rNzbezwg
If you get the following digits from your date-time, you should be able to make it work...
Soemthing like:
DateTime.Now.ToString("yyMMddHHmmssff");
which is 16 characters, leaving 4 for some other prefix as you need.
So, Feb 21, 2013, at approximately 10:21 would be "130321102142" and the next one would be "130321102169", etc...
Have a look at http://msdn.microsoft.com/en-us/library/zdtaw1bw.aspx for more details on datetime formatting.
Since you specified at least 1 second between each call, this should work :
DateTime.Now.ToString("yyyyMMddHHmmss");
its exactly 14 characters.
Just to add... If you want to use number only from ticks, you can by using substring, for example:
int onlyThisAmount = 20;
string ticks = DateTime.Now.Ticks.ToString();
ticks = ticks.Substring(ticks.Length - onlyThisAmount);
/// <summary>
/// Get a unique reference number.
/// </summary>
/// <returns></returns>
public string GetUniqueReferenceNumber(char firstChar)
{
var ticks = DateTime.Now.Ticks;
var ticksString = ticks.ToString();
var ticksSubString = ticksString.Substring((ticksString.Length - 15 > 0) ? ticksString.Length - 15 : 0);
if (this.currentTicks.Equals(ticks))
{
this.currentReference++;
if (this.currentReference >= 9999)
{
// Only when there are very fast computers.
System.Threading.Thread.Sleep(1);
}
return (firstChar + ticksSubString + this.currentReference.ToString("D4")).PadRight(20, '9');
}
this.currentReference = -1;
this.currentTicks = ticks;
return (firstChar + ticksSubString).PadRight(20, '9');
}
In my case I needed to create a unique reference number with a unique first character and a maximum of 20 characters. Maybe you can use the function below, it allows you to create 9999 unique numbers within one tick. (zero included)
Of course you can create your own implementation without the first character and maximum character count of 20
public async Task<string> GeneratePatientNumberAsync()
{
var random = new Random();
var chars = DateTime.Now.Ticks + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz123456789" + DateTime.Now.Ticks;
return new string(Enumerable.Repeat(chars, 5)
.Select(s => s[random.Next(s.Length)]).ToArray());
}

Compressing big number (or string) to small value

My ASP.NET page has following query string parameter:
…?IDs=1000000012,1000000021,1000000013,1000000022&...
Here IDs parameter will always have numbers separated by something, in this case ,. Currently there are 4 numbers but normally they would be in between 3 and 7.
Now, I am looking for method to convert each big number from above into smallest possible value; specifically compressing value of IDs query string parameter. Both, compressing each number algorithm or compressing whole value of IDs query string parameter are welcome.
Encode or decode is not an issue; just compressing the value IDs query string parameter.
Creating some unique small value for IDs and then retrieving its value from some data source is out of scope.
Is there an algorithm to compress such big numbers to small values or to compress value of the IDs query string parameter all together?
You basically need so much room for your numbers because you are using base 10 to represent them. An improvement would be to use base 16 (hex). So for example, you could represent 255 (3 digits) as ff (2 digits).
You can take that concept further by using a much larger number base... the set of all characters that are valid query string parameters:
A-Z, a-z, 0-9, '.', '-', '~', '_', '+'
That gives you a base of 67 characters to work with (see Wikipedia on QueryString).
Have a look at this SO post for approaches to converting base 10 to arbitrary number bases.
EDIT:
In the linked SO post, look at this part:
string xx = IntToString(42,
new char[] { '0','1','2','3','4','5','6','7','8','9',
'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z',
'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x'});
That's almost what you need. Just expand it by adding the few characters it is missing:
yz.-~_+
That post is missing a method to go back to base 10. I'm not going to write it :-) but the procedure is like this:
Define a counter I'll call TOTAL.
Look at the right most character and find it's position in the array.
TOTAL = (the position of the character in the array)
Example: Input is BA1. TOTAL is now 1 (since "1" is in position 1 in the array)
Now look at the next character left of the first one and find it's position in the array.
TOTAL += 47 * (the position of the character in the array)
Example: Input is BA1. TOTAL is now (47 * 11) + 1 = 518
Now look at the next character left of the previous one and find it's position in the array.
TOTAL += 47 * 47 * (the position of the character in the array)
Example: Input is BA1. Total is now (47 * 47 * 10) + (47 * 11) + 1 = 243508
And so on.
I suggest you write a unit test that converts a bunch of base 10 numbers into base 47 and then back again to make sure your conversion code works properly.
Note how you represented a 6 digit base 10 number in just 3 digits of base 47 :-)
What is the range of your numbers? Assuming they can fit in a 16-bit integer, I would:
Store all your numbers as 16-bit integers (2 bytes per number, range -32,768 to 32,767)
Build a bytestream of 16-bit integers (XDR might be a good option here; at very least, make sure to handle endianness correctly)
Base64 encode the bytestream, using the modified base64 encoding for URLs (net is about 3 characters per number)
As an added bonus you don't need comma characters anymore because you know each number is 2 bytes.
Alternatively, if that isn't good enough, I'd use zlib to compress your stream of integers and then base64 the zlib-compressed stream. You can also switch to 32-bit integers if 16-bit isn't a large enough range (i.e. if you really need numbers in the 1,000,000,000 range).
Edit:
Maybe too late, but here's an implementation that might do what you need:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Scratch {
class Program {
static void Main(string[] args) {
//var ids = new[] { 1000000012, 1000000021, 1000000013, 1000000022 };
var rand = new Random();
var ids = new int[rand.Next(20)];
for(var i = 0; i < ids.Length; i++) {
ids[i] = rand.Next();
}
WriteIds(ids);
var s = IdsToString(ids);
Console.WriteLine("\nResult string is: {0}", s);
var newIds = StringToIds(s);
WriteIds(newIds);
Console.ReadLine();
}
public static void WriteIds(ICollection<Int32> ids) {
Console.Write("\nIDs: ");
bool comma = false;
foreach(var id in ids) {
if(comma) {
Console.Write(",");
} else {
comma = true;
}
Console.Write(id);
}
Console.WriteLine();
}
public static string IdsToString(ICollection<Int32> ids) {
var allbytes = new List<byte>();
foreach(var id in ids) {
var bytes = BitConverter.GetBytes(id);
allbytes.AddRange(bytes);
}
var str = Convert.ToBase64String(allbytes.ToArray(), Base64FormattingOptions.None);
return str.Replace('+', '-').Replace('/', '_').Replace('=', '.');
}
public static ICollection<Int32> StringToIds(string idstring) {
var result = new List<Int32>();
var str = idstring.Replace('-', '+').Replace('_', '/').Replace('.', '=');
var bytes = Convert.FromBase64String(str);
for(var i = 0; i < bytes.Length; i += 4) {
var id = BitConverter.ToInt32(bytes, i);
result.Add(id);
}
return result;
}
}
}
Here's another really simple scheme that should give good compression for a set of numbers of the form N + delta where N is a large constant.
public int[] compress(int[] input) {
int[] res = input.clone();
Arrays.sort(res);
for (int i = 1; i < res.length; i++) {
res[i] = res[i] - res[i - 1];
}
return res;
}
This should reduce the set {1000000012,1000000021,1000000013,1000000022} to the list [1000000012,1,9,1], which you can then compress further by representing the numbers in base47 encoding as described in another answer.
Using simple decimal encoding, this goes from 44 characters to 16 characters; i.e. 63%. (And using base47 will give even more compression).
If it is unacceptable to sort the ids, you don't get quite as good compression. For this example, {1000000012,1000000021,1000000013,1000000022} compresses to the list [1000000012,9,-8,9]. That is just one character longer for this example
Either way, this is better than a generic compression algorithm or encoding schemes ... FOR THIS KIND OF INPUT.
If the only issue is the URL length, you can convert numbers to base64 characters, then convert them back to numbers at the server side
how patterned are the IDs you are getting? if digit by digit, the IDs are random, then the method I am about to propose won't be very efficient. but if the IDs you gave as an example are representative of the types you'd be getting, then perhaps the following could work?
i motivate this idea by example.
you have for example, 1000000012 as ID that you'd like to compress. why not store it as [{1},{0,7},{12}]? This would mean that the first digit is a 1 followed by 7 zeros followed by a 12. Thus if we use the notation {x} that would represent one instance of x, while if we use {x,y} that would mean that x occurs y times in a row.
you could extend this with a little bit of pattern matching and/or function fitting.
for example, pattern matching: 1000100032 would be [{1000,2}{32}].
for example, function fitting:
if your IDs are 10 digits, then split the ID into two 5 digit numbers and store the equation of the line that goes through both points. if ID = 1000000012, the you have y1 = 10000 and y2 = 12. therefore, your slope is -9988 and your intercept is 10000 (assuming x1 = 0, x2 = 1). In this case, it's not an improvement, but if the numbers were more random, it could be. Equivalently, you could store the sequence of IDs with piecewise linear functions.
in any case, this mostly depends on the structure of your IDs.
I assume you are doing this as a workaround for request URL length restrictions ...
Other answers have suggested encoding the decimal id numbers in hex, base47 or base64, but you can (in theory) do a lot better than that by using LZW (or similar) to compress the id list. Depending on how much redundancy there is in your ID lists, you could get significantly more than 40% reduction, even after re-encoding the compressed bytes as text.
In a nut-shell, I suggest that you find an off-the-shelf text compression library implemented in Javascript and use it client side to compress the ID list. Then encode the compressed bytestring using base47/base64, and pass the encoded string as the URL parameter. On the server side do the reverse; i.e. decode followed by decompress.
EDIT: As an experiment, I created a list of 36 different identifiers like the ones you supplied and compressed it using gzip. The original file is 396 bytes, the compressed file is 101 bytes, and the compressed + base64 file 138 bytes. That is a 65% reduction overall. And the compression ratio could actually improve for larger files. However, when I tried this with a small input set (e.g. just the 4 original identifiers), I got no compression, and after encoding the size was larger than the original.
Google "lzw library javascript"
In theory, there might be simpler solution. Send the parameters as "post data" rather than in the request URL, and get the browser to apply the compression using one of the encodings that it understands. That will give you more savings too since there is no need to encode the compressed data into legal URL characters.
The problem is getting the browser to compress the request ... and doing that in a browser independent way.

How do display the Integer value of an IPAddress [closed]

Closed. This question is off-topic. It is not currently accepting answers.
Want to improve this question? Update the question so it's on-topic for Stack Overflow.
Closed 14 years ago.
Improve this question
I was looking at the System.Net Namespace and it has an IPAddress instance you can use. This has a Parse method which you can use to parse a string into an IPInstance then use the Address property to give you the long value.
However...
The number returned is NOT the true conversion.
e.g. For IP 58.0.0.0 , the System.Net namespace gives me a value of 58...
When in fact, the integer value should be 973078528
Can someone please show me the correct code do convert this?
The formula should be.. (for ip 192.1.20.10).
192 * (256*256*256) + 1 * (256*256) + 20 * (256) + 10
The reason this formula is correct is that the number it returns you can use in a >= and <= query to determine an IP address that falls within a range.
The Address Property (of the IPAddress instance) does not calculate/return this. A bonus point for anyone that knows why the address property does not return what I think is the correct answer...
Other examples from other links did not work either.
Please see How to convert an IPv4 address into a integer in C#?
What you are seeing appears to be an endian problem.
As a generic function, if your IP address is A.B.C.D then the value you're after is:
A << 24 + (= A * 16777216)
B << 16 + (= B * 65536)
C << 8 + (= C * 256)
D
On little endian machines when the four-byte array ABCD is cast into an integer it comes out with A as the least significant byte instead of the most significant.
I don't write vb.net code, but it should be pretty trivial to knock out a function that'll do this.
You'll need to ensure that A - D are all in the 0 .. 255 range first, though!
How to convert an IPv4 address into a integer in C#?
example code is listed in the picked answer
e; f, b
This might work, will try it and see.
public double IPAddressToNumber(string IPaddress)
{
int i;
string [] arrDec;
double num = 0;
if (IPaddress == "")
{
return 0;
}
else
{
arrDec = IPaddress.Split('.');
for(i = arrDec.Length - 1; i >= 0 ; i = i -1)
{
num += ((int.Parse(arrDec[i])%256) * Math.Pow(256 ,(3 - i )));
}
return num;
}
}

Categories